From 7acd1b9c2e1a52090c751ba17a437b253c9a0dea Mon Sep 17 00:00:00 2001 From: Avi Dessauer Date: Thu, 25 Jan 2024 21:10:37 -0600 Subject: [PATCH] Add transaction tests and fix bugs --- Cargo.lock | 512 ++++++++++----------------- kairos-server/Cargo.toml | 12 +- kairos-server/src/lib.rs | 13 +- kairos-server/src/routes/deposit.rs | 16 +- kairos-server/src/routes/mod.rs | 4 + kairos-server/src/routes/transfer.rs | 22 +- kairos-server/src/routes/withdraw.rs | 10 +- kairos-server/src/state.rs | 1 + kairos-server/tests/transactions.rs | 140 ++++++++ 9 files changed, 369 insertions(+), 361 deletions(-) create mode 100644 kairos-server/tests/transactions.rs diff --git a/Cargo.lock b/Cargo.lock index 0742e9d0..5f24967b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.79" @@ -35,42 +44,16 @@ dependencies = [ ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "auto-future" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "3c1e7e457ea78e524f48639f551fd79703ac3f2237f5ecccdf4708f8a75ad373" [[package]] -name = "axum" -version = "0.6.20" +name = "autocfg" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core 0.3.4", - "bitflags 1.3.2", - "bytes", - "futures-util", - "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower", - "tower-layer", - "tower-service", -] +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" @@ -79,13 +62,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" dependencies = [ "async-trait", - "axum-core 0.4.3", + "axum-core", "bytes", "futures-util", "http 1.0.0", - "http-body 1.0.0", + "http-body", "http-body-util", - "hyper 1.1.0", + "hyper", "hyper-util", "itoa", "matchit", @@ -106,23 +89,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 0.2.11", - "http-body 0.4.6", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - [[package]] name = "axum-core" version = "0.4.3" @@ -133,7 +99,7 @@ dependencies = [ "bytes", "futures-util", "http 1.0.0", - "http-body 1.0.0", + "http-body", "http-body-util", "mime", "pin-project-lite", @@ -150,15 +116,15 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "895ff42f72016617773af68fb90da2a9677d89c62338ec09162d4909d86fdd8f" dependencies = [ - "axum 0.7.4", - "axum-core 0.4.3", + "axum", + "axum-core", "axum-macros", "bytes", "form_urlencoded", "futures-util", "headers", "http 1.0.0", - "http-body 1.0.0", + "http-body", "http-body-util", "mime", "percent-encoding", @@ -185,21 +151,32 @@ dependencies = [ ] [[package]] -name = "axum-test-helper" -version = "0.3.0" +name = "axum-test" +version = "14.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298f62fa902c2515c169ab0bfb56c593229f33faa01131215d58e3d4898e3aa9" +checksum = "e2d15e9969313df61a64e25ce39cc8e586d42432696a0c8e0cfac1d377013d9c" dependencies = [ - "axum 0.6.20", + "anyhow", + "async-trait", + "auto-future", + "axum", "bytes", - "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", - "reqwest", + "cookie", + "http 1.0.0", + "http-body-util", + "hyper", + "hyper-util", + "mime", + "pretty_assertions", + "reserve-port", + "rust-multipart-rfc7578_2", "serde", + "serde_json", + "serde_urlencoded", + "smallvec", "tokio", "tower", - "tower-service", + "url", ] [[package]] @@ -259,12 +236,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bumpalo" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - [[package]] name = "bytes" version = "1.5.0" @@ -287,21 +258,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "core-foundation" -version = "0.9.4" +name = "cookie" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8" dependencies = [ - "core-foundation-sys", - "libc", + "time", + "version_check", ] -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - [[package]] name = "cpufeatures" version = "0.2.12" @@ -321,6 +286,21 @@ dependencies = [ "typenum", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.10.7" @@ -337,15 +317,6 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -404,17 +375,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "futures-sink" version = "0.3.30" @@ -435,8 +395,6 @@ checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-io", - "futures-macro", - "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -471,25 +429,6 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -[[package]] -name = "h2" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.11", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "h2" version = "0.4.1" @@ -573,17 +512,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.11", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.0" @@ -603,7 +531,7 @@ dependencies = [ "bytes", "futures-util", "http 1.0.0", - "http-body 1.0.0", + "http-body", "pin-project-lite", ] @@ -619,30 +547,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "hyper" -version = "0.14.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.23", - "http 0.2.11", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.1.0" @@ -652,14 +556,15 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.1", + "h2", "http 1.0.0", - "http-body 1.0.0", + "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", "tokio", + "want", ] [[package]] @@ -672,11 +577,13 @@ dependencies = [ "futures-channel", "futures-util", "http 1.0.0", - "http-body 1.0.0", - "hyper 1.1.0", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", + "tower", + "tower-service", "tracing", ] @@ -700,27 +607,12 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - [[package]] name = "itoa" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" -[[package]] -name = "js-sys" -version = "0.3.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "kairos-cli" version = "0.1.0" @@ -730,9 +622,9 @@ name = "kairos-server" version = "0.1.0" dependencies = [ "anyhow", - "axum 0.7.4", + "axum", "axum-extra", - "axum-test-helper", + "axum-test", "dotenvy", "proptest", "serde", @@ -783,6 +675,15 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -943,12 +844,28 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro2" version = "1.0.76" @@ -972,7 +889,7 @@ dependencies = [ "rand", "rand_chacha", "rand_xorshift", - "regex-syntax", + "regex-syntax 0.8.2", "rusty-fork", "tempfile", "unarray", @@ -1041,6 +958,44 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.5", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.2" @@ -1048,41 +1003,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] -name = "reqwest" -version = "0.11.23" +name = "reserve-port" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "9838134a2bfaa8e1f40738fcc972ac799de6e0e06b5157acb95fc2b05a0ea283" +dependencies = [ + "lazy_static", + "thiserror", +] + +[[package]] +name = "rust-multipart-rfc7578_2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b748410c0afdef2ebbe3685a6a862e2ee937127cdaae623336a459451c8d57" dependencies = [ - "base64", "bytes", - "encoding_rs", "futures-core", "futures-util", - "h2 0.3.23", "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", - "ipnet", - "js-sys", - "log", "mime", "mime_guess", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "system-configuration", - "tokio", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "winreg", + "rand", + "thiserror", ] [[package]] @@ -1271,27 +1214,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tempfile" version = "3.9.0" @@ -1335,6 +1257,35 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1473,10 +1424,14 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] @@ -1576,95 +1531,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" - -[[package]] -name = "wasm-streams" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "winapi" version = "0.3.9" @@ -1820,11 +1686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] -name = "winreg" -version = "0.50.0" +name = "yansi" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/kairos-server/Cargo.toml b/kairos-server/Cargo.toml index d05c2445..e2210378 100644 --- a/kairos-server/Cargo.toml +++ b/kairos-server/Cargo.toml @@ -12,16 +12,20 @@ name = "kairos-server" [dependencies] dotenvy = "0.15" -axum = { version = "0.7", features = ["tracing"]} -axum-extra = { version = "0.9", features = ["typed-routing", "typed-header", "json-deserializer"]} +axum = { version = "0.7", features = ["tracing"] } +axum-extra = { version = "0.9", features = [ + "typed-routing", + "typed-header", + "json-deserializer", +] } thiserror = "1.0" anyhow = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "1.35", features = ["full", "tracing", "macros"] } tracing = "0.1" -tracing-subscriber = "0.3" +tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] } [dev-dependencies] proptest = "1.4" -axum-test-helper = "0.3" +axum-test = "14" diff --git a/kairos-server/src/lib.rs b/kairos-server/src/lib.rs index 59fb1094..7efc0001 100644 --- a/kairos-server/src/lib.rs +++ b/kairos-server/src/lib.rs @@ -4,21 +4,18 @@ pub mod routes; pub mod state; use axum::Router; -use axum_extra::routing::{RouterExt, TypedPath}; +use axum_extra::routing::RouterExt; use state::LockedBatchState; pub use errors::AppErr; type PublicKey = String; - -#[derive(TypedPath)] -#[typed_path("/api/v1")] -pub struct ApiV1Path; +type Signature = String; pub fn app_router(state: LockedBatchState) -> Router { Router::new() - .typed_post(routes::deposit::handler) - .typed_post(routes::withdraw::handler) - .typed_post(routes::transfer::handler) + .typed_post(routes::deposit_handler) + .typed_post(routes::withdraw_handler) + .typed_post(routes::transfer_handler) .with_state(state) } diff --git a/kairos-server/src/routes/deposit.rs b/kairos-server/src/routes/deposit.rs index a45d145a..f40cecd8 100644 --- a/kairos-server/src/routes/deposit.rs +++ b/kairos-server/src/routes/deposit.rs @@ -4,20 +4,22 @@ use anyhow::anyhow; use axum::{extract::State, http::StatusCode, Json}; use axum_extra::routing::TypedPath; use serde::{Deserialize, Serialize}; +use tracing::*; use crate::{state::LockedBatchState, AppErr, PublicKey}; -#[derive(TypedPath)] -#[typed_path("/deposit")] +#[derive(TypedPath, Debug, Clone, Copy)] +#[typed_path("/api/v1/deposit")] pub struct DepositPath; -#[derive(Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Deposit { pub public_key: PublicKey, pub amount: u64, } -pub async fn handler( +#[instrument(level = "trace", skip(state), ret)] +pub async fn deposit_handler( _: DepositPath, state: State, Json(Deposit { public_key, amount }): Json, @@ -29,14 +31,16 @@ pub async fn handler( let mut state = state.deref().write().await; let account = state.balances.entry(public_key.clone()); - let prior_balance = account.or_insert(0); - let updated_balance = prior_balance.checked_add(amount).ok_or_else(|| { + let balance = account.or_insert(0); + let updated_balance = balance.checked_add(amount).ok_or_else(|| { AppErr::set_status( anyhow!("deposit would overflow account"), StatusCode::CONFLICT, ) })?; + *balance = updated_balance; + tracing::info!( "Updated account public_key={} balance={}", public_key, diff --git a/kairos-server/src/routes/mod.rs b/kairos-server/src/routes/mod.rs index 13010da8..1ebcb1d2 100644 --- a/kairos-server/src/routes/mod.rs +++ b/kairos-server/src/routes/mod.rs @@ -1,3 +1,7 @@ pub mod deposit; pub mod transfer; pub mod withdraw; + +pub use deposit::deposit_handler; +pub use transfer::transfer_handler; +pub use withdraw::withdraw_handler; diff --git a/kairos-server/src/routes/transfer.rs b/kairos-server/src/routes/transfer.rs index b579eb11..3c2ba15c 100644 --- a/kairos-server/src/routes/transfer.rs +++ b/kairos-server/src/routes/transfer.rs @@ -2,33 +2,27 @@ use anyhow::anyhow; use axum::{extract::State, http::StatusCode, Json}; use axum_extra::routing::TypedPath; use serde::{Deserialize, Serialize}; +use tracing::instrument; -use crate::{state::LockedBatchState, AppErr, PublicKey}; +use crate::{state::LockedBatchState, AppErr, PublicKey, Signature}; #[derive(TypedPath)] -#[typed_path("/transfer")] +#[typed_path("/api/v1/transfer")] pub struct TransferPath; -#[derive(Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Transfer { pub from: PublicKey, + pub signature: Signature, pub to: PublicKey, pub amount: u64, } -#[derive(Serialize, Deserialize)] -pub struct TransferRequest { - transfer: Transfer, - signature: String, -} - -pub async fn handler( +#[instrument(level = "trace", skip(state), ret)] +pub async fn transfer_handler( _: TransferPath, State(state): State, - Json(TransferRequest { - transfer, - signature: _, - }): Json, + Json(transfer): Json, ) -> Result<(), AppErr> { if transfer.amount == 0 { return Err(AppErr::set_status( diff --git a/kairos-server/src/routes/withdraw.rs b/kairos-server/src/routes/withdraw.rs index 3e16708d..2fdf0968 100644 --- a/kairos-server/src/routes/withdraw.rs +++ b/kairos-server/src/routes/withdraw.rs @@ -2,21 +2,23 @@ use anyhow::anyhow; use axum::{extract::State, http::StatusCode, Json}; use axum_extra::routing::TypedPath; use serde::{Deserialize, Serialize}; +use tracing::*; use crate::{state::LockedBatchState, AppErr, PublicKey}; -#[derive(TypedPath)] -#[typed_path("/withdraw")] +#[derive(Debug, TypedPath)] +#[typed_path("/api/v1/withdraw")] pub struct WithdrawPath; -#[derive(Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Withdrawal { pub public_key: PublicKey, pub signature: String, pub amount: u64, } -pub async fn handler( +#[instrument(level = "trace", skip(state), ret)] +pub async fn withdraw_handler( _: WithdrawPath, State(state): State, Json(withdrawal): Json, diff --git a/kairos-server/src/state.rs b/kairos-server/src/state.rs index d103ab99..12563194 100644 --- a/kairos-server/src/state.rs +++ b/kairos-server/src/state.rs @@ -12,6 +12,7 @@ use crate::{ pub type LockedBatchState = Arc>; +#[derive(Debug)] pub struct BatchState { pub balances: HashMap, pub batch_epoch: u64, diff --git a/kairos-server/tests/transactions.rs b/kairos-server/tests/transactions.rs new file mode 100644 index 00000000..3ae42dae --- /dev/null +++ b/kairos-server/tests/transactions.rs @@ -0,0 +1,140 @@ +use std::sync::OnceLock; + +use axum_extra::routing::TypedPath; +use axum_test::{TestServer, TestServerConfig}; +use kairos_server::{ + routes::{ + deposit::{Deposit, DepositPath}, + transfer::{Transfer, TransferPath}, + withdraw::{WithdrawPath, Withdrawal}, + }, + state::BatchState, +}; +use tracing_subscriber::{prelude::*, EnvFilter}; + +static TEST_ENVIRONMENT: OnceLock<()> = OnceLock::new(); + +fn new_test_app() -> TestServer { + TEST_ENVIRONMENT.get_or_init(|| { + tracing_subscriber::registry() + .with(EnvFilter::try_from_default_env().unwrap_or_else(|_| "trace".into())) + .with(tracing_subscriber::fmt::layer()) + .init(); + }); + let config = TestServerConfig::builder().mock_transport().build(); + + TestServer::new_with_config(kairos_server::app_router(BatchState::new()), config).unwrap() +} + +#[tokio::test] +async fn test_deposit_withdraw() { + let server = new_test_app(); + + let deposit = Deposit { + public_key: "alice_key".into(), + amount: 100, + }; + + // no arguments + server + .post(DepositPath.to_uri().path()) + .await + .assert_status_failure(); + + // deposit + server + .post(DepositPath.to_uri().path()) + .json(&deposit) + .await + .assert_status_success(); + + // wrong arguments + server + .post(WithdrawPath.to_uri().path()) + .json(&deposit) + .await + .assert_status_failure(); + + let withdrawal = Withdrawal { + public_key: "alice_key".into(), + signature: "TODO".into(), + amount: 50, + }; + + // first withdrawal + server + .post(WithdrawPath.to_uri().path()) + .json(&withdrawal) + .await + .assert_status_success(); + + // withdrawal with insufficient funds + server + .post(WithdrawPath.to_uri().path()) + .json(&Withdrawal { + amount: 51, + ..withdrawal.clone() + }) + .await + .assert_status_failure(); + + // second withdrawal + server + .post(WithdrawPath.to_uri().path()) + .json(&Withdrawal { + amount: 50, + ..withdrawal.clone() + }) + .await + .assert_status_success(); + + server + .post(WithdrawPath.to_uri().path()) + .json(&withdrawal) + .await + .assert_status_failure(); +} + +#[tokio::test] +async fn test_deposit_transfer_withdraw() { + let server = new_test_app(); + + let deposit = Deposit { + public_key: "alice_key".into(), + amount: 100, + }; + + let transfer = Transfer { + from: "alice_key".into(), + signature: "TODO".into(), + to: "bob_key".into(), + amount: 50, + }; + + let withdrawal = Withdrawal { + public_key: "bob_key".into(), + signature: "TODO".into(), + amount: 50, + }; + + // deposit + server + .post(DepositPath.to_uri().path()) + .json(&deposit) + .await + .assert_status_success(); + + // transfer + server + .post(TransferPath.to_uri().path()) + .json(&transfer) + .await + .assert_status_success(); + + // withdraw + server + .post(WithdrawPath.to_uri().path()) + .json(&withdrawal) + .await + .assert_status_success(); +}