diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..a6b5f55ce37307cf7ce08edf1fddef0ff766b434 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 + +[*.md] +# double whitespace at end of line +# denotes a line break in Markdown +trim_trailing_whitespace = false + +[*.yml] +indent_size = 2 \ No newline at end of file diff --git a/.env.example b/.env.example deleted file mode 100644 index ea8377ce969f474b8f1b927f8d3bf5f88aa70b18..0000000000000000000000000000000000000000 --- a/.env.example +++ /dev/null @@ -1,6 +0,0 @@ -SIGNING_KEY= -RPC_URL=http://127.0.0.1:8545 - -CHAIN_ID=636363 - -AUTO_SEND_PERIOD=30 \ No newline at end of file diff --git a/.gitignore b/.gitignore index ec47942092de7bc76c4196700eabd10c573bc900..889edcbe58be2b13dab6075145c089d87430bff9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target -.env \ No newline at end of file +.env +.cargo/config.toml +*.db \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 07c1cf500ae889333734e0b95848802b40b018c9..8b3a74cba7aa160a3211586bfb781f1850861513 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,23 @@ -stages: - - build +image: gitlab.limos.fr:5005/ecomobicoin/infra/rust-builder-image:1.71 + +cache: + paths: + - target/ + +variables: + PACKAGE_REGISTRY_URL: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic before_script: - - rustc --version && cargo --version + - | # skip if not using custom Rust image + if hash rustc 2>/dev/null; then + rustc --version + cargo --version + fi + +stages: + - build + - release-build + - release package-docker: image: docker:latest @@ -30,23 +45,47 @@ package-docker: - if: $CI_COMMIT_BRANCH exists: - Dockerfile -# release: -# stage: release -# image: registry.gitlab.com/gitlab-org/release-cli:latest -# variables: -# LINUX_ASSET_LINK: '{"name":"gitlab-downloader-linux-86_64-${CI_COMMIT_TAG}","url":"${PACKAGE_REGISTRY_URL}/${CI_COMMIT_TAG}/linux-x86_64/gitlab-downloader","type":"package"}' -# script: -# - echo "Releasing..." -# - echo "Tag = ${CI_COMMIT_TAG}" -# - echo "URL = ${PACKAGE_REGISTRY_URL}/${CI_COMMIT_TAG}" -# - echo "LINUX = ${LINUX_ASSET_LINK}" -# - echo "WIN = ${WIN_ASSET_LINK}" -# - | -# release-cli --debug create --name "Release ${CI_COMMIT_TAG}" --tag-name "${CI_COMMIT_TAG}" --assets-link "${LINUX_ASSET_LINK}" --assets-link "${WIN_ASSET_LINK}" -# - echo "Done!" -# needs: -# - job: build -# artifacts: true -# rules: -# - if: '$CI_COMMIT_TAG && $CI_COMMIT_TAG =~ /^v[0-9]\.[0-9]\.[0-9](-rc.+)?/' -# when: always + +release-build-windows: + stage: release-build + script: + - cargo build --target x86_64-pc-windows-gnu --release + - | + curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file "target/x86_64-pc-windows-gnu/release/testnet-injector.exe" "${PACKAGE_REGISTRY_URL}/${CI_COMMIT_TAG}/win-x86_64/testnet-injector.exe" + rules: + - if: "$CI_COMMIT_TAG && $CI_COMMIT_TAG =~ /^v/" + when: always + +release-build-linux: + stage: release-build + script: + - cargo build --target x86_64-unknown-linux-gnu --release + - | + curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file "target/x86_64-unknown-linux-gnu/release/testnet-injector" "${PACKAGE_REGISTRY_URL}/${CI_COMMIT_TAG}/linux-x86_64/testnet-injector" + rules: + - if: "$CI_COMMIT_TAG && $CI_COMMIT_TAG =~ /^v/" + when: always + +release: + stage: release + image: registry.gitlab.com/gitlab-org/release-cli:latest + variables: + LINUX_ASSET_LINK: '{"name":"testnet-injector-linux-86_64-${CI_COMMIT_TAG}","url":"${PACKAGE_REGISTRY_URL}/${CI_COMMIT_TAG}/linux-x86_64/testnet-injector","type":"package"}' + WIN_ASSET_LINK: '{"name":"testnet-injector-win-86_64-${CI_COMMIT_TAG}","url":"${PACKAGE_REGISTRY_URL}/${CI_COMMIT_TAG}/win-x86_64/testnet-injector.exe","type":"package"}' + script: + - echo "Releasing..." + - echo "Tag = ${CI_COMMIT_TAG}" + - echo "URL = ${PACKAGE_REGISTRY_URL}/${CI_COMMIT_TAG}" + - echo "LINUX = ${LINUX_ASSET_LINK}" + - echo "WIN = ${WIN_ASSET_LINK}" + - | + release-cli --debug create --name "Release ${CI_COMMIT_TAG}" --tag-name "${CI_COMMIT_TAG}" --assets-link "${LINUX_ASSET_LINK}" --assets-link "${WIN_ASSET_LINK}" + - echo "Done!" + needs: + - job: release-build-windows + artifacts: true + - job: release-build-linux + artifacts: true + rules: + - if: "$CI_COMMIT_TAG && $CI_COMMIT_TAG =~ /^v/" + when: always diff --git a/Cargo.lock b/Cargo.lock index 603e5b666e5885b8bd65567d2e0d6fb1acadaca7..958a6473a547ac6d0b9077bc9ed3f88f97584919 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,187 +12,6 @@ dependencies = [ "regex", ] -[[package]] -name = "actix-codec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" -dependencies = [ - "bitflags 1.3.2", - "bytes", - "futures-core", - "futures-sink", - "memchr", - "pin-project-lite", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "actix-http" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "ahash 0.8.3", - "base64 0.21.2", - "bitflags 1.3.2", - "brotli", - "bytes", - "bytestring", - "derive_more", - "encoding_rs", - "flate2", - "futures-core", - "h2", - "http", - "httparse", - "httpdate", - "itoa", - "language-tags", - "local-channel", - "mime", - "percent-encoding", - "pin-project-lite", - "rand", - "sha1", - "smallvec", - "tokio", - "tokio-util", - "tracing", - "zstd 0.12.3+zstd.1.5.2", -] - -[[package]] -name = "actix-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "actix-router" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" -dependencies = [ - "bytestring", - "http", - "regex", - "serde", - "tracing", -] - -[[package]] -name = "actix-rt" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" -dependencies = [ - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "futures-util", - "mio", - "num_cpus", - "socket2", - "tokio", - "tracing", -] - -[[package]] -name = "actix-service" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" -dependencies = [ - "futures-core", - "paste", - "pin-project-lite", -] - -[[package]] -name = "actix-utils" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" -dependencies = [ - "local-waker", - "pin-project-lite", -] - -[[package]] -name = "actix-web" -version = "4.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-utils", - "actix-web-codegen", - "ahash 0.7.6", - "bytes", - "bytestring", - "cfg-if", - "cookie", - "derive_more", - "encoding_rs", - "futures-core", - "futures-util", - "http", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "pin-project-lite", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2", - "time 0.3.22", - "url", -] - -[[package]] -name = "actix-web-codegen" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2262160a7ae29e3415554a3f1fc04c764b1540c116aa524683208078b7a75bc9" -dependencies = [ - "actix-router", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "addr2line" version = "0.20.0" @@ -219,29 +38,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "aho-corasick" version = "0.7.20" @@ -260,21 +56,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -292,16 +73,15 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] @@ -331,9 +111,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys", @@ -341,9 +121,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arrayref" @@ -388,7 +168,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -399,7 +179,7 @@ checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -615,27 +395,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "brotli" -version = "3.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - [[package]] name = "bs58" version = "0.4.0" @@ -682,15 +441,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bytestring" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" -dependencies = [ - "bytes", -] - [[package]] name = "bzip2" version = "0.4.4" @@ -767,11 +517,8 @@ checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ "android-tzdata", "iana-time-zone", - "js-sys", "num-traits", "serde", - "time 0.1.45", - "wasm-bindgen", "winapi", ] @@ -787,20 +534,19 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.10" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a" +checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.10" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d" +checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstream", "anstyle", @@ -810,21 +556,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "coins-bip32" @@ -900,23 +646,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "cookie" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" -dependencies = [ - "percent-encoding", - "time 0.3.22", - "version_check", -] - [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -1012,6 +741,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "ctr" version = "0.9.2" @@ -1042,7 +792,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -1053,7 +803,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ "darling_core", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -1072,16 +822,24 @@ dependencies = [ "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "derive_more" version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case", "proc-macro2", "quote", - "rustc_version", "syn 1.0.109", ] @@ -1133,12 +891,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - [[package]] name = "dunce" version = "1.0.4" @@ -1162,7 +914,7 @@ dependencies = [ [[package]] name = "ecomobicoin-jsonrpc" version = "0.1.0" -source = "git+https://gitlab.limos.fr/ecomobicoin/ecomobicoin-jsonrpc.git#20df825e93327e0cf691918d64321960b0b4d92f" +source = "git+https://gitlab.limos.fr/ecomobicoin/ecomobicoin-jsonrpc.git#1a7e75657d759587cff39a96bdbd6aa944d98cfb" dependencies = [ "arrayvec", "bytes", @@ -1412,7 +1164,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "syn 2.0.23", + "syn 2.0.32", "toml", "walkdir", ] @@ -1429,7 +1181,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -1454,7 +1206,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.23", + "syn 2.0.32", "tempfile", "thiserror", "tiny-keccak", @@ -1709,9 +1461,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -1724,9 +1476,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -1734,15 +1486,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -1751,9 +1503,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-locks" @@ -1767,26 +1519,26 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-timer" @@ -1800,9 +1552,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -1845,7 +1597,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -2378,12 +2130,6 @@ version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - [[package]] name = "lazy_static" version = "1.4.0" @@ -2408,24 +2154,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" -[[package]] -name = "local-channel" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c" -dependencies = [ - "futures-core", - "futures-sink", - "futures-util", - "local-waker", -] - -[[package]] -name = "local-waker" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" - [[package]] name = "lock_api" version = "0.4.10" @@ -2494,8 +2222,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys", ] @@ -2558,7 +2285,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -2673,12 +2400,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "paste" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" - [[package]] name = "path-slash" version = "0.2.1" @@ -2763,7 +2484,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -2801,7 +2522,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -2832,6 +2553,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[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" @@ -2861,7 +2588,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9825a04601d60621feed79c4e6b56d65db77cdca55cef43b46b0de1096d1c282" dependencies = [ "proc-macro2", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -3408,9 +3135,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.166" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] @@ -3426,13 +3153,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.166" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -3480,7 +3207,7 @@ dependencies = [ "serde", "serde_json", "serde_with_macros", - "time 0.3.22", + "time", ] [[package]] @@ -3492,7 +3219,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -3562,15 +3289,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - [[package]] name = "signature" version = "2.1.0" @@ -3702,7 +3420,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -3744,9 +3462,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.23" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", @@ -3794,10 +3512,9 @@ dependencies = [ name = "testnet-injector" version = "0.1.0" dependencies = [ - "actix-web", - "chrono", + "anyhow", "clap", - "dotenvy", + "csv", "ecomobicoin-jsonrpc", "ethereum-interfaces", "ethereum-types", @@ -3805,13 +3522,14 @@ dependencies = [ "ethnum", "eyre", "fastrlp", + "futures", "primitive-types", "serde", "serde-big-array", "serde_json", "serde_with", + "time", "tokio", - "tokio_schedule", "tracing", "tracing-subscriber", ] @@ -3833,7 +3551,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -3848,22 +3566,13 @@ dependencies = [ [[package]] name = "time" -version = "0.1.45" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ + "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -3871,15 +3580,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] @@ -3920,9 +3629,7 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys", @@ -3946,7 +3653,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -4000,16 +3707,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tokio_schedule" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b4a14ab1658c39d137ebcc5fbaab364c9922a6cc04ab48b364546c2e6022256" -dependencies = [ - "chrono", - "tokio", -] - [[package]] name = "toml" version = "0.7.5" @@ -4142,7 +3839,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -4332,12 +4029,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -4365,7 +4056,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", "wasm-bindgen-shared", ] @@ -4399,7 +4090,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4639,8 +4330,8 @@ dependencies = [ "hmac", "pbkdf2 0.11.0", "sha1", - "time 0.3.22", - "zstd 0.11.2+zstd.1.5.2", + "time", + "zstd", ] [[package]] @@ -4649,16 +4340,7 @@ version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ - "zstd-safe 5.0.2+zstd.1.5.2", -] - -[[package]] -name = "zstd" -version = "0.12.3+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" -dependencies = [ - "zstd-safe 6.0.5+zstd.1.5.4", + "zstd-safe", ] [[package]] @@ -4671,16 +4353,6 @@ dependencies = [ "zstd-sys", ] -[[package]] -name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" -dependencies = [ - "libc", - "zstd-sys", -] - [[package]] name = "zstd-sys" version = "2.0.8+zstd.1.5.5" diff --git a/Cargo.toml b/Cargo.toml index e691cdcbbc24d695bd86591dc4941171daf19e85..b0f8a7068a7377bdd0fbfd28545c8ecde56badac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ license = "AGPL-3.0-only" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -actix-web = "4" ethereum-interfaces = { git = "https://github.com/ledgerwatch/interfaces", features = [ "remotekv", "sentry", @@ -34,13 +33,12 @@ primitive-types = { version = "0.12", default-features = false, features = [ "rlp", "rustc-hex", ] } -serde = "1" +serde = { version = "1", features = ["derive"] } serde-big-array = "0.5.1" serde_json = "1" serde_with = "2" tracing = "0.1" tracing-subscriber = "0.3" -dotenvy = "0.15" ethers = { version = "2.0", git = "https://gitlab.limos.fr/ecomobicoin/ethers-rs.git", features = [ "ecomobicoin", ] } @@ -52,6 +50,8 @@ ethers = { version = "2.0", git = "https://gitlab.limos.fr/ecomobicoin/ethers-rs tokio = { version = "1", features = ["macros"] } # Flexible concrete Error Reporting type built on std::error::Error with customizable Reports eyre = "0.6" -tokio_schedule = "0.3" -chrono = "0.4" +time = { version = "0.3", features = ["serde", "serde-well-known"] } clap = { version = "4", features = ["derive"] } +futures = "0.3.29" +csv = "1.3.0" +anyhow = "1.0.75" diff --git a/README.md b/README.md index 583524046b534c4a6738adebc708aa271d893509..4e1cc62ef7a11cdd4d0e773ac68152481619e1f9 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,101 @@ # Testnet injector -A simple HTTP API to inject transactions or behaviors in an EcoMobiCoin test network. +A CLI tool to inject transactions or behaviors in an EcoMobiCoin blockchain. -## Features +## Components This project heavily use the forked and adapted [ethers-rs](https://gitlab.limos.fr/ecomobicoin/ethers-rs). +### ETL + +- Extract CSV file e.g. `dataset-extract.csv` to `MobilityRecord` +- Transform it with simulation params (relative time from earliest entry and behavior attributes) +- Load data in memory with thread scheduled + +### Sending Queue + +## Usage + +### Simulate tx & bx + +To simulate Behaviors from a CSV Mobility dataset, run `cargo run simulate-bx-from-csv < data/dataset-extract.csv` + +Options: + +- dataset e.g. CSV file +- `total_simulation_duration` in seconds (or minutes ?) + +### Send + +Send tx or bx + +Options: + +- `txs` Number of transactions to send +- `bxs` Number of behaviors to send + +In a near future: + +- `time_period` in seconds + +### Mine + +To mine a block from env vars `MINER_SIGNING_KEY` and `RPC_URL`, use `cargo run mine` + +Options: + +- None yet + +In a near futur: + +- quantity +- timestamp +- input + ## Env vars -Copy the env example file to `.env` with the following command: `cp .env.example .env` +You can export env vars in the cli like `export RPC_URL="http://ecomobicoin-2.cri.local.isima.fr:10002"; testnet-injector mine` + +Or you can use your user's cargo config file in (see https://doc.rust-lang.org/cargo/reference/config.html) or the project one `cargo/config.toml`. +And set the following content to set env vars: + +``` +[env] +MINER_SIGNING_KEY = "" +MNEMONIC = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + +RPC_URL=http://127.0.0.1:8545 +CHAIN_ID = "636363" +DATABASE_URL = "./injector.db" +``` + +You can also specify `--rpc-url` and `--chain-id` as args in command line that will overwrite env vars. + +## Ecomobicoin Simulation + +> WORK IN PROGRESS + +```mermaid +flowchart LR + subgraph Configuration + A[CSV Dataset] --> B[Simulation config] + AA[Nodes & Miner config] --> B + AAA[Chainspec] --> B + end + subgraph S[Simulation] + subgraph D[Deployments] + B --> D1[Deploy nodes] + B --> D21[Deploy txbx CSV injector] + B --> D22[Deploy txbx injectors cli] + B --> D23[Deploy miners cli] + B --> D3[Deploy block explorer] + end + D --> E[Run] + end + subgraph R[Reporting] + E --> R1[Report] + end +``` ## Development diff --git a/data/dataset-extract.csv b/data/dataset-extract.csv new file mode 100644 index 0000000000000000000000000000000000000000..a74a11e9922efa679c4aa1967c2a153ea50b4235 --- /dev/null +++ b/data/dataset-extract.csv @@ -0,0 +1,7 @@ +id,user_id,network,service,label,created_at,from_coord,from_label,from_datetime,to_coord,to_label,to_datetime,from_latitude,from_longitude,to_latitude,to_longitude +0,79,mulhouse,PUBLIC_TRANSPORT,"Abonnement mensuel 26-64 ans, 1 passager(s)",2023-05-31T22:09:51.226773Z,,,2023-06-01T00:09:50.000Z,,,,,,, +1,901,mulhouse,PUBLIC_TRANSPORT,"Abonnement mensuel - de 26 ans, 1 passager(s)",2023-05-31T22:12:24.054056Z,,,2023-06-01T00:12:23.000Z,,,,,,, +2,4037,mulhouse,PUBLIC_TRANSPORT,"Abonnement mensuel - de 26 ans, 1 passager(s)",2023-05-31T22:22:14.465196Z,,,2023-06-01T00:22:13.000Z,,,,,,, +3,4638,mulhouse,PUBLIC_TRANSPORT,"Abonnement mensuel - de 26 ans, 1 passager(s)",2023-06-01T00:21:37.066224Z,,,2023-06-01T02:21:36.000Z,,,,,,, +12,4272,mulhouse,PUBLIC_TRANSPORT,"Abonnement mensuel - de 26 ans, 1 passager(s)",2023-06-01T04:03:48.286701Z,,,2023-06-01T06:03:47.000Z,,,,,,, +10653,79,mulhouse,PUBLIC_TRANSPORT,"Abonnement mensuel 26-64 ans, 1 passager(s)",2023-07-02T19:02:04.987621Z,,,2023-07-02T21:02:04.000Z,,,,,,, \ No newline at end of file diff --git a/scripts/t0-1mine-2sync.sh b/scripts/t0-1mine-2sync.sh new file mode 100755 index 0000000000000000000000000000000000000000..f8c79834679038fac051c2a38b1f8517f47ef1cc --- /dev/null +++ b/scripts/t0-1mine-2sync.sh @@ -0,0 +1,31 @@ +#!/bin/bash +export MINER_SIGNING_KEY=4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318; + +# MINER_SIGNING_KEY & MNEMONIC env vars are set on workstation. +export CHAIN_ID=6363; +# 12 sec +BLOCK_TIME=12; + +export RPC_URL=http://ecomobicoin-1.cri.local.isima.fr:8545; + +# Mine on eco1 with `--max-block 0` +# +# rm -rf /home/angraign/.local/share/truite/chaindata/ +# ./truite --chain-spec-file clermont-bootnode.ron --no-dns-discovery --cidr 192.168.0.0/16 --rpc-listen-address 0.0.0.0:8545 --max-block 0 +# export RPC_URL=http://ecomobicoin-1.cri.local.isima.fr:8545; export CHAIN_ID=6363; ./target/release/testnet-injector mine +# ./truite --chain-spec-file clermont-bootnode.ron --no-dns-discovery --cidr 192.168.0.0/16 --rpc-listen-address 0.0.0.0:8545 +# + +# Mine every 10 secs or number of blocks +for ((i=1; i<=100; i++)) +do + echo "MINE $i/100"; + ./target/release/testnet-injector mine + if [ $i -ne 100 ]; then + sleep $BLOCK_TIME + fi +done + +# Check if eco2 sync properly + + diff --git a/scripts/t1-1mine-with-simulation-2sync.sh b/scripts/t1-1mine-with-simulation-2sync.sh new file mode 100755 index 0000000000000000000000000000000000000000..d536649ee610a7b211431a6d8af6ff47c1c10700 --- /dev/null +++ b/scripts/t1-1mine-with-simulation-2sync.sh @@ -0,0 +1,35 @@ +#!/bin/bash +export MINER_SIGNING_KEY=4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318; +export MNEMONIC='before exclude interest push zone jealous boat ice chimney juice young side diagram silent pipe'; + +# MINER_SIGNING_KEY & MNEMONIC env vars are set on workstation. +export CHAIN_ID=6363; +# 30 mins +TOTAL_SIMULATION_TIME=1800; +# 12 sec +BLOCK_TIME=12; + +export RPC_URL=http://ecomobicoin-1.cri.local.isima.fr:8545; + +# Mine on eco1 with `--max-block 0` +# +# rm -rf /home/angraign/.local/share/truite/chaindata/ +# ./truite --chain-spec-file clermont-bootnode.ron --no-dns-discovery --cidr 192.168.0.0/16 --rpc-listen-address 0.0.0.0:8545 --max-block 0 +# export RPC_URL=http://ecomobicoin-1.cri.local.isima.fr:8545; export CHAIN_ID=6363; ./target/release/testnet-injector mine +# ./truite --chain-spec-file clermont-bootnode.ron --no-dns-discovery --cidr 192.168.0.0/16 --rpc-listen-address 0.0.0.0:8545 +# + +# ./target/release/testnet-injector simulate-bx-from-csv ../movement-data/public-transport/MF_user_mobility_june-to-august-2023_mulhouse_dataset/anonymized_june-to-august-2023_mulhouse_dataset.csv $TOTAL_SIMULATION_TIME & +./target/release/testnet-injector simulate-bx-from-csv ./data/dataset-extract.csv $TOTAL_SIMULATION_TIME & + +# Mine every 10 secs or number of blocks +for ((i=1; i<=100; i++)) +do + echo "MINE $i/100"; + ./target/release/testnet-injector mine + if [ $i -ne 100 ]; then + sleep $BLOCK_TIME + fi +done + + diff --git a/scripts/t2.sh b/scripts/t2.sh new file mode 100755 index 0000000000000000000000000000000000000000..e62ef7ab487f4251083a4b521f23cbe2b452819d --- /dev/null +++ b/scripts/t2.sh @@ -0,0 +1,34 @@ +#!/bin/bash +export MINER_SIGNING_KEY=4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318; +export MNEMONIC='before exclude interest push zone jealous boat ice chimney juice young side diagram silent pipe'; + +# MINER_SIGNING_KEY & MNEMONIC env vars are set on workstation. +export CHAIN_ID=6363; +# 30 mins +TOTAL_SIMULATION_TIME=180; +# 12 sec +BLOCK_TIME=12; + +# export RPC_URL=http://ecomobicoin-2.cri.local.isima.fr:10002; +export RPC_URL=http://ecomobicoin-2.cri.local.isima.fr:8545; +export RPC_URL_MINER=http://ecomobicoin-2.cri.local.isima.fr:8545; +# export RPC_URL=http://127.0.0.1:8545; +# export RPC_URL_MINER=http://127.0.0.1:8545; + +# Mine on eco1 with `--max-block 0` +# export RPC_URL=http://ecomobicoin-1.cri.local.isima.fr:8545; export CHAIN_ID=6363; ./target/release/testnet-injector mine + +# ./target/release/testnet-injector simulate-bx-from-csv ../movement-data/public-transport/MF_user_mobility_june-to-august-2023_mulhouse_dataset/anonymized_june-to-august-2023_mulhouse_dataset.csv $TOTAL_SIMULATION_TIME & +./target/release/testnet-injector simulate-bx-from-csv ./data/dataset-extract.csv $TOTAL_SIMULATION_TIME & + +# Mine every 10 secs or number of blocks +for ((i=1; i<=100; i++)) +do + echo "MINE $i/100"; + export RPC_URL=$RPC_URL_MINER; ./target/release/testnet-injector mine + if [ $i -ne 100 ]; then + sleep $BLOCK_TIME + fi +done + + diff --git a/src/helpers.rs b/src/helpers.rs deleted file mode 100644 index e863b11c515d00af0a0bad6c5378f96047ebe2b9..0000000000000000000000000000000000000000 --- a/src/helpers.rs +++ /dev/null @@ -1,88 +0,0 @@ -pub mod txbx_helper { - use ecomobicoin_jsonrpc::types::{self, Bytes}; - use ethereum_types::{Address, U64}; - use ethnum::U256; - use std::time::SystemTime; - - pub fn build_default_tx() -> ecomobicoin_jsonrpc::types::TransactionMessage { - build_tx(None, None, None, None, None, None, None) - } - - pub fn build_tx( - chain_id: Option<U64>, - nonce: Option<U64>, - to: Option<Address>, - gas: Option<U64>, - gas_price: Option<U256>, - value: Option<U256>, - input: Option<Bytes>, - ) -> ecomobicoin_jsonrpc::types::TransactionMessage { - let chain_id = chain_id.unwrap_or_else(|| 636363.into()); - let nonce = nonce.unwrap_or_else(|| 1.into()); - // let to = to.unwrap_or_default(); - let gas = gas.unwrap_or_default(); - let gas_price = gas_price.unwrap_or_default(); - let value = value.unwrap_or_default(); - let input = input.unwrap_or_default(); - - types::TransactionMessage::EIP2930 { - chain_id, - nonce, - to, - gas, - gas_price, - value, - input, - access_list: [].to_vec(), - } - } - - pub fn build_default_bx() -> ecomobicoin_jsonrpc::types::BehaviorMessage { - build_bx(None, None, None, None, None) - } - - pub fn build_bx( - chain_id: Option<U64>, - to: Option<Address>, - timestamp: Option<U64>, - quantity: Option<U64>, - input: Option<Bytes>, - ) -> types::BehaviorMessage { - let chain_id = chain_id.unwrap_or_else(|| 636363.into()); - let now = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(); - let timestamp = timestamp.unwrap_or(now.into()); - let quantity = quantity.unwrap_or(10_000.into()); - let input = input.unwrap_or_default(); - - types::BehaviorMessage { - chain_id, - to, - timestamp, - quantity, - input, - } - } -} - -#[cfg(test)] -mod tests { - use std::default; - - use super::*; - use crate::res::chainspec::MAINNET; - - #[test] - fn build_default_tx_test() { - // ARRANGE - // ACT - let default_tx = build_default_tx(); - // ASSERT - // default_tx. - } - - #[test] - fn build_default_bx_test() {} -} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..b5614dd82335c52db29069c990b16b6c71b5a8b8 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod utils; diff --git a/src/main.rs b/src/main.rs index dd8fc3229e1153df0da4efa72f77b653a3ef6e40..cff2cb7d83d5fa070958c9471311b281069cdff1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,108 +1,71 @@ -use crate::helpers::txbx_helper::*; -use actix_web::{ - get, post, - web::{self, Data}, - App, HttpRequest, HttpResponse, HttpServer, Responder, +use ecomobicoin_jsonrpc::types::{Behavior, Block}; +use ethereum_types::U64; +use ethers::{prelude::*, signers::coins_bip39::English}; +use futures::executor::block_on; +use testnet_injector::utils::miner::get_miner_behavior; +use std::{process, time::Duration}; +use std::time::{SystemTime, UNIX_EPOCH}; +use testnet_injector::{ + utils::{mobility_data::*, txbx}, }; -use dotenvy::dotenv; -use ethers::{ - prelude::*, - types::transaction::{ - ecomobicoin_behavior::EcoMobiCoinBehaviorTransactionRequest, eip2718::TypedTransaction, - }, - utils::keccak256, -}; -use serde::Deserialize; -use std::{ - str::FromStr, - thread, - time::{Duration, SystemTime}, -}; -use tokio::spawn; -use tokio_schedule::{every, Job}; -use tracing::{debug, info, Level}; - -mod helpers; - -struct AppState { - // Mutex for thread safety ? https://actix.rs/docs/application#shared-mutable-state - signer: SignerMiddleware<Provider<Http>, LocalWallet>, - chain_id: u64, - rpc_url: String, -} - -#[get("/health")] -async fn healthcheck() -> impl Responder { - HttpResponse::Ok() -} - -#[get("/")] -async fn info(req: HttpRequest) -> impl Responder { - let data = req.app_data::<Data<AppState>>().unwrap(); - HttpResponse::Ok().body(format!( - "Chain ID: {:?}, Signer's Address: {:?}, RPC: {:?}", - data.chain_id, - data.signer.address(), - data.rpc_url - )) +use tracing::{debug, error, info, Level}; + +use ::clap::{Args, Parser, Subcommand}; + +#[derive(Parser)] +#[command(author, version)] +#[command( + about = "testnet-injector - a simple CLI to interact with a EcoMobiCoin testnet", + long_about = "The testnet-injector can perform all kinds of operations to enable the block construction of EcoMobiCoin blockchain with transactions (tx) and behaviors (bx)." +)] +struct Cli { + #[command(subcommand)] + command: Option<Commands>, } -#[derive(Deserialize)] -struct BlockFeed { - txs: usize, - bxs: usize, +#[derive(Subcommand)] +enum Commands { + /// Send txs and/or bxs using default content + Send(SendParams), + /// Simulate behaviors from dataset in csv and send them to node + SimulateBxFromCsv(SimulateBxFromCsv), + /// Mine a block + Mine(MineParams), } -#[get("/blocks/feed")] -async fn feed_block(req: HttpRequest, block_feed: web::Query<BlockFeed>) -> impl Responder { - let data = req.app_data::<Data<AppState>>().unwrap(); - - for _ in 0..block_feed.txs { - send_default_tx(data.signer.clone(), data.chain_id).await; - } - - for _ in 0..block_feed.bxs { - send_default_bx(data.signer.clone(), data.chain_id).await; - } - - HttpResponse::Ok().body(format!( - "Fed block with {} txs, {} bxs ...", - block_feed.txs, block_feed.bxs, - )) +#[derive(Args)] +struct SendParams { + /// Number of transactions to send + txs: u64, + /// Number of behaviors to send + bxs: u64, + // Node RPC URL + rpc_url: Option<String>, + // Chain ID + chain_id: Option<u64> } -#[get("/bxs/send")] -async fn send_bx(req: HttpRequest) -> impl Responder { - let data = req.app_data::<Data<AppState>>().unwrap(); - - let bx_hash = send_default_bx(data.signer.clone(), data.chain_id).await; - - HttpResponse::Ok() - // .content_type("application/json") - .body(format!("BxHash: {:?}", bx_hash)) +#[derive(Args)] +struct SimulateBxFromCsv { + /// Path to CSV file + file: String, + /// Total duration of simulation in seconds + total_simulation_duration: u64, + // Node RPC URL + rpc_url: Option<String>, + // Chain ID + chain_id: Option<u64> } -#[get("/bxs/encode")] -async fn encode_bx() -> impl Responder { - HttpResponse::Ok().body("bx") -} -#[get("/txs/send")] -async fn send_tx(req: HttpRequest) -> impl Responder { - let data = req.app_data::<Data<AppState>>().unwrap(); - - let tx_hash = send_default_tx(data.signer.clone(), data.chain_id).await; - HttpResponse::Ok() - // .content_type("application/json") - .body(format!("TxHash: {:?}", tx_hash)) -} -#[get("/txs/encode")] -async fn encode_tx() -> impl Responder { - HttpResponse::Ok().body("tx encode") +#[derive(Args)] +struct MineParams { + // Node RPC URL + rpc_url: Option<String>, + // Chain ID + chain_id: Option<u64> } -#[actix_web::main] -async fn main() -> std::io::Result<()> { - let wait = false; +async fn init() -> (u64, String) { // construct a subscriber that prints formatted traces to stdout let subscriber = tracing_subscriber::FmtSubscriber::builder() .with_max_level(Level::INFO) @@ -111,7 +74,7 @@ async fn main() -> std::io::Result<()> { tracing::subscriber::set_global_default(subscriber) .expect("setting default tracing subscriber failed"); - dotenv().expect(".env file not found"); + // dotenv().expect(".env file not found"); // for (key, value) in std::env::vars() { // println!("{key}: {value}"); // } @@ -119,9 +82,6 @@ async fn main() -> std::io::Result<()> { std::env::var("RPC_URL").unwrap_or_else(|_| "http://127.0.0.1:8545".into()); let provider = Provider::<Http>::try_from(rpc_url.clone()).unwrap(); - let signing_key = std::env::var("SIGNING_KEY").unwrap(); - let signer: LocalWallet = signing_key.parse::<LocalWallet>().unwrap(); - let chain_id: u64 = std::env::var("CHAIN_ID") .unwrap_or_else(|_| String::from("63")) .parse::<u64>() @@ -133,220 +93,192 @@ async fn main() -> std::io::Result<()> { rpc_url ) }); - // connect the wallet to the provider - let client = SignerMiddleware::new(provider, signer.clone().with_chain_id(chain_id)); - let addr = client.clone().address(); - let nonce_manager = client.clone().nonce_manager(addr); - let curr_nonce = nonce_manager - .get_transaction_count(signer.address(), Some(BlockNumber::Pending.into())) - .await - .unwrap() - .as_u64(); - - info!( - "Signer: 0x{:02x}, nonce: {:?}", - signer.address(), - curr_nonce - ); - // nonce_manager.clone(); - - // let shared_nonce_manager = Arc::new(Mutex::new(nonce_manager)); info!( "Connected to chain id: {:?}, rpc: {:?}, latest block number: {:?}.", chain_id, rpc_url, block_number ); - let auto_send_period: u64 = std::env::var("AUTO_SEND_PERIOD") - .unwrap_or_else(|_| "0".into()) - .parse::<u64>() - .unwrap(); + (chain_id, rpc_url) +} - if auto_send_period > 0 { - info!( - "Auto sending bx and tx every {:?} seconds...", - auto_send_period - ); - - loop { - // TX - let tr = TransactionRequest::new() - .to(Address::zero()) - .value(U256::from(210000)) - .chain_id(chain_id); - let pending_tx = nonce_manager.send_transaction(tr, None).await.unwrap(); - info!("Pending tx: {:?}", pending_tx.tx_hash()); - - // let tr = TransactionRequest::new() - // .to(Address::zero()) - // .value(U256::from(210000)) - // .chain_id(chain_id); - // let pending_tx = nonce_manager.send_transaction(tr, None).await.unwrap(); - // info!("Pending tx2: {:?}\n", pending_tx.tx_hash()); - - // BX - let tr = TransactionRequest::new() - // .to(signer.address()) - .value(U256::from(210000)) - .chain_id(chain_id) - .data(Bytes::from_str("0x015d8eb90000000000000000000000000000000000000000000000000000000000878c1c00000000000000000000000000000000000000000000000000000000644662bc0000000000000000000000000000000000000000000000000000001ee24fba17b7e19cc10812911dfa8a438e0a81a9933f843aa5b528899b8d9e221b649ae0df00000000000000000000000000000000000000000000000000000000000000060000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240").unwrap()); - - let now = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) +#[tokio::main] +async fn main() -> Result<(), anyhow::Error> { + let cli = Cli::parse(); + + let (mut chain_id, mut rpc_url) = init().await; + + match &cli.command { + Some(Commands::Send(params)) => { + info!("Sending {:?} tx and {:?} bx ...", params.txs, params.bxs); + // Override env vars if specified in CLI + if params.rpc_url.clone().is_some(){ + rpc_url = params.rpc_url.clone().unwrap(); + } + if params.chain_id.clone().is_some(){ + chain_id = params.chain_id.clone().unwrap(); + } + + let max = std::cmp::max(params.txs, params.bxs); + + for i in 0..max { + let mnemonic = std::env::var("MNEMONIC").unwrap_or("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string()); + let wallet = MnemonicBuilder::<English>::default() + .phrase(PathOrString::String(mnemonic.clone())) + .index(i as u32) + .unwrap() + .build() .unwrap() - .as_secs(); - let bx = EcoMobiCoinBehaviorTransactionRequest::new(tr, Some(U64::from(now))); - // Sending with client to keep nonce update only for tx - let pending_bx = client.send_transaction(bx, None).await.unwrap(); - info!("Pending bx: {:?}", pending_bx.tx_hash()); - - info!("Sleeping for {:?} secs...", auto_send_period); - thread::sleep(Duration::from_millis(auto_send_period * 1000)); + .with_chain_id(chain_id); + let provider = Provider::<Http>::try_from(rpc_url.clone()).unwrap(); + let client = SignerMiddleware::new(provider.clone(), wallet.clone()); + + + if i < params.bxs { + let bx_hash = txbx::send_default_bx( + client.clone(), + chain_id, + ).await; + info!("Sent default bx {:?}", bx_hash); + } + if i < params.txs { + let tx_hash = txbx::send_default_tx( + client.clone(), + chain_id, + ).await; + info!("Sent default tx {:?}", tx_hash); + } + } } - // let every_x_seconds = every(auto_send_period) - // .seconds() // by default chrono::Local timezone - // .perform(|| async { - // let chain_id2: u64 = std::env::var("CHAIN_ID") - // .unwrap_or_else(|_| String::from("63")) - // .parse::<u64>() - // .unwrap(); - // let rpc_url2: String = - // std::env::var("RPC_URL").unwrap_or_else(|_| "http://127.0.0.1:8545".into()); - // let provider2 = Provider::<Http>::try_from(rpc_url2).unwrap(); - // let signing_key2 = std::env::var("SIGNING_KEY").unwrap(); - // let signer2: LocalWallet = signing_key2.parse::<LocalWallet>().unwrap(); - // let client2 = SignerMiddleware::new(provider2, signer2.with_chain_id(chain_id2)); - - // // send_default_tx(client2.clone(), chain_id2).await; - // // send_default_bx(client2, chain_id2).await; - // let addr = client2.clone().address(); - - // let nonce_manager = client2.clone().nonce_manager(addr); - - // // TX - // let tr = TransactionRequest::new() - // .to(Address::zero()) - // .value(U256::from(210000)) - // .chain_id(chain_id2); - - // let pending_tx = - // nonce_manager.send_transaction(tr, None).await.unwrap(); - // info!("Pending tx: {:?}\n", pending_tx.tx_hash()); - - // let tr = TransactionRequest::new() - // .to(Address::zero()) - // .value(U256::from(210000)) - // .chain_id(chain_id2); - // let pending_tx = - // nonce_manager.send_transaction(tr, None).await.unwrap(); - // info!("Pending tx2: {:?}\n", pending_tx.tx_hash()); - - // // BX - // let tr = TransactionRequest::new() - // // .to(signer.address()) - // .value(U256::from(210000)) - // .chain_id(chain_id2) - // .data(Bytes::from_str("0x015d8eb90000000000000000000000000000000000000000000000000000000000878c1c00000000000000000000000000000000000000000000000000000000644662bc0000000000000000000000000000000000000000000000000000001ee24fba17b7e19cc10812911dfa8a438e0a81a9933f843aa5b528899b8d9e221b649ae0df00000000000000000000000000000000000000000000000000000000000000060000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240").unwrap()); - - // let now = SystemTime::now() - // .duration_since(SystemTime::UNIX_EPOCH) - // .unwrap() - // .as_secs(); - // let bx = EcoMobiCoinBehaviorTransactionRequest::new(tr, Some(U64::from(now))); - // let pending_bx = - // nonce_manager.send_transaction(bx, None).await.unwrap(); - // info!("Pending bx: {:?}\n", pending_bx.tx_hash()); - // }); - // spawn(every_x_seconds); + Some(Commands::SimulateBxFromCsv(params)) => { + info!("Simulating file {:?} for a total time of {:?} secs",params.file.clone(), params.total_simulation_duration.clone()); + // Override env vars if specified in CLI + if params.rpc_url.clone().is_some(){ + rpc_url = params.rpc_url.clone().unwrap(); + } + if params.chain_id.clone().is_some(){ + chain_id = params.chain_id.clone().unwrap(); + } + // println!("Processing {:?}", params.file); + // csv::Reader::from_path(path) + let records = read_csv(csv::Reader::from_path(params.file.clone())?); + if let Err(err) = records { + error!("error running example: {}", err); + process::exit(1); + } + let records = records.unwrap(); + + let mobility_records = generate_behaviors_from_records(records.clone()); + + let dataset_max_relative_time = mobility_records + .iter() + .max_by(|x, y| x.relative_start_time.cmp(&y.relative_start_time)) + .unwrap() + .relative_start_time; + + let total_simulation_duration = if params.total_simulation_duration > 0 { + params.total_simulation_duration + } else { + dataset_max_relative_time as u64 + }; + + info!( + "Simulation of {:?} records for {:?}secs (max time {:?})", + mobility_records.len(), + total_simulation_duration, + Duration::from_millis(dataset_max_relative_time as u64) + ); + + let mobility_records_times: Vec<Duration> = mobility_records.iter().map(|r| compute_stretched_relative_start_time( + r.relative_start_time as u64, + total_simulation_duration, + dataset_max_relative_time as u64, + )).collect(); + + // ---------------- + // Sending queue + // ---------------- + let mut handles = Vec::with_capacity(mobility_records.len()); + + for i in 0..mobility_records.len() { + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); + let simulation_behavior = mobility_records[i].clone(); + let relative_start_duration = mobility_records_times[i].clone(); + let rpc_url = rpc_url.clone(); + let chain_id = chain_id.clone(); + let mut time_since_beginning = now; + + handles.push(tokio::spawn({ + async move { + tokio::time::sleep(relative_start_duration).await; + time_since_beginning += relative_start_duration.as_secs(); + + let provider = Provider::<Http>::try_from(rpc_url.clone()).unwrap(); + + debug!("Waited {:?} for behavior {:?}", relative_start_duration, simulation_behavior.id); + // Wallet is derived from mnemomic with user_id to ensure each user have the same wallet for the simulation. + let mnemonic = std::env::var("MNEMONIC").unwrap_or("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string()); + let wallet = MnemonicBuilder::<English>::default() + .phrase(PathOrString::String(mnemonic.clone())) + .index(simulation_behavior.mobility_record.user_id as u32) + .unwrap() + .build() + .unwrap() + .with_chain_id(chain_id); + + info!( + "Sending bx {:?}, qty: {:?},user {:?} with wallet {:?} ", + simulation_behavior.id, + simulation_behavior.quantity, + simulation_behavior.mobility_record.user_id, + wallet.address() + ); + let client = SignerMiddleware::new(provider.clone(), wallet.clone()); + + let bx_hash = txbx::send_behavior_transaction( + client.clone(), + chain_id, + time_since_beginning, + simulation_behavior.quantity, + Bytes::default(), // Bytes::from_static(&behavior.mobility_record), + ).await; + info!("Sent bx({:?}): {:?}",simulation_behavior.id,bx_hash); + } + })) + } + + info!("Scheduling done, waiting for bx & tx threads..."); + + // Wait for all of them to complete. + for handle in handles { + block_on(handle).unwrap(); + } + }, + Some(Commands::Mine(params)) => { + + // Override env vars if specified in CLI + if params.rpc_url.clone().is_some(){ + rpc_url = params.rpc_url.clone().unwrap(); + } + if params.chain_id.clone().is_some(){ + chain_id = params.chain_id.clone().unwrap(); + } + + // Wallet is derived from mnemomic with user_id to ensure each user have the same wallet for the simulation. + let miner_signing_key = std::env::var("MINER_SIGNING_KEY").expect("Must specify a miner private key"); + let wallet: LocalWallet = miner_signing_key.parse::<LocalWallet>().unwrap(); + let miner_addr = wallet.address(); + info!("Mining with 0x{:02x} ...",miner_addr); + + let behavior = get_miner_behavior(wallet, chain_id, miner_addr); + // SEND IT + let provider = Provider::<Http>::try_from(rpc_url.clone()).unwrap(); + debug!("Miner behavior: {:?}",serde_json::to_string(&behavior.clone())); + let block = provider.request::<[Behavior;1], Block>("emc_mine", [behavior]).await.unwrap(); + + debug!("Mining response: {:?}", block); + info!("Mined block {:?}", block.number.unwrap()) + }, + None => {} } - - HttpServer::new(move || { - App::new() - .app_data(web::Data::new(AppState { - signer: client.clone(), - chain_id, - rpc_url: rpc_url.clone(), - })) - .service(info) - .service(healthcheck) - .service(feed_block) - .service(send_tx) - .service(send_bx) - }) - .bind(("127.0.0.1", 8080))? - .run() - .await -} - -async fn send_transaction( - signer: SignerMiddleware<Provider<Http>, LocalWallet>, - chain_id: u64, - to: Address, - value: U256, -) -> H256 { - // craft the transaction - let tx = TransactionRequest::new() - .to(to) - .value(value) - .chain_id(chain_id); - - let pending_tx: PendingTransaction<'_, Http> = signer.send_transaction(tx, None).await.unwrap(); - info!("Pending tx: {:?}\n", pending_tx.tx_hash()); - pending_tx.tx_hash() -} -async fn send_behavior_transaction( - signer: SignerMiddleware<Provider<Http>, LocalWallet>, - chain_id: u64, - timestamp: u64, - quantity: U256, - data: Bytes, -) -> H256 { - let tr = TransactionRequest::new() - // .to(signer.address()) - .value(quantity) - .chain_id(chain_id) - .data(data.0.clone()); - - let bx = EcoMobiCoinBehaviorTransactionRequest::new(tr, Some(U64::from(timestamp))); - // let typed_tx = transaction::eip2718::TypedTransaction::EcoMobiCoinBehavior(bx); - // let bx_signed = signer.sign_transaction(&typed_tx); - // send it! - let pending_bx: PendingTransaction<'_, Http> = signer.send_transaction(bx, None).await.unwrap(); - - let bx: EcoMobiCoinBehaviorTransactionRequest = EcoMobiCoinBehaviorTransactionRequest::new( - TransactionRequest::new() - // .to(signer.address()) - .value(quantity) - .chain_id(chain_id) - .data(data.0.clone()), - Some(U64::from(timestamp)), - ); - debug!("bx {:?}", bx); - - let bx_sighash = TypedTransaction::EcoMobiCoinBehavior(bx.clone()).sighash(); - debug!("bx_sighash {:02x}", bx_sighash); - - let behavior_rlp = bx.rlp(); - debug!("behavior_rlp {:02x}", behavior_rlp.clone()); - let bx_hash: H256 = keccak256(behavior_rlp.as_ref()).into(); - debug!("bx_hash {:02x}", bx_hash); - - info!("Pending bx: {:?}\n", pending_bx.tx_hash()); - pending_bx.tx_hash() -} - -async fn send_default_tx( - signer: SignerMiddleware<Provider<Http>, LocalWallet>, - chain_id: u64, -) -> H256 { - send_transaction(signer, chain_id, Address::zero(), U256::from(210000)).await -} -async fn send_default_bx( - signer: SignerMiddleware<Provider<Http>, LocalWallet>, - chain_id: u64, -) -> H256 { - let now = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(); - send_behavior_transaction(signer, chain_id, now,U256::from(210000), Bytes::from_str("0x015d8eb90000000000000000000000000000000000000000000000000000000000878c1c00000000000000000000000000000000000000000000000000000000644662bc0000000000000000000000000000000000000000000000000000001ee24fba17b7e19cc10812911dfa8a438e0a81a9933f843aa5b528899b8d9e221b649ae0df00000000000000000000000000000000000000000000000000000000000000060000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240").unwrap()).await + Ok(()) } diff --git a/src/utils/miner.rs b/src/utils/miner.rs new file mode 100644 index 0000000000000000000000000000000000000000..82fae9982958ee0bf88926739df982e13a110f9e --- /dev/null +++ b/src/utils/miner.rs @@ -0,0 +1,55 @@ +use std::time::SystemTime; + +use ecomobicoin_jsonrpc::types::{Behavior, BehaviorMessage, Bytes}; +use ethereum_interfaces::types::H256; +use ethereum_types::{Address, U64}; +use ethers::{ + signers::{LocalWallet, Signer}, + types::{ + transaction::ecomobicoin_behavior::EcoMobiCoinBehaviorTransactionRequest, + TransactionRequest, + }, +}; +use ethnum::U256; + +/// Get a RPC Behavior for miner with default values, ready to send. +pub fn get_miner_behavior(wallet: LocalWallet, chain_id: u64, to: Address) -> Behavior { + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + + let behavior_msg = BehaviorMessage{ + chain_id: U64::from(chain_id), + to:Some(to.clone()), + timestamp: U64::from(now), + quantity: U256::from(210000_u64), + input: Bytes::from("0x015d8eb90000000000000000000000000000000000000000000000000000000000878c1c00000000000000000000000000000000000000000000000000000000644662bc0000000000000000000000000000000000000000000000000000001ee24fba17b7e19cc10812911dfa8a438e0a81a9933f843aa5b528899b8d9e221b649ae0df00000000000000000000000000000000000000000000000000000000000000060000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240") }; + + // let behavior = wallet. + let tr = TransactionRequest::new() + .to(to) + .value(primitive_types::U256::zero()) + .chain_id(chain_id) + .data(behavior_msg.input.0.clone()); + + let bx = EcoMobiCoinBehaviorTransactionRequest::new(tr, Some(U64::from(now))); + // wallet.sig + let signature = wallet + .sign_transaction_sync( + ðers::types::transaction::eip2718::TypedTransaction::EcoMobiCoinBehavior(bx), + ) + .unwrap(); + + Behavior { + message: behavior_msg, + v: signature.v.into(), + r: H256::from(signature.r).into(), + s: H256::from(signature.s).into(), + from: wallet.address(), + hash: H256::default().into(), + behavior_index: None, + block_number: None, + block_hash: None, + } +} diff --git a/src/utils/mobility_data.rs b/src/utils/mobility_data.rs new file mode 100644 index 0000000000000000000000000000000000000000..edc15bf75154d526e49eb7b07ba9601341554f8d --- /dev/null +++ b/src/utils/mobility_data.rs @@ -0,0 +1,297 @@ +use std::{error::Error, ops::Sub, time::Duration}; + +use csv::Reader; +use ethers::{ + prelude::rand::{thread_rng, Rng}, + types::Bytes, +}; +use primitive_types::U256; +use serde::Deserialize; +use time::OffsetDateTime; + +/// Mobility Record from Public Transportation +/// --- +/// Created from data in `data/dataset-extract.csv` +/// +/// CSV header: +/// id user_id network service label created_at from_coord from_label from_datetime from_latitude from_longitude to_coord to_label to_datetime to_latitude to_longitude +#[derive(Debug, Deserialize, Clone)] +pub struct MobilityRecord { + pub id: u64, + pub user_id: u64, + pub network: String, + pub service: String, + pub label: String, + // `YYYY-MM-DD HH:mm:ss.SSSSSS` rfc3339 + #[serde(with = "time::serde::rfc3339")] + pub created_at: time::OffsetDateTime, + /// Departure coordinates in hex PostGIS EWKB (Extended Well-Known Binary) + pub from_coord: Option<String>, + pub from_label: Option<String>, + // either `YYYY-MM-DD HH:mm:ss` or `YYYY-MM-DD HH:mm:ss.SSS` rfc3339 + #[serde(with = "time::serde::rfc3339::option")] + pub from_datetime: Option<time::OffsetDateTime>, + pub from_latitude: Option<f64>, + pub from_longitude: Option<f64>, + /// Arrival coordinates in hex PostGIS EWKB (Extended Well-Known Binary) + pub to_coord: Option<String>, + pub to_label: Option<String>, + // `YYYY-MM-DD HH:mm:ss` rfc3339 + #[serde(with = "time::serde::rfc3339::option")] + pub to_datetime: Option<time::OffsetDateTime>, + pub to_latitude: Option<f64>, + pub to_longitude: Option<f64>, +} + +impl MobilityRecord { + unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] { + ::core::slice::from_raw_parts((p as *const T) as *const u8, ::core::mem::size_of::<T>()) + } + pub fn to_bytes(&self) -> Bytes { + let bytes: &[u8] = unsafe { + ::core::slice::from_raw_parts( + (self as *const MobilityRecord) as *const u8, + ::core::mem::size_of::<MobilityRecord>(), + ) + }; + + Bytes::from_static(bytes) + } +} + +pub fn read_csv<R: std::io::Read>( + mut reader: Reader<R>, +) -> Result<Vec<MobilityRecord>, Box<dyn Error>> { + let mut records = vec![]; + for result in reader.deserialize() { + let record: MobilityRecord = result?; + // println!("{:?}", record); + records.push(record) + } + Ok(records) +} + +/// Mobility Behavior +/// --- +/// +/// Including a MobilityRecord from CSV extraction +#[derive(Debug, Clone)] +pub struct MobilityBehavior { + pub id: u64, + // pub dataset_id: u64, + // in milliseconds + pub relative_start_time: i128, + pub quantity: U256, + pub mobility_record: MobilityRecord, +} + +pub fn find_earliest_mobility_record(records: &Vec<MobilityRecord>) -> OffsetDateTime { + records + .iter() + .min_by(|x, y| x.created_at.cmp(&y.created_at)) + .unwrap() + .created_at +} + +pub fn find_latest_mobility_record(records: &Vec<MobilityRecord>) -> OffsetDateTime { + records + .iter() + .max_by(|x, y| x.created_at.cmp(&y.created_at)) + .unwrap() + .created_at +} + +pub fn generate_behaviors_from_records(records: Vec<MobilityRecord>) -> Vec<MobilityBehavior> { + let earliest_datetime = find_earliest_mobility_record(&records); + + // let dataset_id = thread_rng().gen::<u64>(); + + records + .iter() + .enumerate() + .map(|(i, r)| MobilityBehavior { + id: i as u64, + // dataset_id, + relative_start_time: r.created_at.sub(earliest_datetime).whole_milliseconds(), + quantity: quantify_mobility(r.service.clone()), + mobility_record: r.clone(), + }) + .collect() +} + +/// Strech behavior start time for simulation +pub fn compute_stretched_relative_start_time( + relative_start_time: u64, + total_simulation_duration: u64, + dataset_max_relative_time: u64, +) -> Duration { + Duration::from_millis( + (relative_start_time * total_simulation_duration * 1000) / dataset_max_relative_time, + ) +} + +/// Provide differents fake quantities depending on mobility e.g. BIKE that will be use for rewards calculation +pub fn quantify_mobility(service: String) -> U256 { + if service == "BIKE_SHARING" { + U256::from(1000000000000000000_u128) + } else if service == "BIKE_RENTAL" { + U256::from(500000000000000000_u128) + } else if service == "PUBLIC_TRANSPORT" { + U256::from(10000000000000000_u128) + } else { + U256::from(1) + } +} + +#[cfg(test)] +mod tests { + use time::OffsetDateTime; + + use super::*; + + #[test] + fn read_csv_should_work() { + // ARRANGE + let example_data = "\ + id,user_id,network,service,label,created_at,from_coord,from_label,from_datetime,to_coord,to_label,to_datetime,from_latitude,from_longitude,to_latitude,to_longitude + 10653,79,mulhouse,PUBLIC_TRANSPORT,Abo,2023-07-02T19:02:04.987621Z,DEADC0DE,TEST,2023-07-02T21:02:04.000Z,DEADC0DE,TEST,2023-07-02T21:02:04.000Z,0.0,0.0,0.0,0.0 + "; + // let mut rdr = Reader::from_reader(example_data.as_bytes()); + let mut rdr = Reader::from_path("./data/dataset-extract.csv").unwrap(); + // ACT + let result = read_csv(rdr).unwrap(); + // ASSERT + assert_eq!(6, result.len()) + } + + #[test] + fn to_bytes_should_work() { + // ARRANGE + let mobility_record = MobilityRecord { + id: 1337, + user_id: 1, + network: String::from("mulhouse"), + service: String::from("PUBLIC_TRANSPORT"), + label: String::from("BLABLABLA"), + created_at: OffsetDateTime::from_unix_timestamp(1699353308).unwrap(), + from_datetime: None, + from_coord: None, + from_label: None, + from_latitude: None, + from_longitude: None, + to_datetime: None, + to_coord: None, + to_label: None, + to_latitude: None, + to_longitude: None, + }; + + // ACT + let result = mobility_record.to_bytes(); + // ASSERT + + // FIXME Binary data change between tests o + assert!(!result.to_string().is_empty()); + // assert_eq!("0x", result.to_string()); + } + + #[test] + fn find_earliest_mobility_record_should_work() { + // ARRANGE + let mut rdr = Reader::from_path("./data/dataset-extract.csv").unwrap(); + let records = read_csv(rdr).unwrap(); + + // ACT + let earliest_datetime = find_earliest_mobility_record(&records); + + // ASSERT + assert_eq!( + 1685570991226773000, + earliest_datetime.unix_timestamp_nanos() + ); + } + + #[test] + fn find_latest_mobility_record_should_work() { + // ARRANGE + let mut rdr = Reader::from_path("./data/dataset-extract.csv").unwrap(); + let records = read_csv(rdr).unwrap(); + + // ACT + let latest_datetime = find_latest_mobility_record(&records); + + // ASSERT + assert_eq!(1688324524987621000, latest_datetime.unix_timestamp_nanos()); + } + + #[test] + fn create_behaviors_from_records_should_work() { + // ARRANGE + let mut rdr = Reader::from_path("./data/dataset-extract.csv").unwrap(); + let records = read_csv(rdr).unwrap(); + + // ACT + let behaviors = generate_behaviors_from_records(records.clone()); + // ASSERT + assert_eq!(records.len(), behaviors.len()); + } + + #[test] + fn compute_stretched_relative_start_time_should_work_0() { + // ARRANGE + + // ACT + let result = compute_stretched_relative_start_time(0, 10, 1000); + + // ASSERT + assert_eq!(Duration::from_secs(0), result); + } + + #[test] + fn compute_stretched_relative_start_time_should_work() { + // ARRANGE + // 1000 ms + let relative_start_time = 1000; + // 10 secs + let total_simulation_duration = 10; + // 2 sec + let dataset_max_relative_time = 2000; + + // ACT + let result = compute_stretched_relative_start_time( + relative_start_time, + total_simulation_duration, + dataset_max_relative_time, + ); + // ASSERT + assert_eq!(Duration::from_secs(5), result); + } + #[test] + fn compute_stretched_relative_start_time_should_work_max() { + // ARRANGE + // 1000 ms + let relative_start_time = 2000; + // 10 secs + let total_simulation_duration = 10; + // 2 sec + let dataset_max_relative_time = 2000; + + // ACT + let result = compute_stretched_relative_start_time( + relative_start_time, + total_simulation_duration, + dataset_max_relative_time, + ); + // ASSERT + assert_eq!(Duration::from_secs(10), result); + } + + #[test] + fn quantify_mobility_bike() { + // ARRANGE + // ACT + let result = quantify_mobility(String::from("BIKE_SHARING")); + // ASSERT + assert_eq!(U256::from(1000000000000000000_u128), result); + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..7a89f5ada9ca2650f6cad2fde6d451c3f679c2f0 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,3 @@ +pub mod miner; +pub mod mobility_data; +pub mod txbx; diff --git a/src/utils/txbx.rs b/src/utils/txbx.rs new file mode 100644 index 0000000000000000000000000000000000000000..fb674957cbd33ed43f6629781d353efe239dfee7 --- /dev/null +++ b/src/utils/txbx.rs @@ -0,0 +1,103 @@ +use ethereum_types::U256; +use ethereum_types::{Address, U64}; +use ethers::{ + prelude::*, + types::transaction::{ + ecomobicoin_behavior::EcoMobiCoinBehaviorTransactionRequest, eip2718::TypedTransaction, + }, + utils::keccak256, +}; +use std::str::FromStr; +use std::time::SystemTime; +use tracing::debug; + +pub async fn send_transaction( + signer: SignerMiddleware<Provider<Http>, LocalWallet>, + chain_id: u64, + to: Address, + value: U256, +) -> H256 { + // craft the transaction + let tx = TransactionRequest::new() + .to(to) + .value(value) + .chain_id(chain_id); + + let pending_tx: PendingTransaction<'_, Http> = signer.send_transaction(tx, None).await.unwrap(); + pending_tx.tx_hash() +} +pub async fn send_behavior_transaction( + signer: SignerMiddleware<Provider<Http>, LocalWallet>, + chain_id: u64, + timestamp: u64, + quantity: U256, + data: Bytes, +) -> H256 { + let tr = TransactionRequest::new() + // .to(signer.address()) + .value(quantity) + .chain_id(chain_id) + .data(data.0.clone()); + + let bx = EcoMobiCoinBehaviorTransactionRequest::new(tr, Some(U64::from(timestamp))); + // let typed_tx = transaction::eip2718::TypedTransaction::EcoMobiCoinBehavior(bx); + // let bx_signed = signer.sign_transaction(&typed_tx); + // send it! + let pending_bx: PendingTransaction<'_, Http> = signer.send_transaction(bx, None).await.unwrap(); + + let bx: EcoMobiCoinBehaviorTransactionRequest = EcoMobiCoinBehaviorTransactionRequest::new( + TransactionRequest::new() + // .to(signer.address()) + .value(quantity) + .chain_id(chain_id) + .data(data.0.clone()), + Some(U64::from(timestamp)), + ); + debug!("bx {:?}", bx); + + let bx_sighash = TypedTransaction::EcoMobiCoinBehavior(bx.clone()).sighash(); + debug!("bx_sighash {:02x}", bx_sighash); + + let behavior_rlp = bx.rlp(); + debug!("behavior_rlp {:02x}", behavior_rlp.clone()); + let bx_hash: H256 = keccak256(behavior_rlp.as_ref()).into(); + debug!("bx_hash {:02x}", bx_hash); + + pending_bx.tx_hash() +} + +pub async fn send_default_tx( + signer: SignerMiddleware<Provider<Http>, LocalWallet>, + chain_id: u64, +) -> H256 { + send_transaction(signer, chain_id, Address::zero(), U256::from(210000)).await +} +pub async fn send_default_bx( + signer: SignerMiddleware<Provider<Http>, LocalWallet>, + chain_id: u64, +) -> H256 { + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + send_behavior_transaction(signer, chain_id, now,U256::from(210000), Bytes::from_str("0x015d8eb90000000000000000000000000000000000000000000000000000000000878c1c00000000000000000000000000000000000000000000000000000000644662bc0000000000000000000000000000000000000000000000000000001ee24fba17b7e19cc10812911dfa8a438e0a81a9933f843aa5b528899b8d9e221b649ae0df00000000000000000000000000000000000000000000000000000000000000060000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240").unwrap()).await +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn build_default_tx_test() { + // ARRANGE + // ACT + // ASSERT + } + + #[test] + fn build_default_bx_test() { + // ARRANGE + // ACT + // ASSERT + } +}