From 37d45f5b74cc10494def74e6e4be644e88014f4f Mon Sep 17 00:00:00 2001 From: ghe0 Date: Fri, 14 Mar 2025 16:16:17 +0200 Subject: [PATCH] added app code from noor --- Cargo.lock | 265 ++++++++++-------- Cargo.toml | 11 +- README.md | 3 + src/data.rs | 773 ++++++++++++++++++++++++---------------------------- src/grpc.rs | 479 +++++++++++++++++++++----------- src/main.rs | 13 +- vm.proto | 77 +----- 7 files changed, 871 insertions(+), 750 deletions(-) create mode 100644 README.md diff --git a/Cargo.lock b/Cargo.lock index 14d3b2f..68bb50f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,11 +82,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] @@ -120,9 +121,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", @@ -217,9 +218,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "block-buffer" @@ -237,6 +238,7 @@ dependencies = [ "bs58", "chrono", "dashmap", + "detee-shared", "ed25519-dalek", "env_logger", "log", @@ -244,7 +246,7 @@ dependencies = [ "prost-types", "reqwest", "serde", - "serde_yaml", + "serde_json", "thiserror", "tokio", "tokio-stream", @@ -264,9 +266,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -276,15 +278,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "cc" -version = "1.2.5" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" dependencies = [ "shlex", ] @@ -305,7 +307,6 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", - "serde", "wasm-bindgen", "windows-targets", ] @@ -402,7 +403,6 @@ dependencies = [ "lock_api", "once_cell", "parking_lot_core", - "serde", ] [[package]] @@ -415,6 +415,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "detee-shared" +version = "0.1.0" +source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared?branch=stable_01#b8f37dec1845d29ea0b69035712e6ebb214376f4" +dependencies = [ + "base64", + "prost", + "serde", + "serde_yaml", + "thiserror", + "tonic", + "tonic-build", +] + [[package]] name = "digest" version = "0.10.7" @@ -619,7 +633,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", ] [[package]] @@ -640,7 +666,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.7.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -707,9 +733,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpdate" @@ -725,9 +751,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -983,9 +1009,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -993,9 +1019,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is_terminal_polyfill" @@ -1020,9 +1046,9 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -1030,15 +1056,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -1058,9 +1084,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "matchit" @@ -1082,9 +1108,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] @@ -1096,7 +1122,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1108,9 +1134,9 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" dependencies = [ "libc", "log", @@ -1134,24 +1160,24 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ "bitflags", "cfg-if", @@ -1175,15 +1201,15 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" dependencies = [ "cc", "libc", @@ -1217,23 +1243,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.7.0", + "indexmap 2.7.1", ] [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" dependencies = [ "proc-macro2", "quote", @@ -1242,9 +1268,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1279,9 +1305,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", "syn", @@ -1289,9 +1315,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -1350,9 +1376,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -1384,7 +1410,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -1427,9 +1453,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.10" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3536321cfc54baa8cf3e273d5e1f63f889067829c4b410fcdbac8ca7b80994" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64", "bytes", @@ -1477,7 +1503,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "spin", "untrusted", @@ -1501,9 +1527,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags", "errno", @@ -1514,9 +1540,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7" dependencies = [ "once_cell", "rustls-pki-types", @@ -1536,9 +1562,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" @@ -1553,15 +1579,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "schannel" @@ -1593,9 +1619,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -1629,9 +1655,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -1657,7 +1683,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.7.0", + "indexmap 2.7.1", "itoa", "ryu", "serde", @@ -1745,9 +1771,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.90" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -1797,12 +1823,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ "cfg-if", "fastrand", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -1855,9 +1882,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -1871,9 +1898,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", @@ -2060,9 +2087,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unsafe-libyaml" @@ -2107,11 +2134,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" dependencies = [ - "getrandom", + "getrandom 0.3.1", ] [[package]] @@ -2142,21 +2169,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasm-bindgen" -version = "0.2.99" +name = "wasi" +version = "0.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", @@ -2168,9 +2205,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -2181,9 +2218,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2191,9 +2228,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -2204,15 +2241,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -2339,6 +2379,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "write16" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 3ae3262..810cbfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,21 +5,24 @@ edition = "2021" [dependencies] bs58 = "0.5.1" -chrono = { version = "0.4.39", features = ["serde"] } -dashmap = { version = "6.1.0", features = ["serde"] } +chrono = "0.4.39" +dashmap = "6.1.0" ed25519-dalek = "2.1.1" env_logger = "0.11.6" log = "0.4.22" prost = "0.13.4" prost-types = "0.13.4" reqwest = "0.12.10" -serde = { version = "1.0.217", features = ["derive"] } -serde_yaml = "0.9.34" +serde = { version = "1.0.216", features = ["derive"] } +serde_json = "1.0.134" thiserror = "2.0.11" tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread"] } tokio-stream = "0.1.17" tonic = "0.12" uuid = { version = "1.11.0", features = ["v4"] } +detee-shared = { git = "ssh://git@gitea.detee.cloud/noormohammedb/detee-shared", branch = "stable_01" } +# detee-shared = { path = "../detee-shared" } + [build-dependencies] tonic-build = "0.12" diff --git a/README.md b/README.md new file mode 100644 index 0000000..a818250 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Brain mock + +eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_ed25519 \ No newline at end of file diff --git a/src/data.rs b/src/data.rs index 17add83..302837a 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,57 +1,39 @@ use crate::grpc::snp_proto::{self as grpc}; use chrono::Utc; use dashmap::DashMap; +use detee_shared::sgx::pb::brain::DelAppReq; use log::{debug, info, warn}; -use serde::{Deserialize, Serialize}; use std::str::FromStr; use std::sync::RwLock; -use std::{ - collections::{HashMap, HashSet}, - fs::File, - io::Write, -}; use tokio::sync::mpsc::Sender; use tokio::sync::oneshot::Sender as OneshotSender; -const DATA_PATH: &str = "/etc/detee/brain-mock/saved_data.yaml"; +use detee_shared::sgx::pb::brain::AppContract as AppContractPB; +use detee_shared::sgx::pb::brain::AppNodeResources; +use detee_shared::sgx::pb::brain::AppResource as AppResourcePB; +use detee_shared::sgx::pb::brain::BrainMessageApp; +use detee_shared::sgx::pb::brain::MappedPort; +use detee_shared::sgx::pb::brain::NewAppReq; +use detee_shared::sgx::pb::brain::NewAppRes; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("We do not allow locking of more than 100000 LP.")] TxTooBig, - #[error("Escrow must be at least 5000 LP.")] - MinimalEscrow, #[error("Account has insufficient funds for this operation")] InsufficientFunds, #[error("Could not find contract {0}")] VmContractNotFound(String), - #[error("This error should never happen.")] - ImpossibleError, - #[error("You don't have the required permissions for this operation.")] - AccessDenied, } -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct AccountData { +#[derive(Clone)] +pub struct AccountNanoLP { pub balance: u64, pub tmp_locked: u64, - // holds reasons why VMs of this account got kicked - pub kicked_for: Vec, - pub last_kick: chrono::DateTime, - // holds accounts that banned this account - pub banned_by: HashSet, } -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct OperatorData { - pub escrow: u64, - pub email: String, - pub banned_users: HashSet, - pub vm_nodes: HashSet, -} - -impl From for grpc::AccountBalance { - fn from(value: AccountData) -> Self { +impl From for grpc::AccountBalance { + fn from(value: AccountNanoLP) -> Self { grpc::AccountBalance { balance: value.balance, tmp_locked: value.tmp_locked, @@ -59,10 +41,10 @@ impl From for grpc::AccountBalance { } } -#[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Eq, Hash, PartialEq, Clone, Debug, Default)] pub struct VmNode { pub public_key: String, - pub operator_wallet: String, + pub owner_key: String, pub country: String, pub region: String, pub city: String, @@ -76,27 +58,24 @@ pub struct VmNode { pub max_ports_per_vm: u32, // nanoLP per unit per minute pub price: u64, - // 1st String is user wallet and 2nd String is report message - pub reports: HashMap, - pub offline_minutes: u64, } impl Into for VmNode { fn into(self) -> grpc::VmNodeListResp { grpc::VmNodeListResp { - operator: self.operator_wallet, node_pubkey: self.public_key, country: self.country, region: self.region, city: self.city, ip: self.ip, + server_rating: 0, + provider_rating: 0, price: self.price, - reports: self.reports.into_values().collect(), } } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug)] pub struct VmContract { pub uuid: String, pub hostname: String, @@ -112,15 +91,14 @@ pub struct VmContract { pub dtrfs_sha: String, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, + // price per unit per minute // recommended value is 20000 - /// price per unit per minute pub price_per_unit: u64, pub locked_nano: u64, pub collected_at: chrono::DateTime, } impl VmContract { - /// total hardware units of this VM fn total_units(&self) -> u64 { // TODO: Optimize this based on price of hardware. // I tried, but this can be done better. @@ -131,7 +109,7 @@ impl VmContract { + (!self.public_ipv4.is_empty() as u64 * 10) } - /// Returns price per minute in nanoLP + // Returns price per minute in nanoLP fn price_per_minute(&self) -> u64 { self.total_units() * self.price_per_unit } @@ -162,178 +140,162 @@ impl Into for VmContract { } } -#[derive(Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default)] +pub struct AppContract { + pub uuid: String, + pub package_url: String, + pub admin_pubkey: String, + pub node_pubkey: String, + pub mapped_ports: Vec<(u16, u16)>, + pub host_ipv4: String, + pub disk_size_mb: u32, + pub vcpus: u32, + pub memory_mb: u32, + pub created_at: chrono::DateTime, + pub updated_at: chrono::DateTime, + // price per unit per minute + // recommended value is 20000 + pub price_per_unit: u64, + pub locked_nano: u64, + pub collected_at: chrono::DateTime, + pub hratls_pubkey: String, + pub public_package_mr_enclave: Option>, +} + +impl From for AppContractPB { + fn from(value: AppContract) -> Self { + let mapped_ports = value + .mapped_ports + .clone() + .into_iter() + .map(MappedPort::from) + .collect(); + + let resource = Some(AppResourcePB { + memory_mb: value.memory_mb, + disk_mb: value.disk_size_mb, + vcpu: value.vcpus, + ports: value.mapped_ports.iter().map(|p| p.1 as u32).collect(), + }); + Self { + uuid: value.uuid, + package_url: value.package_url, + admin_pubkey: value.admin_pubkey, + node_pubkey: value.node_pubkey, + mapped_ports, + public_ipv4: value.host_ipv4, + resource, + created_at: value.created_at.to_rfc3339(), + updated_at: value.updated_at.to_rfc3339(), + // TODO: check while implementing pricing + nano_per_minute: value.price_per_unit, + locked_nano: value.locked_nano, + collected_at: value.collected_at.to_rfc3339(), + hratls_pubkey: value.hratls_pubkey, + public_package_mr_enclave: value.public_package_mr_enclave, + } + } +} + +#[derive(Eq, Hash, PartialEq, Clone, Debug, Default)] +pub struct AppNode { + pub node_pubkey: String, + pub operator_pubkey: String, + pub country: String, + pub region: String, + pub city: String, + pub ip: String, + pub avail_mem_mb: u32, + pub avail_vcpus: u32, + pub avail_storage_mb: u32, + pub avail_no_of_port: u32, + pub max_ports_per_app: u32, + // nanotokens per unit per minute + pub price: u64, +} + +#[derive(Default)] pub struct BrainData { // amount of nanoLP in each account - accounts: DashMap, - operators: DashMap, + accounts: DashMap, vm_nodes: RwLock>, vm_contracts: RwLock>, - #[serde(skip_serializing, skip_deserializing)] tmp_newvm_reqs: DashMap)>, - #[serde(skip_serializing, skip_deserializing)] tmp_updatevm_reqs: DashMap)>, - #[serde(skip_serializing, skip_deserializing)] daemon_tx: DashMap>, + + app_nodes: RwLock>, + app_daemon_tx: DashMap>, + tmp_new_container_reqs: DashMap)>, + app_contracts: RwLock>, } impl BrainData { - pub fn save_to_disk(&self) -> Result<(), Box> { - let mut file = File::create(DATA_PATH)?; - file.write_all(serde_yaml::to_string(self)?.as_bytes())?; - Ok(()) - } - - fn load_from_disk() -> Result> { - let content = std::fs::read_to_string(DATA_PATH)?; - let data: Self = serde_yaml::from_str(&content)?; - Ok(data) - } - pub fn new() -> Self { - match Self::load_from_disk() { - Ok(data) => data, - Err(e) => { - warn!("Could not data {DATA_PATH} due to error: {e:?}"); - info!("Creating new instance of brain."); - Self { - accounts: DashMap::new(), - operators: DashMap::new(), - vm_nodes: RwLock::new(Vec::new()), - vm_contracts: RwLock::new(Vec::new()), - tmp_newvm_reqs: DashMap::new(), - tmp_updatevm_reqs: DashMap::new(), - daemon_tx: DashMap::new(), - } - } + Self { + accounts: DashMap::new(), + vm_nodes: RwLock::new(Vec::new()), + vm_contracts: RwLock::new(Vec::new()), + tmp_newvm_reqs: DashMap::new(), + tmp_updatevm_reqs: DashMap::new(), + daemon_tx: DashMap::new(), + + app_daemon_tx: DashMap::new(), + tmp_new_container_reqs: DashMap::new(), + app_contracts: RwLock::new(Vec::new()), + app_nodes: RwLock::new(Vec::new()), } } - pub fn get_balance(&self, account: &str) -> AccountData { + pub fn get_balance(&self, account: &str) -> AccountNanoLP { if let Some(account) = self.accounts.get(account) { return account.value().clone(); } else { - let balance = AccountData { + let balance = AccountNanoLP { balance: 0, tmp_locked: 0, - kicked_for: Vec::new(), - banned_by: HashSet::new(), - last_kick: chrono::Utc::now(), }; return balance; } } pub fn give_airdrop(&self, account: &str, tokens: u64) { - warn!("Airdropping {tokens} to {account}."); self.add_nano_to_wallet(account, tokens.saturating_mul(1_000_000_000)); } - pub fn slash_account(&self, account: &str, tokens: u64) { - warn!("Slashing {tokens} from {account}."); - self.rm_nano_from_wallet(account, tokens.saturating_mul(1_000_000_000)); - } - fn add_nano_to_wallet(&self, account: &str, nano_lp: u64) { log::debug!("Adding {nano_lp} nanoLP to {account}"); self.accounts .entry(account.to_string()) .and_modify(|d| d.balance += nano_lp) - .or_insert(AccountData { + .or_insert(AccountNanoLP { balance: nano_lp, - ..Default::default() + tmp_locked: 0, }); } - fn rm_nano_from_wallet(&self, account: &str, nano_lp: u64) { - log::debug!("Slashing {nano_lp} nanoLP to {account}"); - self.accounts.entry(account.to_string()).and_modify(|d| { - d.balance = d.balance.saturating_sub(nano_lp); - }); - } - - /// This is written to run every minute - pub async fn vm_nodes_cron(&self) { - log::debug!("Running vm nodes cron..."); - let mut nodes = self.vm_nodes.write().unwrap(); - let mut vm_contracts = self.vm_contracts.write().unwrap(); - for node in nodes.iter_mut() { - if self.daemon_tx.contains_key(&node.public_key) { - node.offline_minutes = 0; - continue; - } - let mut operator = match self - .operators - .iter_mut() - .find(|o| o.vm_nodes.contains(&node.public_key)) - { - Some(op) => op, - None => continue, - }; - node.offline_minutes += 1; - // compensate contract admin if the node is offline more then 5 minutes - if node.offline_minutes > 5 { - for c in vm_contracts - .iter() - .filter(|c| c.node_pubkey == node.public_key) - { - let compensation = c.price_per_minute() * 10; - if compensation < operator.escrow { - operator.escrow -= compensation; - self.add_nano_to_wallet(&c.admin_pubkey, compensation); - } - } - } - } - // delete nodes that are offline more than 3 hours, and clean contracts - nodes.retain(|n| { - if n.offline_minutes > 1600 { - vm_contracts.retain_mut(|c| { - if c.node_pubkey == n.public_key { - self.add_nano_to_wallet(&c.admin_pubkey, c.locked_nano); - } - c.node_pubkey != n.public_key - }); - for mut op in self.operators.iter_mut() { - op.vm_nodes.remove(&n.public_key); - } - } - n.offline_minutes <= 180 - }); - } - pub async fn vm_contracts_cron(&self) { let mut deleted_contracts: Vec<(String, String)> = Vec::new(); log::debug!("Running contracts cron..."); { let mut contracts = self.vm_contracts.write().unwrap(); contracts.retain_mut(|c| { - let node = self.find_node_by_pubkey(&c.node_pubkey).unwrap(); - if node.offline_minutes == 0 { - let operator_wallet = node.operator_wallet.clone(); - let minutes_to_collect = (Utc::now() - c.collected_at).num_minutes() as u64; - c.collected_at = Utc::now(); - let mut nanolp_to_collect = - c.price_per_minute().saturating_mul(minutes_to_collect); - if nanolp_to_collect > c.locked_nano { - nanolp_to_collect = c.locked_nano; - } - log::debug!("Removing {nanolp_to_collect} nanoLP from {}", c.uuid); - c.locked_nano -= nanolp_to_collect; - let escrow_multiplier = match self.operators.get(&operator_wallet) { - Some(op) if op.escrow > 5000 => match self.operators.get(&c.admin_pubkey) { - Some(user_is_op) if user_is_op.escrow > 5000 => 1, - _ => 5, - }, - _ => 1, - }; - self.add_nano_to_wallet( - &operator_wallet, - nanolp_to_collect * escrow_multiplier, - ); - if c.locked_nano == 0 { - deleted_contracts.push((c.uuid.clone(), c.node_pubkey.clone())); - } + let owner_key = self + .find_nodes_by_pubkey(&c.node_pubkey) + .unwrap() + .owner_key + .clone(); + let minutes_to_collect = (Utc::now() - c.collected_at).num_minutes() as u64; + c.collected_at = Utc::now(); + let mut nanolp_to_collect = c.price_per_minute().saturating_mul(minutes_to_collect); + if nanolp_to_collect > c.locked_nano { + nanolp_to_collect = c.locked_nano; + } + log::debug!("Removing {nanolp_to_collect} nanoLP from {}", c.uuid); + c.locked_nano -= nanolp_to_collect; + self.add_nano_to_wallet(&owner_key, nanolp_to_collect); + if c.locked_nano == 0 { + deleted_contracts.push((c.uuid.clone(), c.node_pubkey.clone())); } c.locked_nano > 0 }); @@ -353,9 +315,8 @@ impl BrainData { } } - pub fn register_node(&self, node: VmNode) { + pub fn insert_node(&self, node: VmNode) { info!("Registering node {node:?}"); - self.add_vmnode_to_operator(&node.operator_wallet, &node.public_key); let mut nodes = self.vm_nodes.write().unwrap(); for n in nodes.iter_mut() { if n.public_key == node.public_key { @@ -368,101 +329,6 @@ impl BrainData { nodes.push(node); } - // todo: this should also support Apps - /// Receives: operator, contract uuid, reason of kick - pub async fn kick_contract( - &self, - operator: &str, - uuid: &str, - reason: &str, - ) -> Result { - log::debug!("Operator {operator} requested a kick of {uuid} for reason: {reason}"); - let contract = self.find_contract_by_uuid(uuid)?; - let mut operator_data = self - .operators - .get_mut(operator) - .ok_or(Error::AccessDenied)?; - if !operator_data.vm_nodes.contains(&contract.node_pubkey) { - return Err(Error::AccessDenied); - } - - let mut minutes_to_refund = chrono::Utc::now() - .signed_duration_since(contract.updated_at) - .num_minutes() - .abs() as u64; - // cap refund at 1 week - if minutes_to_refund > 10080 { - minutes_to_refund = 10080; - } - - let mut refund_amount = minutes_to_refund * contract.price_per_minute(); - let mut admin_account = self - .accounts - .get_mut(&contract.admin_pubkey) - .ok_or(Error::ImpossibleError)?; - - // check if he got kicked within the last day - if !chrono::Utc::now() - .signed_duration_since(admin_account.last_kick) - .gt(&chrono::Duration::days(1)) - { - refund_amount = 0; - } - - if operator_data.escrow < refund_amount { - refund_amount = operator_data.escrow; - } - - log::debug!( - "Removing {refund_amount} escrow from {} and giving it to {}", - operator_data.key(), - admin_account.key() - ); - admin_account.balance += refund_amount; - admin_account.kicked_for.push(reason.to_string()); - operator_data.escrow -= refund_amount; - - let admin_pubkey = contract.admin_pubkey.clone(); - drop(admin_account); - drop(contract); - - self.delete_vm(grpc::DeleteVmReq { - uuid: uuid.to_string(), - admin_pubkey, - }) - .await?; - - Ok(refund_amount) - } - - pub fn ban_user(&self, operator: &str, user: &str) { - self.accounts - .entry(user.to_string()) - .and_modify(|a| { - a.banned_by.insert(operator.to_string()); - }) - .or_insert(AccountData { - banned_by: HashSet::from([operator.to_string()]), - ..Default::default() - }); - self.operators - .entry(operator.to_string()) - .and_modify(|o| { - o.banned_users.insert(user.to_string()); - }) - .or_insert(OperatorData { - banned_users: HashSet::from([user.to_string()]), - ..Default::default() - }); - } - - pub fn report_node(&self, admin_pubkey: String, node: &str, report: String) { - let mut nodes = self.vm_nodes.write().unwrap(); - if let Some(node) = nodes.iter_mut().find(|n| n.public_key == node) { - node.reports.insert(admin_pubkey, report); - } - } - pub fn lock_nanotockens(&self, account: &str, nano_lp: u64) -> Result<(), Error> { if nano_lp > 100_000_000_000_000 { return Err(Error::TxTooBig); @@ -548,11 +414,17 @@ impl BrainData { } pub async fn delete_vm(&self, delete_vm: grpc::DeleteVmReq) -> Result<(), Error> { - log::debug!("Starting deletion of VM {}", delete_vm.uuid); - let contract = self.find_contract_by_uuid(&delete_vm.uuid)?; - if contract.admin_pubkey != delete_vm.admin_pubkey { - return Err(Error::AccessDenied); - } + let contract = match self.find_contract_by_uuid(&delete_vm.uuid) { + Some(contract) => { + if contract.admin_pubkey != delete_vm.admin_pubkey { + return Err(Error::VmContractNotFound(delete_vm.uuid)); + } + contract + } + None => { + return Err(Error::VmContractNotFound(delete_vm.uuid)); + } + }; info!("Found vm {}. Deleting...", delete_vm.uuid); if let Some(daemon_tx) = self.daemon_tx.get(&contract.node_pubkey) { debug!( @@ -661,11 +533,6 @@ impl BrainData { return; } }; - if let Err(e) = update_vm_req.1.send(update_vm_resp.clone()) { - log::warn!( - "CLI RX dropped before receiving UpdateVMResp {update_vm_resp:?}. Error: {e:?}" - ); - } if update_vm_resp.error != "" { return; } @@ -702,6 +569,11 @@ impl BrainData { update_vm_resp.error = "VM Contract not found.".to_string(); } } + if let Err(e) = update_vm_req.1.send(update_vm_resp.clone()) { + log::warn!( + "CLI RX dropped before receiving UpdateVMResp {update_vm_resp:?}. Error: {e:?}" + ); + } } pub async fn submit_newvm_req( @@ -761,7 +633,7 @@ impl BrainData { let uuid = req.uuid.clone(); info!("Inserting new vm update request in memory: {req:?}"); let node_pubkey = match self.find_contract_by_uuid(&req.uuid) { - Ok(contract) => { + Some(contract) => { if contract.admin_pubkey != req.admin_pubkey { let _ = tx.send(grpc::UpdateVmResp { uuid, @@ -772,7 +644,7 @@ impl BrainData { } contract.node_pubkey } - Err(_) => { + None => { log::warn!( "Received UpdateVMReq for a contract that does not exist: {}", req.uuid @@ -823,81 +695,12 @@ impl BrainData { } } - pub fn find_node_by_pubkey(&self, public_key: &str) -> Option { + pub fn find_nodes_by_pubkey(&self, public_key: &str) -> Option { let nodes = self.vm_nodes.read().unwrap(); nodes.iter().cloned().find(|n| n.public_key == public_key) } - pub fn is_user_banned_by_node(&self, user_wallet: &str, node_pubkey: &str) -> bool { - if let Some(node) = self.find_node_by_pubkey(&node_pubkey) { - if let Some(account) = self.accounts.get(user_wallet) { - if account.banned_by.contains(&node.operator_wallet) { - return true; - } - } - } - false - } - - pub fn add_vmnode_to_operator(&self, operator_wallet: &str, node_pubkey: &str) { - self.operators - .entry(operator_wallet.to_string()) - .and_modify(|op| { - op.vm_nodes.insert(node_pubkey.to_string()); - }) - .or_insert(OperatorData { - escrow: 0, - email: String::new(), - banned_users: HashSet::new(), - vm_nodes: HashSet::from([node_pubkey.to_string()]), - }); - } - - pub fn register_operator(&self, req: grpc::RegOperatorReq) -> Result<(), Error> { - let mut operator = match self.operators.get(&req.pubkey) { - Some(o) => (*(o.value())).clone(), - None => OperatorData { - ..Default::default() - }, - }; - if req.escrow < 5000 { - return Err(Error::MinimalEscrow); - } - let escrow = req.escrow * 1_000_000_000; - if let Some(mut account) = self.accounts.get_mut(&req.pubkey) { - if (account.balance + operator.escrow) < escrow { - return Err(Error::InsufficientFunds); - } - account.balance = account.balance + operator.escrow - escrow; - operator.escrow = escrow; - } else { - return Err(Error::InsufficientFunds); - } - operator.email = req.email; - self.operators.insert(req.pubkey, operator); - Ok(()) - } - - pub fn find_vm_nodes_by_operator(&self, operator_wallet: &str) -> Vec { - let nodes = self.vm_nodes.read().unwrap(); - nodes - .iter() - .filter(|node| node.operator_wallet == operator_wallet) - .cloned() - .collect() - } - - pub fn total_operator_reports(&self, operator_wallet: &str) -> usize { - let nodes = self.vm_nodes.read().unwrap(); - nodes - .iter() - .cloned() - .filter(|n| n.operator_wallet == operator_wallet) - .map(|node| node.reports.len()) - .sum() - } - - pub fn find_vm_nodes_by_filters( + pub fn find_nodes_by_filters( &self, filters: &crate::grpc::snp_proto::VmNodeFilters, ) -> Vec { @@ -944,13 +747,9 @@ impl BrainData { .cloned() } - pub fn find_contract_by_uuid(&self, uuid: &str) -> Result { + pub fn find_contract_by_uuid(&self, uuid: &str) -> Option { let contracts = self.vm_contracts.read().unwrap(); - contracts - .iter() - .cloned() - .find(|c| c.uuid == uuid) - .ok_or(Error::VmContractNotFound(uuid.to_string())) + contracts.iter().cloned().find(|c| c.uuid == uuid) } pub fn list_all_contracts(&self) -> Vec { @@ -969,72 +768,21 @@ impl BrainData { .collect() } - pub fn list_operators(&self) -> Vec { - self.operators - .iter() - .map(|op| grpc::ListOperatorsResp { - pubkey: op.key().to_string(), - escrow: op.escrow / 1_000_000_000, - email: op.email.clone(), - app_nodes: 0, - vm_nodes: op.vm_nodes.len() as u64, - reports: self.total_operator_reports(op.key()) as u64, - }) - .collect() - } - - pub fn inspect_operator(&self, wallet: &str) -> Option { - self.operators.get(wallet).map(|op| { - let nodes = self - .find_vm_nodes_by_operator(wallet) - .into_iter() - .map(|n| n.into()) - .collect(); - grpc::InspectOperatorResp { - operator: Some(grpc::ListOperatorsResp { - pubkey: op.key().to_string(), - escrow: op.escrow, - email: op.email.clone(), - app_nodes: 0, - vm_nodes: op.vm_nodes.len() as u64, - reports: self.total_operator_reports(op.key()) as u64, - }), - nodes, - } - }) - } - - pub fn find_vm_contracts_by_operator(&self, wallet: &str) -> Vec { - debug!("Searching contracts for operator {wallet}"); - let nodes = match self.operators.get(wallet) { - Some(op) => op.vm_nodes.clone(), - None => return Vec::new(), - }; + pub fn find_contracts_by_admin_pubkey(&self, admin_pubkey: &str) -> Vec { + debug!("Searching contracts for admin pubkey {admin_pubkey}"); let contracts: Vec = self .vm_contracts .read() .unwrap() .iter() - .filter(|c| nodes.contains(&c.node_pubkey)) + .filter(|c| c.admin_pubkey == admin_pubkey) .cloned() .collect(); + debug!("Found {} contracts or {admin_pubkey}.", contracts.len()); contracts } - pub fn find_vm_contracts_by_admin(&self, admin_wallet: &str) -> Vec { - debug!("Searching contracts for admin pubkey {admin_wallet}"); - let contracts: Vec = self - .vm_contracts - .read() - .unwrap() - .iter() - .filter(|c| c.admin_pubkey == admin_wallet) - .cloned() - .collect(); - contracts - } - - pub fn find_vm_contracts_by_node(&self, node_pubkey: &str) -> Vec { + pub fn find_contracts_by_node_pubkey(&self, node_pubkey: &str) -> Vec { let contracts = self.vm_contracts.read().unwrap(); contracts .iter() @@ -1043,3 +791,204 @@ impl BrainData { .collect() } } + +impl BrainData { + pub fn add_app_daemon_tx(&self, node_pubkey: &str, tx: Sender) { + self.app_daemon_tx.insert(node_pubkey.to_string(), tx); + } + + pub fn del_app_daemon_tx(&self, node_pubkey: &str) { + self.app_daemon_tx.remove(node_pubkey); + } + + pub fn insert_app_node(&self, node: AppNode) { + let mut nodes = self.app_nodes.write().unwrap(); + for n in nodes.iter_mut() { + if n.node_pubkey == node.node_pubkey { + // TODO: figure what to do in this case. + warn!("Node {} already exists. Updating data.", n.node_pubkey); + *n = node; + return; + } + } + nodes.push(node); + } + + pub fn find_app_contract_by_uuid(&self, uuid: &str) -> Option { + let contracts = self.app_contracts.read().unwrap(); + contracts.iter().find(|c| c.uuid == uuid).cloned() + } + + pub fn find_app_contracts_by_admin_pubkey(&self, admin_pubkey: &str) -> Vec { + debug!("Searching contracts for admin pubkey {admin_pubkey}"); + let contracts: Vec = self + .app_contracts + .read() + .unwrap() + .iter() + .filter(|c| c.admin_pubkey == admin_pubkey) + .cloned() + .collect(); + debug!("Found {} contracts or {admin_pubkey}.", contracts.len()); + contracts + } + + pub fn find_app_contracts_by_node_pubkey(&self, node_pubkey: &str) -> Vec { + let app_contracts = self.app_contracts.read().unwrap(); + app_contracts + .iter() + .filter(|c| c.node_pubkey == node_pubkey) + .cloned() + .collect() + } + + pub fn submit_app_node_resources(&self, node_resource: AppNodeResources) { + debug!("{:#?}", &node_resource); + let mut nodes = self.app_nodes.write().unwrap(); + for n in nodes.iter_mut() { + if n.node_pubkey == node_resource.node_pubkey { + debug!( + "Found node {}. Updating resources to {:?}", + n.node_pubkey, node_resource + ); + n.avail_vcpus = node_resource.avail_vcpus; + n.avail_mem_mb = node_resource.avail_memory_mb; + n.avail_storage_mb = node_resource.avail_storage_mb; + n.max_ports_per_app = node_resource.max_ports_per_app; + n.avail_no_of_port = node_resource.avail_no_of_port; + return; + } + } + debug!( + "VM Node {} not found when trying to update resources.", + node_resource.node_pubkey + ); + debug!("VM Node list:\n{:?}", nodes); + } + + pub async fn send_new_container_req(&self, mut req: NewAppReq, tx: OneshotSender) { + req.uuid = uuid::Uuid::new_v4().to_string(); + + info!("Inserting new container request in memory: {req:?}"); + self.tmp_new_container_reqs + .insert(req.uuid.clone(), (req.clone(), tx)); + + if let Some(app_daemon_tx) = self.app_daemon_tx.get(&req.node_pubkey) { + debug!( + "Found daemon TX for {}. Sending newVMReq {}", + req.node_pubkey, req.uuid + ); + let msg = BrainMessageApp { + msg: Some( + detee_shared::sgx::pb::brain::brain_message_app::Msg::NewAppReq(req.clone()), + ), + }; + if let Err(e) = app_daemon_tx.send(msg).await { + warn!( + "Failed to send new container request to {} due to error: {e:?}", + req.node_pubkey + ); + info!("Deleting daemon TX for {}", req.node_pubkey); + self.del_app_daemon_tx(&req.node_pubkey); + self.send_new_container_resp(NewAppRes { + uuid: req.uuid, + status: "failed".to_string(), + error: "Daemon is offline.".to_string(), + ..Default::default() + }) + .await; + } + } + // TODO: implement daemon offline handling + } + + pub async fn send_del_container_req( + &self, + req: DelAppReq, + ) -> Result<(), Box> { + if let Some(app_contract) = self.find_app_contract_by_uuid(&req.uuid) { + info!("Found app contract {}. Deleting...", &req.uuid); + if let Some(app_daemon_tx) = self.app_daemon_tx.get(&app_contract.node_pubkey) { + debug!( + "TX for daemon {} found. Informing daemon about deletion of {}.", + app_contract.node_pubkey, &req.uuid + ); + let msg = BrainMessageApp { + msg: Some( + detee_shared::sgx::pb::brain::brain_message_app::Msg::DeleteAppReq( + req.clone(), + ), + ), + }; + + if let Err(e) = app_daemon_tx.send(msg).await { + warn!( + "Failed to send deletion request to {} due to error: {e:?}", + app_contract.node_pubkey + ); + info!("Deleting daemon TX for {}", app_contract.node_pubkey); + self.del_app_daemon_tx(&app_contract.node_pubkey); + } + } + + let mut app_contracts = self.app_contracts.write().unwrap(); + app_contracts.retain(|c| c.uuid != req.uuid); + + Ok(()) + } else { + Err("Contract not found".into()) + } + } + + pub async fn send_new_container_resp(&self, new_container_resp: NewAppRes) { + let new_container_req = match self.tmp_new_container_reqs.remove(&new_container_resp.uuid) { + Some((_, r)) => r, + None => { + log::error!( + "Received confirmation for ghost new container req {}", + new_container_resp.uuid + ); + return; + } + }; + if let Err(err) = new_container_req.1.send(new_container_resp.clone()) { + log::error!( + "CLI RX for {} dropped before receiving confirmation {:?}.\n{:?}", + &new_container_req.0.admin_pubkey, + new_container_resp, + err + ); + } + + if new_container_resp.error != "" { + return; + } + + let requested_resource = new_container_req.0.resource.clone().unwrap_or_default(); + + let app_contracts = AppContract { + uuid: new_container_req.0.uuid, + package_url: new_container_req.0.package_url, + admin_pubkey: new_container_req.0.admin_pubkey, + node_pubkey: new_container_req.0.node_pubkey.clone(), + mapped_ports: new_container_resp + .mapped_ports + .iter() + .map(|p| (p.host_port as u16, p.app_port as u16)) + .collect::>(), + host_ipv4: new_container_resp.ip_address, + disk_size_mb: requested_resource.disk_mb, + vcpus: requested_resource.vcpu, + memory_mb: requested_resource.memory_mb, + created_at: Utc::now(), + updated_at: Utc::now(), + price_per_unit: new_container_req.0.price_per_unit, + locked_nano: new_container_req.0.locked_nano, + collected_at: Utc::now(), + hratls_pubkey: new_container_req.0.hratls_pubkey, + public_package_mr_enclave: new_container_req.0.public_package_mr_enclave, + }; + log::info!("Created new app contract: {app_contracts:?}"); + self.app_contracts.write().unwrap().push(app_contracts); + } +} diff --git a/src/grpc.rs b/src/grpc.rs index 370d075..2fb07bd 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + pub mod snp_proto { tonic::include_proto!("vm_proto"); } @@ -14,10 +16,15 @@ use tokio::sync::mpsc; use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt}; use tonic::{Request, Response, Status, Streaming}; +use detee_shared::sgx::pb::brain::brain_app_cli_server::BrainAppCli; +use detee_shared::sgx::pb::brain::brain_app_daemon_server::BrainAppDaemon; +use detee_shared::sgx::pb::brain::{ + AppContract, BrainMessageApp, DaemonMessageApp, DelAppReq, ListAppContractsReq, NewAppReq, + NewAppRes, RegisterAppNodeReq, +}; const ADMIN_ACCOUNTS: &[&str] = &[ "x52w7jARC5erhWWK65VZmjdGXzBK6ZDgfv1A283d8XK", "FHuecMbeC1PfjkW2JKyoicJAuiU7khgQT16QUB3Q1XdL", - "H21Shi4iE7vgfjWEQNvzmpmBMJSaiZ17PYUcdNoAoKNc", ]; pub struct BrainDaemonMock { @@ -40,6 +47,26 @@ impl BrainCliMock { } } +pub struct BrainAppCliMock { + data: Arc, +} + +impl BrainAppCliMock { + pub fn new(data: Arc) -> Self { + Self { data } + } +} + +pub struct BrainAppDaemonMock { + data: Arc, +} + +impl BrainAppDaemonMock { + pub fn new(data: Arc) -> Self { + Self { data } + } +} + #[tonic::async_trait] impl BrainVmDaemon for BrainDaemonMock { type RegisterVmNodeStream = Pin> + Send>>; @@ -51,7 +78,7 @@ impl BrainVmDaemon for BrainDaemonMock { info!("Starting registration process for {:?}", req); let node = crate::data::VmNode { public_key: req.node_pubkey.clone(), - operator_wallet: req.operator_wallet, + owner_key: req.owner_pubkey, country: req.country, region: req.region, city: req.city, @@ -59,10 +86,10 @@ impl BrainVmDaemon for BrainDaemonMock { price: req.price, ..Default::default() }; - self.data.register_node(node); + self.data.insert_node(node); info!("Sending existing contracts to {}", req.node_pubkey); - let contracts = self.data.find_vm_contracts_by_node(&req.node_pubkey); + let contracts = self.data.find_contracts_by_node_pubkey(&req.node_pubkey); let (tx, rx) = mpsc::channel(6); tokio::spawn(async move { for contract in contracts { @@ -160,14 +187,6 @@ impl BrainCli for BrainCliMock { async fn new_vm(&self, req: Request) -> Result, Status> { let req = check_sig_from_req(req)?; info!("New VM requested via CLI: {req:?}"); - if self - .data - .is_user_banned_by_node(&req.admin_pubkey, &req.node_pubkey) - { - return Err(Status::permission_denied( - "This operator banned you. What did you do?", - )); - } let admin_pubkey = req.admin_pubkey.clone(); let (oneshot_tx, oneshot_rx) = tokio::sync::oneshot::channel(); self.data.submit_newvm_req(req, oneshot_tx).await; @@ -195,9 +214,9 @@ impl BrainCli for BrainCliMock { info!("Sending UpdateVMResp: {response:?}"); Ok(Response::new(response)) } - Err(e) => Err(Status::unknown(format!( - "Update VM request failed due to error: {e}" - ))), + Err(_) => Err(Status::unknown( + "Update VM request failed due to error: {e}", + )), } } @@ -212,55 +231,20 @@ impl BrainCli for BrainCliMock { } } - async fn delete_vm(&self, req: Request) -> Result, Status> { - let req = check_sig_from_req(req)?; - match self.data.delete_vm(req).await { - Ok(()) => Ok(Response::new(Empty {})), - Err(e) => Err(Status::not_found(e.to_string())), - } - } - - async fn report_node(&self, req: Request) -> Result, Status> { - let req = check_sig_from_req(req)?; - match self.data.find_contract_by_uuid(&req.contract) { - Ok(contract) - if contract.admin_pubkey == req.admin_pubkey - && contract.node_pubkey == req.node_pubkey => - { - () - } - _ => return Err(Status::unauthenticated("No contract found by this ID.")), - }; - self.data - .report_node(req.admin_pubkey, &req.node_pubkey, req.reason); - Ok(Response::new(Empty {})) - } - type ListVmContractsStream = Pin> + Send>>; async fn list_vm_contracts( &self, req: Request, ) -> Result, Status> { let req = check_sig_from_req(req)?; - info!( - "CLI {} requested ListVMVmContractsStream. As operator: {}", - req.wallet, req.as_operator - ); - let mut contracts = Vec::new(); - if !req.uuid.is_empty() { - if let Ok(specific_contract) = self.data.find_contract_by_uuid(&req.uuid) { - if specific_contract.admin_pubkey == req.wallet { - contracts.push(specific_contract); - } - // TODO: allow operator to inspect contracts - } - } else { - if req.as_operator { - contracts.append(&mut self.data.find_vm_contracts_by_operator(&req.wallet)); - } else { - contracts.append(&mut self.data.find_vm_contracts_by_admin(&req.wallet)); - } - } + info!("CLI {} requested ListVMVmContractsStream", req.admin_pubkey); + let contracts = match req.uuid.is_empty() { + false => match self.data.find_contract_by_uuid(&req.uuid) { + Some(contract) => vec![contract], + None => Vec::new(), + }, + true => self.data.find_contracts_by_admin_pubkey(&req.admin_pubkey), + }; let (tx, rx) = mpsc::channel(6); tokio::spawn(async move { for contract in contracts { @@ -280,7 +264,7 @@ impl BrainCli for BrainCliMock { ) -> Result, tonic::Status> { let req = check_sig_from_req(req)?; info!("CLI requested ListVmNodesStream: {req:?}"); - let nodes = self.data.find_vm_nodes_by_filters(&req); + let nodes = self.data.find_nodes_by_filters(&req); let (tx, rx) = mpsc::channel(6); tokio::spawn(async move { for node in nodes { @@ -307,65 +291,12 @@ impl BrainCli for BrainCliMock { } } - async fn register_operator( - &self, - req: Request, - ) -> Result, Status> { + async fn delete_vm(&self, req: Request) -> Result, Status> { let req = check_sig_from_req(req)?; - info!("Regitering new operator: {req:?}"); - match self.data.register_operator(req) { + info!("Unknown CLI requested to delete vm {}", req.uuid); + match self.data.delete_vm(req).await { Ok(()) => Ok(Response::new(Empty {})), - Err(e) => Err(Status::failed_precondition(e.to_string())), - } - } - - async fn kick_contract(&self, req: Request) -> Result, Status> { - let req = check_sig_from_req(req)?; - match self - .data - .kick_contract(&req.operator_wallet, &req.contract_uuid, &req.reason) - .await - { - Ok(nano_lp) => Ok(Response::new(KickResp { nano_lp })), - Err(e) => Err(Status::permission_denied(e.to_string())), - } - } - - async fn ban_user(&self, req: Request) -> Result, Status> { - let req = check_sig_from_req(req)?; - self.data.ban_user(&req.operator_wallet, &req.user_wallet); - Ok(Response::new(Empty {})) - } - - type ListOperatorsStream = - Pin> + Send>>; - async fn list_operators( - &self, - req: Request, - ) -> Result, Status> { - let _ = check_sig_from_req(req)?; - let operators = self.data.list_operators(); - let (tx, rx) = mpsc::channel(6); - tokio::spawn(async move { - for op in operators { - let _ = tx.send(Ok(op.into())).await; - } - }); - let output_stream = ReceiverStream::new(rx); - Ok(Response::new( - Box::pin(output_stream) as Self::ListOperatorsStream - )) - } - - async fn inspect_operator( - &self, - req: Request, - ) -> Result, Status> { - match self.data.inspect_operator(&req.into_inner().pubkey) { - Some(op) => Ok(Response::new(op.into())), - None => Err(Status::not_found( - "The wallet you specified is not an operator", - )), + Err(e) => Err(Status::not_found(e.to_string())), } } @@ -376,13 +307,6 @@ impl BrainCli for BrainCliMock { Ok(Response::new(Empty {})) } - async fn slash(&self, req: Request) -> Result, Status> { - check_admin_key(&req)?; - let req = check_sig_from_req(req)?; - self.data.slash_account(&req.pubkey, req.tokens); - Ok(Response::new(Empty {})) - } - type ListAllVmContractsStream = Pin> + Send>>; async fn list_all_vm_contracts( &self, @@ -428,39 +352,280 @@ trait PubkeyGetter { fn get_pubkey(&self) -> Option; } -macro_rules! impl_pubkey_getter { - ($t:ty, $field:ident) => { - impl PubkeyGetter for $t { - fn get_pubkey(&self) -> Option { - Some(self.$field.clone()) - } - } - }; - ($t:ty) => { - impl PubkeyGetter for $t { - fn get_pubkey(&self) -> Option { - None - } - } - }; +impl PubkeyGetter for Pubkey { + fn get_pubkey(&self) -> Option { + Some(self.pubkey.clone()) + } } -impl_pubkey_getter!(Pubkey, pubkey); -impl_pubkey_getter!(NewVmReq, admin_pubkey); -impl_pubkey_getter!(DeleteVmReq, admin_pubkey); -impl_pubkey_getter!(UpdateVmReq, admin_pubkey); -impl_pubkey_getter!(ExtendVmReq, admin_pubkey); -impl_pubkey_getter!(ReportNodeReq, admin_pubkey); -impl_pubkey_getter!(ListVmContractsReq, wallet); -impl_pubkey_getter!(RegisterVmNodeReq, node_pubkey); -impl_pubkey_getter!(RegOperatorReq, pubkey); -impl_pubkey_getter!(KickReq, operator_wallet); -impl_pubkey_getter!(BanUserReq, operator_wallet); +#[tonic::async_trait] +impl BrainAppCli for BrainAppCliMock { + type ListAppContractsStream = Pin> + Send>>; -impl_pubkey_getter!(VmNodeFilters); -impl_pubkey_getter!(Empty); -impl_pubkey_getter!(AirdropReq); -impl_pubkey_getter!(SlashReq); + async fn create_app( + &self, + req: tonic::Request, + ) -> Result, Status> { + let req_data = check_sig_from_req(req)?; + log::info!("Creating new container: {req_data:?}"); + let admin_pubkey = req_data.admin_pubkey.clone(); + let (oneshot_tx, oneshot_rx) = tokio::sync::oneshot::channel(); + self.data.send_new_container_req(req_data, oneshot_tx).await; + + match oneshot_rx.await { + Ok(response) => { + info!("responding container confirmation to {admin_pubkey}: {response:?}"); + Ok(Response::new(response)) + } + Err(e) => { + log::error!("Something went wrong. Reached error {e:?}"); + Err(Status::unknown( + "Request failed due to unknown error. Please try again or contact the DeTEE devs team.", + )) + } + } + } + + async fn delete_app( + &self, + req: tonic::Request, + ) -> Result, Status> { + let req_data = check_sig_from_req(req)?; + log::info!("deleting container: {}", req_data.uuid.clone()); + if let Err(er) = self.data.send_del_container_req(req_data).await { + info!("Could not delete container: {er}"); + return Err(Status::not_found("Could not find container")); + }; + + Ok(Response::new(detee_shared::sgx::pb::brain::Empty {})) + } + + async fn list_app_contracts( + &self, + req: tonic::Request, + ) -> Result, Status> { + let req_data = check_sig_from_req(req)?; + let app_contracts = self + .data + .find_app_contracts_by_admin_pubkey(&req_data.admin_pubkey); + + let (tx, rx) = mpsc::channel(6); + tokio::spawn(async move { + for contract in app_contracts { + let _ = tx.send(contract.into()).await; + } + }); + let output_stream = ReceiverStream::new(rx).map(Ok); + Ok(Response::new( + Box::pin(output_stream) as Self::ListAppContractsStream + )) + } +} + +#[tonic::async_trait] +impl BrainAppDaemon for BrainAppDaemonMock { + type RegisterAppNodeStream = Pin> + Send>>; + type BrainMessagesStream = Pin> + Send>>; + + async fn register_app_node( + &self, + req: tonic::Request, + ) -> Result, Status> { + let req_data = check_sig_from_req(req)?; + log::info!( + "registering app node_key : {}, operator_key: {}", + &req_data.node_pubkey, + &req_data.operator_pubkey + ); + + let app_node = crate::data::AppNode { + node_pubkey: req_data.node_pubkey.clone(), + operator_pubkey: req_data.operator_pubkey, + ip: req_data.main_ip, + city: req_data.city, + region: req_data.region, + country: req_data.country, + ..Default::default() + }; + + self.data.insert_app_node(app_node); + log::info!("Sending existing contracts to {}", &req_data.node_pubkey); + + let app_contracts = self + .data + .find_app_contracts_by_node_pubkey(&req_data.node_pubkey); + + let (tx, rx) = mpsc::channel(6); + tokio::spawn(async move { + for contract in app_contracts { + let _ = tx.send(contract.into()).await; + } + }); + let output_stream = ReceiverStream::new(rx).map(Ok); + Ok(Response::new(Box::pin(output_stream))) + } + + async fn brain_messages( + &self, + req: tonic::Request, + ) -> Result, Status> { + let req_data = req.into_inner(); + let pubkey = req_data.pubkey.clone(); + check_sig_from_parts( + &pubkey, + &req_data.timestamp, + &format!("{:?}", req_data.contracts), + &req_data.signature, + )?; + + info!( + "Daemon {} connected to receive brain messages", + req_data.pubkey + ); + let (tx, rx) = mpsc::channel(6); + self.data.add_app_daemon_tx(&req_data.pubkey, tx); + let output_stream = ReceiverStream::new(rx).map(Ok); + Ok(Response::new( + Box::pin(output_stream) as Self::BrainMessagesStream + )) + } + + async fn daemon_messages( + &self, + req: tonic::Request>, + ) -> Result, Status> { + let mut req_stream = req.into_inner(); + let mut pubkey; + + if let Some(Ok(msg)) = req_stream.next().await { + log::debug!( + "demon_messages received the following auth message: {:?}", + msg.msg + ); + if let Some(detee_shared::sgx::pb::brain::daemon_message_app::Msg::Auth(auth)) = msg.msg + { + pubkey = auth.pubkey.clone(); + check_sig_from_parts( + &pubkey, + &auth.timestamp, + &format!("{:?}", auth.contracts), + &auth.signature, + )?; + } else { + return Err(Status::unauthenticated( + "Could not authenticate the daemon: could not extract auth signature", + )); + } + } else { + return Err(Status::unauthenticated("Could not authenticate the daemon")); + } + + while let Some(daemon_message) = req_stream.next().await { + match daemon_message { + Ok(msg) => match msg.msg { + Some(detee_shared::sgx::pb::brain::daemon_message_app::Msg::Auth( + daemon_auth, + )) => pubkey = daemon_auth.pubkey, + Some(detee_shared::sgx::pb::brain::daemon_message_app::Msg::NewAppRes( + new_app_res, + )) => self.data.send_new_container_resp(new_app_res).await, + Some( + detee_shared::sgx::pb::brain::daemon_message_app::Msg::AppNodeResources( + node_resource, + ), + ) => self.data.submit_app_node_resources(node_resource), + _ => { + dbg!("None"); + } + }, + Err(e) => { + log::warn!("Daemon disconnected: {e:?}"); + self.data.del_app_daemon_tx(&pubkey); + } + } + // + } + + Ok(Response::new(detee_shared::sgx::pb::brain::Empty {})) + } +} +impl PubkeyGetter for NewVmReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for DeleteVmReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for UpdateVmReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for ExtendVmReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for ListVmContractsReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for VmNodeFilters { + fn get_pubkey(&self) -> Option { + None + } +} + +impl PubkeyGetter for RegisterVmNodeReq { + fn get_pubkey(&self) -> Option { + Some(self.node_pubkey.clone()) + } +} + +impl PubkeyGetter for Empty { + fn get_pubkey(&self) -> Option { + None + } +} + +impl PubkeyGetter for AirdropReq { + fn get_pubkey(&self) -> Option { + None + } +} + +impl PubkeyGetter for RegisterAppNodeReq { + fn get_pubkey(&self) -> Option { + None + } +} + +impl PubkeyGetter for NewAppReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for DelAppReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} + +impl PubkeyGetter for ListAppContractsReq { + fn get_pubkey(&self) -> Option { + Some(self.admin_pubkey.clone()) + } +} fn check_sig_from_req(req: Request) -> Result { let time = match req.metadata().get("timestamp") { @@ -475,9 +640,9 @@ fn check_sig_from_req(req: Request) -> Res let parsed_time = chrono::DateTime::parse_from_rfc3339(time) .map_err(|_| Status::unauthenticated("Coult not parse timestamp"))?; let seconds_elapsed = now.signed_duration_since(parsed_time).num_seconds(); - if seconds_elapsed > 4 || seconds_elapsed < -4 { + if seconds_elapsed > 1 || seconds_elapsed < -1 { return Err(Status::unauthenticated(format!( - "Date is not within 4 sec of the time of the server: CLI {} vs Server {}", + "Date is not within 1 sec of the time of the server: CLI {} vs Server {}", parsed_time, now ))); } @@ -530,9 +695,9 @@ fn check_sig_from_parts(pubkey: &str, time: &str, msg: &str, sig: &str) -> Resul let parsed_time = chrono::DateTime::parse_from_rfc3339(time) .map_err(|_| Status::unauthenticated("Coult not parse timestamp"))?; let seconds_elapsed = now.signed_duration_since(parsed_time).num_seconds(); - if seconds_elapsed > 4 || seconds_elapsed < -4 { + if seconds_elapsed > 1 || seconds_elapsed < -1 { return Err(Status::unauthenticated(format!( - "Date is not within 4 sec of the time of the server: CLI {} vs Server {}", + "Date is not within 1 sec of the time of the server: CLI {} vs Server {}", parsed_time, now ))); } diff --git a/src/main.rs b/src/main.rs index 64936a3..37594ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,12 @@ mod data; mod grpc; use data::BrainData; +use detee_shared::sgx::pb::brain::brain_app_cli_server::BrainAppCliServer; +use detee_shared::sgx::pb::brain::brain_app_daemon_server::BrainAppDaemonServer; use grpc::snp_proto::brain_cli_server::BrainCliServer; use grpc::snp_proto::brain_vm_daemon_server::BrainVmDaemonServer; +use grpc::BrainAppCliMock; +use grpc::BrainAppDaemonMock; use grpc::BrainCliMock; use grpc::BrainDaemonMock; use std::sync::Arc; @@ -19,11 +23,7 @@ async fn main() { tokio::spawn(async move { loop { tokio::time::sleep(tokio::time::Duration::from_secs(60)).await; - data_clone.vm_nodes_cron().await; data_clone.vm_contracts_cron().await; - if let Err(e) = data_clone.save_to_disk() { - log::error!("Could not save data to disk due to error: {e}") - } } }); let addr = "0.0.0.0:31337".parse().unwrap(); @@ -31,9 +31,14 @@ async fn main() { let daemon_server = BrainVmDaemonServer::new(BrainDaemonMock::new(data.clone())); let cli_server = BrainCliServer::new(BrainCliMock::new(data.clone())); + let sgx_cli_server = BrainAppCliServer::new(BrainAppCliMock::new(data.clone())); + let sgx_daemon_server = BrainAppDaemonServer::new(BrainAppDaemonMock::new(data.clone())); + Server::builder() .add_service(daemon_server) .add_service(cli_server) + .add_service(sgx_cli_server) + .add_service(sgx_daemon_server) .serve(addr) .await .unwrap(); diff --git a/vm.proto b/vm.proto index f0f4d2e..e755772 100644 --- a/vm.proto +++ b/vm.proto @@ -55,7 +55,7 @@ message MeasurementIP { // This should also include a block hash or similar, for auth message RegisterVmNodeReq { string node_pubkey = 1; - string operator_wallet = 2; + string owner_pubkey = 2; string main_ip = 3; string country = 4; string region = 5; @@ -154,8 +154,8 @@ service BrainVmDaemon { } message ListVmContractsReq { - string wallet = 1; - bool as_operator = 2; + string admin_pubkey = 1; + string node_pubkey = 2; string uuid = 3; } @@ -174,14 +174,15 @@ message VmNodeFilters { } message VmNodeListResp { - string operator = 1; - string node_pubkey = 2; - string country = 3; - string region = 4; - string city = 5; - string ip = 6; // required for latency test - repeated string reports = 7; // TODO: this will become an enum - uint64 price = 8; // nanoLP per unit per minute + string node_pubkey = 1; + string country = 2; + string region = 3; + string city = 4; + string ip = 5; // required for latency test + uint32 server_rating = 6; + uint32 provider_rating = 7; + // nanoLP per unit per minute + uint64 price = 8; } message ExtendVmReq { @@ -195,59 +196,12 @@ message AirdropReq { uint64 tokens = 2; } -message SlashReq { - string pubkey = 1; - uint64 tokens = 2; -} - message Account { string pubkey = 1; uint64 balance = 2; uint64 tmp_locked = 3; } -message RegOperatorReq { - string pubkey = 1; - uint64 escrow = 2; - string email = 3; -} - -message ListOperatorsResp { - string pubkey = 1; - uint64 escrow = 2; - string email = 3; - uint64 app_nodes = 4; - uint64 vm_nodes = 5; - uint64 reports = 6; -} - -message InspectOperatorResp { - ListOperatorsResp operator = 1; - repeated VmNodeListResp nodes = 2; -} - -message ReportNodeReq { - string admin_pubkey = 1; - string node_pubkey = 2; - string contract = 3; - string reason = 4; -} - -message KickReq { - string operator_wallet = 1; - string contract_uuid = 2; - string reason = 3; -} - -message BanUserReq { - string operator_wallet = 1; - string user_wallet = 2; -} - -message KickResp { - uint64 nano_lp = 1; -} - service BrainCli { rpc GetBalance (Pubkey) returns (AccountBalance); rpc NewVm (NewVmReq) returns (NewVmResp); @@ -257,15 +211,8 @@ service BrainCli { rpc DeleteVm (DeleteVmReq) returns (Empty); rpc UpdateVm (UpdateVmReq) returns (UpdateVmResp); rpc ExtendVm (ExtendVmReq) returns (Empty); - rpc ReportNode (ReportNodeReq) returns (Empty); - rpc ListOperators (Empty) returns (stream ListOperatorsResp); - rpc InspectOperator (Pubkey) returns (InspectOperatorResp); - rpc RegisterOperator (RegOperatorReq) returns (Empty); - rpc KickContract (KickReq) returns (KickResp); - rpc BanUser (BanUserReq) returns (Empty); // admin commands rpc Airdrop (AirdropReq) returns (Empty); - rpc Slash (SlashReq) returns (Empty); rpc ListAllVmContracts (Empty) returns (stream VmContract); rpc ListAccounts (Empty) returns (stream Account); }