From 31a7d1fd17673ba23ae59fcc3a51b0358ceddb42 Mon Sep 17 00:00:00 2001 From: o-tsaruk Date: Thu, 10 Aug 2023 14:03:10 +0300 Subject: [PATCH] M2 --- Cargo.lock | 2061 +++++++++++------ crates/builder/src/cli.rs | 6 + crates/builder/src/commands.rs | 1 + crates/builder/src/main.rs | 9 +- crates/builder/src/process/container.rs | 136 +- crates/builder/src/process/worker.rs | 429 +++- crates/common/Cargo.toml | 28 +- crates/common/artifacts/astar.scale | Bin 193455 -> 0 bytes crates/common/src/config.rs | 9 +- crates/common/src/lib.rs | 1 - crates/common/src/logging.rs | 1 + crates/common/src/rpc.rs | 374 +++ crates/common/src/rpc/mod.rs | 369 --- crates/common/src/rpc/schemas.rs | 2 - crates/db/src/build_session.rs | 4 - crates/db/src/node.rs | 5 +- crates/event_client/src/cli.rs | 26 +- crates/event_client/src/cli/initialize.rs | 152 +- crates/event_client/src/cli/traverse.rs | 67 +- .../event_client/src/cli/update_contract.rs | 4 +- crates/event_client/src/cli/watch.rs | 167 +- crates/event_client/src/main.rs | 12 +- crates/event_client/src/utils.rs | 28 +- crates/migration/src/cli.rs | 6 + crates/migration/src/lib.rs | 4 + .../m20220101_000014_remove_node_schema.rs | 36 + .../m20220101_000015_remove_rust_version.rs | 54 + crates/migration/src/main.rs | 2 +- crates/patron/Cargo.toml | 1 + crates/patron/src/commands.rs | 44 +- crates/patron/src/commands/build.rs | 119 + crates/patron/src/commands/deploy.rs | 259 +-- crates/patron/src/commands/verify.rs | 91 + crates/patron/src/config.rs | 3 - crates/patron/src/main.rs | 8 + crates/patron/src/process.rs | 297 +++ crates/server/Cargo.toml | 1 - crates/server/src/handlers/auth/login.rs | 10 +- .../src/handlers/build_sessions/create.rs | 11 +- .../src/handlers/build_sessions/details.rs | 7 - .../src/handlers/build_sessions/latest.rs | 1 - .../src/handlers/build_sessions/list.rs | 2 - .../src/handlers/build_sessions/logs.rs | 1 - .../src/handlers/build_sessions/metadata.rs | 1 - .../src/handlers/build_sessions/status.rs | 1 - .../server/src/handlers/contracts/details.rs | 11 +- .../server/src/handlers/contracts/events.rs | 5 +- crates/server/src/handlers/contracts/mod.rs | 3 +- crates/server/src/handlers/files/upload.rs | 1 - crates/server/src/handlers/keys/delete.rs | 4 +- crates/server/src/handlers/keys/list.rs | 2 +- crates/server/src/handlers/keys/verify.rs | 8 +- crates/server/src/handlers/payment/check.rs | 86 +- crates/server/src/main.rs | 5 +- crates/server/src/schema.rs | 7 +- docs/cli.md | 13 +- docs/self-hosted.md | 5 +- flake.lock | 30 +- flake.nix | 29 +- nix/build-stages.nix | 54 + nix/cargo-contract.nix | 28 - nix/ink-builder.nix | 86 - 62 files changed, 3264 insertions(+), 1963 deletions(-) delete mode 100644 crates/common/artifacts/astar.scale create mode 100644 crates/common/src/rpc.rs delete mode 100644 crates/common/src/rpc/mod.rs delete mode 100644 crates/common/src/rpc/schemas.rs create mode 100644 crates/migration/src/m20220101_000014_remove_node_schema.rs create mode 100644 crates/migration/src/m20220101_000015_remove_rust_version.rs create mode 100644 crates/patron/src/commands/build.rs create mode 100644 crates/patron/src/commands/verify.rs create mode 100644 crates/patron/src/process.rs create mode 100644 nix/build-stages.nix delete mode 100644 nix/cargo-contract.nix delete mode 100644 nix/ink-builder.nix diff --git a/Cargo.lock b/Cargo.lock index 4dd305a..7e78856 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,12 +13,60 @@ dependencies = [ ] [[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +name = "ac-compose-macros" +version = "0.4.0" +source = "git+https://github.com/scs/substrate-api-client?branch=polkadot-v0.9.43#6faaf232a8c5761d4ce69d0006d5312956f64bdc" +dependencies = [ + "ac-primitives", + "log", + "maybe-async", +] + +[[package]] +name = "ac-node-api" +version = "0.4.0" +source = "git+https://github.com/scs/substrate-api-client?branch=polkadot-v0.9.43#6faaf232a8c5761d4ce69d0006d5312956f64bdc" +dependencies = [ + "ac-primitives", + "bitvec", + "derive_more", + "either", + "frame-metadata", + "hex", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "serde_json", + "sp-application-crypto", + "sp-core", + "sp-runtime", + "sp-runtime-interface", +] + +[[package]] +name = "ac-primitives" +version = "0.7.0" +source = "git+https://github.com/scs/substrate-api-client?branch=polkadot-v0.9.43#6faaf232a8c5761d4ce69d0006d5312956f64bdc" dependencies = [ - "gimli 0.26.2", + "frame-system", + "impl-serde", + "pallet-assets", + "pallet-balances", + "pallet-contracts", + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde", + "serde_json", + "sp-application-crypto", + "sp-core", + "sp-core-hashing", + "sp-runtime", + "sp-runtime-interface", + "sp-staking", + "sp-version", + "sp-weights", ] [[package]] @@ -27,7 +75,16 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli 0.27.2", + "gimli", +] + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", ] [[package]] @@ -42,7 +99,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "once_cell", "version_check", ] @@ -54,16 +111,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom 0.2.9", + "getrandom 0.2.10", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] @@ -80,7 +137,7 @@ dependencies = [ "bytes", "cfg-if", "http", - "indexmap", + "indexmap 1.9.3", "schemars", "serde", "serde_json", @@ -97,10 +154,10 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0487f8598afe49e6bc950a613a678bd962c4a6f431022ded62643c8b990301a" dependencies = [ - "darling 0.20.1", + "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] @@ -109,6 +166,18 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -144,15 +213,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] @@ -178,9 +247,18 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" + +[[package]] +name = "approx" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] [[package]] name = "array-bytes" @@ -202,9 +280,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "assert_json" @@ -245,18 +323,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] @@ -298,7 +376,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand", + "fastrand 1.9.0", "hex", "http", "hyper", @@ -318,7 +396,7 @@ checksum = "1fcdb2f7acbc076ff5ad05e7864bdb191ca70a6fd07668dc3a1a8bcd051de5ae" dependencies = [ "aws-smithy-async", "aws-smithy-types", - "fastrand", + "fastrand 1.9.0", "tokio", "tracing", "zeroize", @@ -472,7 +550,7 @@ dependencies = [ "once_cell", "percent-encoding", "regex", - "sha2 0.10.6", + "sha2 0.10.7", "time", "tracing", ] @@ -506,7 +584,7 @@ dependencies = [ "md-5", "pin-project-lite", "sha1", - "sha2 0.10.6", + "sha2 0.10.7", "tracing", ] @@ -521,7 +599,7 @@ dependencies = [ "aws-smithy-http-tower", "aws-smithy-types", "bytes", - "fastrand", + "fastrand 1.9.0", "http", "http-body", "hyper", @@ -643,13 +721,13 @@ dependencies = [ [[package]] name = "axum" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" +checksum = "a6a1de45611fdb535bfde7b7de4fd54f4fd2b17b1737c0a59b69bf9b92074b8c" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "headers", @@ -705,9 +783,9 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "febf23ab04509bd7672e6abe76bd8277af31b679e89fa5ffc6087dc289a448a3" +checksum = "cebbcd90f811f93fc2a993024caecc1e8270d9d1eb9d3359edb3069c2096ea6f" dependencies = [ "axum", "axum-core", @@ -727,16 +805,16 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ - "addr2line 0.19.0", + "addr2line 0.20.0", "cc", "cfg-if", "libc", "miniz_oxide", - "object 0.30.3", + "object 0.31.1", "rustc-demangle", ] @@ -754,10 +832,10 @@ dependencies = [ ] [[package]] -name = "base58" +name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" @@ -781,6 +859,12 @@ dependencies = [ "vsimd", ] +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "beef" version = "0.5.2" @@ -816,6 +900,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bitvec" version = "1.0.1" @@ -844,7 +934,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", - "arrayvec 0.7.2", + "arrayvec 0.7.4", "constant_time_eq", ] @@ -973,9 +1063,9 @@ dependencies = [ [[package]] name = "bounded-collections" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07fbd1d11282a1eb134d3c3b7cf8ce213b5161c6e5f73fb1b98618482c606b64" +checksum = "eb5b05133427c07c4776906f673ccf36c21b102c9829c641a5b56bd151d44fd6" dependencies = [ "log", "parity-scale-codec", @@ -995,7 +1085,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bollard", - "clap 4.3.0", + "clap 4.3.19", "common", "db", "derive_more", @@ -1057,6 +1147,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + [[package]] name = "byteorder" version = "1.4.3" @@ -1081,9 +1177,21 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f1226cd9da55587234753d1245dd5b132343ea240f26b6a9003d68706141ba" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-expr" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" +dependencies = [ + "smallvec", +] [[package]] name = "cfg-if" @@ -1093,12 +1201,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", - "num-integer", "num-traits", "serde", "winapi", @@ -1110,34 +1218,33 @@ version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ - "bitflags", + "bitflags 1.3.2", "clap_derive 3.2.25", "clap_lex 0.2.4", - "indexmap", + "indexmap 1.9.3", "once_cell", "textwrap", ] [[package]] name = "clap" -version = "4.3.0" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" dependencies = [ "clap_builder", - "clap_derive 4.3.0", + "clap_derive 4.3.12", "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.0" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" dependencies = [ "anstream", "anstyle", - "bitflags", "clap_lex 0.5.0", "strsim", ] @@ -1157,14 +1264,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.0" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] @@ -1207,13 +1314,18 @@ dependencies = [ "blake2", "byte-unit", "figment", + "frame-metadata", "futures-util", "hex", - "itertools", + "lru", + "pallet-contracts", "pallet-contracts-primitives", "parity-scale-codec", "serde", - "subxt", + "sp-core", + "sp-version", + "substrate-api-client", + "tokio", "tracing-core", "tracing-subscriber 0.3.17", ] @@ -1247,11 +1359,17 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "const-oid" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" + [[package]] name = "constant_time_eq" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" [[package]] name = "convert_case" @@ -1286,27 +1404,27 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] [[package]] name = "cranelift-entity" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f42ea692c7b450ad18b8c9889661505d51c09ec4380cf1c2d278dbb2da22cae1" +checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" dependencies = [ "serde", ] [[package]] name = "crc32c" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfea2db42e9927a3845fb268a10a72faed6d416065f77873f05e411457c363e" +checksum = "d8f48d60e5b4d2c53d5c2b1d8a58c849a70ae5e5509b08a48d047e3b65714a74" dependencies = [ "rustc_version", ] @@ -1332,9 +1450,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -1345,6 +1463,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1413,12 +1543,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ - "darling_core 0.20.1", - "darling_macro 0.20.1", + "darling_core 0.20.3", + "darling_macro 0.20.3", ] [[package]] @@ -1437,16 +1567,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] @@ -1462,13 +1592,13 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ - "darling_core 0.20.1", + "darling_core 0.20.3", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] @@ -1485,10 +1615,29 @@ dependencies = [ ] [[package]] -name = "derivative" -version = "2.2.0" +name = "der" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8810e7e2cf385b1e9b50d68264908ec367ba642c96d02edfe61c39e88e2a3c01" +dependencies = [ + "serde", +] + +[[package]] +name = "derive-syn-parse" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" dependencies = [ "proc-macro2", "quote", @@ -1533,6 +1682,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle", ] @@ -1592,9 +1742,23 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" + +[[package]] +name = "ecdsa" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature 2.1.0", + "spki", +] [[package]] name = "ed25519" @@ -1602,7 +1766,7 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "signature 1.6.4", ] [[package]] @@ -1633,9 +1797,28 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array 0.14.7", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] [[package]] name = "encode_unicode" @@ -1658,11 +1841,17 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", @@ -1690,7 +1879,7 @@ name = "event_client" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.3.0", + "clap 4.3.19", "common", "db", "derive_more", @@ -1703,6 +1892,19 @@ dependencies = [ "unix-ts", ] +[[package]] +name = "expander" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -1724,16 +1926,32 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "figment" -version = "0.10.9" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d79dace1d256182aa00f1ead2e5042558f420af2e1ed24dd79e00214e9ed99" +checksum = "4547e226f4c9ab860571e070a9034192b3175580ecea38da34fcdb53a018c9a5" dependencies = [ "atomic", "pear", "serde", - "toml 0.7.4", + "toml 0.7.6", "uncased", "version_check", ] @@ -1797,13 +2015,38 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] +[[package]] +name = "frame-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "frame-support", + "frame-support-procedural", + "frame-system", + "linregress", + "log", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "static_assertions", +] + [[package]] name = "frame-metadata" version = "15.1.0" @@ -1817,44 +2060,141 @@ dependencies = [ ] [[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +name = "frame-support" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "bitflags 1.3.2", + "environmental", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "k256", + "log", + "once_cell", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-debug-derive", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-weights", + "tt-call", +] [[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +name = "frame-support-procedural" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", + "Inflector", + "cfg-expr", + "derive-syn-parse", + "frame-support-procedural-tools", + "itertools", + "proc-macro-warning", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +name = "frame-support-procedural-tools" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ - "futures-core", - "futures-sink", + "frame-support-procedural-tools-derive", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +name = "frame-support-procedural-tools-derive" +version = "3.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] [[package]] -name = "futures-executor" +name = "frame-system" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "cfg-if", + "frame-support", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", + "sp-weights", +] + +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" @@ -1890,7 +2230,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] @@ -1946,6 +2286,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1961,38 +2302,42 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] name = "gimli" -version = "0.26.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" dependencies = [ "fallible-iterator", + "indexmap 1.9.3", "stable_deref_trait", ] [[package]] -name = "gimli" -version = "0.27.2" +name = "group" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] [[package]] name = "h2" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -2000,7 +2345,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -2040,13 +2385,23 @@ dependencies = [ "ahash 0.8.3", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash 0.8.3", + "allocator-api2", +] + [[package]] name = "hashlink" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0761a1b9491c4f2e3d66aa0f62d0fba0af9a0e2852e4d48ea506632a4b56e6aa" +checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" dependencies = [ - "hashbrown 0.13.2", + "hashbrown 0.14.0", ] [[package]] @@ -2056,7 +2411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", - "bitflags", + "bitflags 1.3.2", "bytes", "headers-core", "http", @@ -2094,18 +2449,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -2198,9 +2544,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" @@ -2216,9 +2562,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -2251,20 +2597,20 @@ dependencies = [ "rustls-native-certs", "tokio", "tokio-rustls 0.23.4", - "webpki-roots", ] [[package]] name = "hyper-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ + "futures-util", "http", "hyper", - "rustls 0.21.1", + "rustls 0.21.5", "tokio", - "tokio-rustls 0.24.0", + "tokio-rustls 0.24.1", ] [[package]] @@ -2282,9 +2628,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2311,20 +2657,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -2376,23 +2711,40 @@ dependencies = [ "serde", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + [[package]] name = "indicatif" -version = "0.17.3" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729" +checksum = "8ff8cc23a7393a397ed1d7f56e6365cba772aba9f9912ab968b03043c395d057" dependencies = [ "console", + "instant", "number_prefix", - "portable-atomic 0.3.20", + "portable-atomic", "unicode-width", ] [[package]] name = "ink_metadata" -version = "4.2.0" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4af082b4c2eb246d27b358411ef950811f851c1099aa507ba4bcdd7214d40ccd" +checksum = "d5fb2b5ad83f725a6d0c8886ca737964d0013a193ca2d21c7e514fd427672416" dependencies = [ "derive_more", "impl-serde", @@ -2404,18 +2756,18 @@ dependencies = [ [[package]] name = "ink_prelude" -version = "4.2.0" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0662ba1d4aa26f0fea36ce6ef9ef1e510e24c900597d703b148c8c23b675b9" +checksum = "6a6f174d742ff929abe66716ad8159f324441b4ff5161a3b0e282f416afbbac1" dependencies = [ "cfg-if", ] [[package]] name = "ink_primitives" -version = "4.2.0" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52693c5e74600f5bd4c0f6d447ba9c4e491e4edf685eaf9f9f347a3cb1cde66b" +checksum = "14b4e4772e1b9384233103c1f488df9854d24b3c16168bcf23613b7d98fb363f" dependencies = [ "derive_more", "ink_prelude", @@ -2456,16 +2808,16 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "libc", "windows-sys 0.48.0", ] [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is-docker" @@ -2478,13 +2830,12 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix 0.37.19", + "hermit-abi", + "rustix 0.38.4", "windows-sys 0.48.0", ] @@ -2509,15 +2860,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -2530,7 +2881,6 @@ checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", - "jsonrpsee-http-client", "jsonrpsee-types", ] @@ -2568,7 +2918,6 @@ dependencies = [ "futures-channel", "futures-timer", "futures-util", - "hyper", "jsonrpsee-types", "rustc-hash", "serde", @@ -2579,36 +2928,30 @@ dependencies = [ ] [[package]] -name = "jsonrpsee-http-client" +name = "jsonrpsee-types" version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" +checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" dependencies = [ - "async-trait", - "hyper", - "hyper-rustls 0.23.2", - "jsonrpsee-core", - "jsonrpsee-types", - "rustc-hash", + "anyhow", + "beef", "serde", "serde_json", "thiserror", - "tokio", "tracing", ] [[package]] -name = "jsonrpsee-types" -version = "0.16.2" +name = "k256" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ - "anyhow", - "beef", - "serde", - "serde_json", - "thiserror", - "tracing", + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.7", ] [[package]] @@ -2628,9 +2971,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libm" @@ -2697,6 +3040,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linregress" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de0b5f52a9f84544d268f5fabb71b38962d6aa3c6600b8bcd27d44ccf9c9c45" +dependencies = [ + "nalgebra", +] + [[package]] name = "linux-raw-sys" version = "0.1.4" @@ -2709,11 +3061,17 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -2721,9 +3079,18 @@ dependencies = [ [[package]] name = "log" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "lru" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eedb2bdbad7e0634f83989bf596f497b070130daaa398ab22d84c39e266deec5" +dependencies = [ + "hashbrown 0.14.0", +] [[package]] name = "mach" @@ -2740,7 +3107,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -2749,20 +3116,35 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] -name = "matches" -version = "0.1.10" +name = "matchit" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +checksum = "67827e6ea8ee8a7c4a72227ef4fc08957040acffdb5f122733b24fa12daff41b" [[package]] -name = "matchit" -version = "0.7.0" +name = "matrixmultiply" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "maybe-async" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" +checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] name = "md-5" @@ -2785,14 +3167,14 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "rustix 0.37.19", + "rustix 0.37.23", ] [[package]] name = "memoffset" -version = "0.6.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ "autocfg", ] @@ -2862,23 +3244,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2899,6 +3280,33 @@ dependencies = [ "version_check", ] +[[package]] +name = "nalgebra" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -2954,13 +3362,22 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +dependencies = [ + "num-traits", +] + [[package]] name = "num-format" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "itoa", ] @@ -2988,20 +3405,20 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] @@ -3013,30 +3430,30 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.29.0" +version = "0.30.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" dependencies = [ "crc32fast", - "hashbrown 0.12.3", - "indexmap", + "hashbrown 0.13.2", + "indexmap 1.9.3", "memchr", ] [[package]] name = "object" -version = "0.30.3" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -3052,21 +3469,22 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "open" -version = "4.1.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16814a067484415fda653868c9be0ac5f2abd2ef5d951082a5f2fe1b3662944" +checksum = "3a083c0c7e5e4a8ec4176346cf61f67ac674e8bfb059d9226e1c54a96b377c12" dependencies = [ "is-wsl", + "libc", "pathdiff", ] [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -3083,7 +3501,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] @@ -3094,9 +3512,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ "cc", "libc", @@ -3106,9 +3524,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "ouroboros" @@ -3145,13 +3563,71 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "pallet-assets" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-balances" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-contracts" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "bitflags 1.3.2", + "environmental", + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-contracts-primitives", + "pallet-contracts-proc-macro", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "serde", + "smallvec", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "wasm-instrument", + "wasmi 0.28.0", + "wasmparser-nostd", +] + [[package]] name = "pallet-contracts-primitives" -version = "23.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a4b2ba3fa3f9e9480e6fceeb1a2efc95832f1240355ddaac0e90f861f9595f" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ - "bitflags", + "bitflags 1.3.2", "parity-scale-codec", "scale-info", "sp-runtime", @@ -3159,13 +3635,23 @@ dependencies = [ "sp-weights", ] +[[package]] +name = "pallet-contracts-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "parity-scale-codec" -version = "3.5.0" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "bitvec", "byte-slice-cast", "bytes", @@ -3176,9 +3662,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.4" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -3210,7 +3696,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", + "parking_lot_core 0.9.8", ] [[package]] @@ -3229,22 +3715,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.45.0", + "windows-targets 0.48.1", ] [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pathdiff" @@ -3257,7 +3743,7 @@ name = "patron" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.3.0", + "clap 4.3.19", "common", "derive_more", "figment", @@ -3269,8 +3755,9 @@ dependencies = [ "rand 0.8.5", "reqwest", "serde", + "serde_json", "tempfile", - "toml 0.7.4", + "toml 0.7.6", "walkdir", "which", "zip", @@ -3296,9 +3783,9 @@ dependencies = [ [[package]] name = "pear" -version = "0.2.4" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec95680a7087503575284e5063e14b694b7a9c0b065e5dceec661e0497127e8" +checksum = "61a386cd715229d399604b50d1361683fe687066f42d56f54be995bc6868f71c" dependencies = [ "inlinable_string", "pear_codegen", @@ -3307,47 +3794,47 @@ dependencies = [ [[package]] name = "pear_codegen" -version = "0.2.4" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9661a3a53f93f09f2ea882018e4d7c88f6ff2956d809a276060476fd8c879d3c" +checksum = "da9f0f13dac8069c139e8300a6510e3f4143ecf5259c60b116a9b271b4ca0d54" dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -3356,25 +3843,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "pkg-config" -version = "0.3.27" +name = "pkcs8" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] [[package]] -name = "portable-atomic" -version = "0.3.20" +name = "pkg-config" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e30165d31df606f5726b090ec7592c308a0eaf61721ff64c9a3018e344a8753e" -dependencies = [ - "portable-atomic 1.3.2", -] +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "portable-atomic" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc59d1bcc64fc5d021d67521f818db868368028108d37f0e98d74e33f68297b5" +checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" [[package]] name = "ppv-lite86" @@ -3438,24 +3926,35 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-warning" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "proc-macro2-diagnostics" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "606c4ba35817e2922a308af55ad51bab3645b59eae5c570d4a6cf07e36bd493b" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", "version_check", "yansi", ] @@ -3491,9 +3990,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.28" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -3563,7 +4062,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", ] [[package]] @@ -3575,6 +4074,12 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "readme-rustdocifier" version = "0.1.1" @@ -3587,7 +4092,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -3596,7 +4101,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -3605,40 +4110,41 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "ref-cast" -version = "1.0.16" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" +checksum = "61ef7e18e8841942ddb1cf845054f8008410030a3997875d9e49b7a363063df1" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.16" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" +checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] name = "regex" -version = "1.8.3" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.2", + "regex-automata 0.3.4", + "regex-syntax 0.7.4", ] [[package]] @@ -3650,6 +4156,17 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -3658,9 +4175,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "rend" @@ -3686,7 +4203,7 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls 0.24.0", + "hyper-rustls 0.24.1", "ipnet", "js-sys", "log", @@ -3695,13 +4212,13 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.1", + "rustls 0.21.5", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-rustls 0.24.0", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -3711,6 +4228,16 @@ dependencies = [ "winreg", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + [[package]] name = "ring" version = "0.16.20" @@ -3756,13 +4283,12 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.29.1" +version = "1.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26bd36b60561ee1fb5ec2817f198b6fd09fa571c897a5e86d1487cfc2b096dfc" +checksum = "4a2ab0025103a60ecaaf3abf24db1db240a4e1c15837090d2c32f625ac98abea" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "borsh", - "bytecheck", "byteorder", "bytes", "num-traits", @@ -3801,11 +4327,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.14" +version = "0.36.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e4d67015953998ad0eb82887a0eb0129e18a7e2f3b7b0f6c422fddcd503d62" +checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -3815,11 +4341,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -3827,6 +4353,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.5", + "windows-sys 0.48.0", +] + [[package]] name = "rustls" version = "0.20.8" @@ -3841,9 +4380,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.1" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" +checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" dependencies = [ "log", "ring", @@ -3853,9 +4392,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -3865,18 +4404,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64 0.21.2", ] [[package]] name = "rustls-webpki" -version = "0.100.1" +version = "0.101.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" dependencies = [ "ring", "untrusted", @@ -3884,15 +4423,24 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "safe_arch" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +dependencies = [ + "bytemuck", +] [[package]] name = "same-file" @@ -3911,7 +4459,6 @@ checksum = "8dd7aca73785181cc41f0bbe017263e682b585ca660540ba569133901d013ecf" dependencies = [ "parity-scale-codec", "scale-info", - "serde", ] [[package]] @@ -3921,7 +4468,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e5527e4b3bf079d4c0b2f253418598c380722ba37ef20fac9088081407f2b6" dependencies = [ "parity-scale-codec", - "primitive-types", "scale-bits", "scale-decode-derive", "scale-info", @@ -3948,8 +4494,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15546e5efbb45f0fc2291f7e202dee8623274c5d8bbfdf9c6886cc8b44a7ced3" dependencies = [ "parity-scale-codec", - "primitive-types", - "scale-bits", "scale-encode-derive", "scale-info", "thiserror", @@ -3970,9 +4514,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.7.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b569c32c806ec3abdf3b5869fb8bf1e0d275a7c1c9b0b05603d9464632649edf" +checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" dependencies = [ "bitvec", "cfg-if", @@ -3984,9 +4528,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53012eae69e5aa5c14671942a5dd47de59d4cdcff8532a6dd0e081faf1119482" +checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -3994,31 +4538,13 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "scale-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f549769261561e6764218f847e500588f9a79a289de49ce92f9e26642a3574" -dependencies = [ - "either", - "frame-metadata", - "parity-scale-codec", - "scale-bits", - "scale-decode", - "scale-encode", - "scale-info", - "serde", - "thiserror", - "yap", -] - [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -4028,7 +4554,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" dependencies = [ "dyn-clone", - "indexmap", + "indexmap 1.9.3", "schemars_derive", "serde", "serde_json", @@ -4077,9 +4603,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -4260,6 +4786,20 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "secp256k1" version = "0.24.3" @@ -4289,11 +4829,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -4302,9 +4842,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -4312,28 +4852,28 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] @@ -4349,9 +4889,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -4360,10 +4900,11 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7f05c1d5476066defcdfacce1f52fc3cae3af1d3089727100c02ae92e5abbe0" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" dependencies = [ + "itoa", "serde", ] @@ -4391,20 +4932,20 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] name = "serde_spanned" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" dependencies = [ "serde", ] @@ -4430,7 +4971,7 @@ dependencies = [ "base64 0.13.1", "chrono", "hex", - "indexmap", + "indexmap 1.9.3", "serde", "serde_json", "time", @@ -4462,7 +5003,6 @@ dependencies = [ "serde", "serde_json", "serde_plain", - "sp-core", "tokio", "tower", "tracing", @@ -4520,9 +5060,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -4563,6 +5103,29 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simba" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + [[package]] name = "simdutf8" version = "0.1.4" @@ -4580,9 +5143,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "socket2" @@ -4609,11 +5172,44 @@ dependencies = [ "sha-1", ] +[[package]] +name = "sp-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "scale-info", + "sp-api-proc-macro", + "sp-core", + "sp-metadata-ir", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-version", + "thiserror", +] + +[[package]] +name = "sp-api-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "Inflector", + "blake2", + "expander", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "sp-application-crypto" -version = "22.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf23435a4bbd6eeec2bbbc346719ba4f3200e0ddb5f9e9f06c1724db03a8410" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "parity-scale-codec", "scale-info", @@ -4625,9 +5221,8 @@ dependencies = [ [[package]] name = "sp-arithmetic" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3d3507a803e8bc332fa290ed3015a7b51d4436ce2b836744642fc412040456" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "integer-sqrt", "num-traits", @@ -4640,12 +5235,11 @@ dependencies = [ [[package]] name = "sp-core" -version = "20.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7789372146f8ad40d0b40fad0596cb1db5771187a258eabe19b06f00767fcbd6" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "array-bytes", - "bitflags", + "bitflags 1.3.2", "blake2", "bounded-collections", "bs58", @@ -4661,6 +5255,7 @@ dependencies = [ "merlin", "parity-scale-codec", "parking_lot 0.12.1", + "paste", "primitive-types", "rand 0.8.5", "regex", @@ -4684,47 +5279,69 @@ dependencies = [ [[package]] name = "sp-core-hashing" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "blake2b_simd", "byteorder", "digest 0.10.7", - "sha2 0.10.6", + "sha2 0.10.7", "sha3", "sp-std", "twox-hash", ] +[[package]] +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "proc-macro2", + "quote", + "sp-core-hashing", + "syn 2.0.28", +] + [[package]] name = "sp-debug-derive" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62211eed9ef9dac4b9d837c56ccc9f8ee4fc49d9d9b7e6b9daf098fe173389ab" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", +] + +[[package]] +name = "sp-externalities" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std", + "sp-storage", ] [[package]] -name = "sp-externalities" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae0f275760689aaefe967943331d458cd99f5169d18364365d4cb584b246d1c" +name = "sp-inherents" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ - "environmental", + "async-trait", + "impl-trait-for-tuples", "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", "sp-std", - "sp-storage", + "thiserror", ] [[package]] name = "sp-io" -version = "22.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd3431c245992fe51b8256c838fc2e981f8d3b0afc1d1377ca7dbe0a3287a764" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "bytes", "ed25519", @@ -4749,25 +5366,32 @@ dependencies = [ [[package]] name = "sp-keystore" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "452d079f592c97369c9ca8a5083b25f146751c6b5af10cbcacc2b24dc53fd72a" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "futures", - "merlin", "parity-scale-codec", "parking_lot 0.12.1", - "schnorrkel", "sp-core", "sp-externalities", "thiserror", ] +[[package]] +name = "sp-metadata-ir" +version = "0.1.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-std", +] + [[package]] name = "sp-panic-handler" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75986cc917d897e0f6d0c848088064df4c74ccbb8f1c1848700b725f5ca7fe04" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "backtrace", "lazy_static", @@ -4776,9 +5400,8 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "23.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6220216caa67e3d931c693b06a3590dfcaa255f19bb3c3e3150f1672b8bc53f6" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "either", "hash256-std-hasher", @@ -4799,9 +5422,8 @@ dependencies = [ [[package]] name = "sp-runtime-interface" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5d0cd80200bf85b8b064238b2508b69b6146b13adf36066ec5d924825af737" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -4818,22 +5440,33 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae5b00aef477127ddb6177b3464ad1e2bdcc12ee913fc5dfc9d065c6cea89b" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "Inflector", "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", +] + +[[package]] +name = "sp-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-state-machine" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e49c3bfcc8c832c34552cd8194180cc60508c6d3d9b0b9615d6b7c3e275019" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "hash-db", "log", @@ -4852,15 +5485,13 @@ dependencies = [ [[package]] name = "sp-std" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" [[package]] name = "sp-storage" -version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad1f8c52d4700ac7bc42b3375679a6c6fc1fe876f4b40c6efdf36f933ef0291" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "impl-serde", "parity-scale-codec", @@ -4872,9 +5503,8 @@ dependencies = [ [[package]] name = "sp-tracing" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00fab60bf3d42255ce3f678903d3a2564662371c75623de4a1ffc7cac46143df" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "parity-scale-codec", "sp-std", @@ -4885,9 +5515,8 @@ dependencies = [ [[package]] name = "sp-trie" -version = "21.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58401c53c08b6ecad83acd7e14534c8bbcb3fa73e81e26685e0ac70e51b00c56" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "ahash 0.8.3", "hash-db", @@ -4907,26 +5536,52 @@ dependencies = [ "trie-root", ] +[[package]] +name = "sp-version" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std", + "sp-version-proc-macro", + "thiserror", +] + +[[package]] +name = "sp-version-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "sp-wasm-interface" -version = "13.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "153b7374179439e2aa783c66ed439bd86920c67bbc95d34c76390561972bc02f" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", "sp-std", - "wasmi", + "wasmi 0.13.2", "wasmtime", ] [[package]] name = "sp-weights" -version = "19.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123c661915e1bf328e21f8ecbe4e5247feba86f9149b782ea4348004547ce8ef" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" dependencies = [ "parity-scale-codec", "scale-info", @@ -4953,6 +5608,16 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "sqlformat" version = "0.2.1" @@ -4984,7 +5649,7 @@ dependencies = [ "atoi", "base64 0.13.1", "bigdecimal", - "bitflags", + "bitflags 1.3.2", "byteorder", "bytes", "chrono", @@ -5003,7 +5668,7 @@ dependencies = [ "hex", "hkdf", "hmac 0.12.1", - "indexmap", + "indexmap 1.9.3", "itoa", "libc", "libsqlite3-sys", @@ -5019,7 +5684,7 @@ dependencies = [ "serde", "serde_json", "sha1", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "sqlformat", "sqlx-rt", @@ -5065,9 +5730,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47a8ad42e5fc72d5b1eb104a5546937eaf39843499948bb666d6e93c62423b" +checksum = "bfc443bad666016e012538782d9e3006213a7db43e9fb1dda91657dc06a6fa08" dependencies = [ "Inflector", "num-format", @@ -5092,9 +5757,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stringprep" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +checksum = "db3737bde7edce97102e0e2b15365bf7a20bfdb5f60f4f9e8d7004258a51a8da" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -5107,103 +5772,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "substrate-bip39" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" -dependencies = [ - "hmac 0.11.0", - "pbkdf2 0.8.0", - "schnorrkel", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "subxt" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b9c4ddefcb2d87eb18a6336f65635c29208f766d0deefaa2a1a19f7426a993" +name = "substrate-api-client" +version = "0.12.0" +source = "git+https://github.com/scs/substrate-api-client?branch=polkadot-v0.9.43#6faaf232a8c5761d4ce69d0006d5312956f64bdc" dependencies = [ - "base58", - "blake2", - "derivative", - "either", + "ac-compose-macros", + "ac-node-api", + "ac-primitives", + "async-trait", + "derive_more", "frame-metadata", + "frame-support", "futures", - "getrandom 0.2.9", "hex", - "impl-serde", "jsonrpsee", + "log", + "maybe-async", "parity-scale-codec", - "parking_lot 0.12.1", - "primitive-types", - "scale-bits", - "scale-decode", - "scale-encode", - "scale-info", - "scale-value", "serde", "serde_json", "sp-core", - "sp-core-hashing", "sp-runtime", - "subxt-macro", - "subxt-metadata", - "thiserror", - "tracing", -] - -[[package]] -name = "subxt-codegen" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e924f41069e9273236398ff89662d6d336468a5d94faac812129d44547db0e7f" -dependencies = [ - "darling 0.14.4", - "frame-metadata", - "heck 0.4.1", - "hex", - "jsonrpsee", - "parity-scale-codec", - "proc-macro2", - "quote", - "scale-info", - "subxt-metadata", - "syn 1.0.109", - "thiserror", - "tokio", + "sp-runtime-interface", + "url", ] [[package]] -name = "subxt-macro" -version = "0.28.0" +name = "substrate-bip39" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced0b043a069ee039f8700d3dfda01be156e4229c82277c305bc8e79a7dd855d" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" dependencies = [ - "darling 0.14.4", - "proc-macro-error", - "subxt-codegen", - "syn 1.0.109", + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.9", + "zeroize", ] [[package]] -name = "subxt-metadata" -version = "0.28.0" +name = "subtle" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18be3b8f4308fe7369ee1df66ae59c2eca79de20eab57b0f41c75736e843300f" -dependencies = [ - "frame-metadata", - "parity-scale-codec", - "scale-info", - "sp-core-hashing", -] +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" @@ -5218,9 +5829,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -5241,9 +5852,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "ec96d2ffad078296368d46ff1cb309be1c23c513b4ab0e22a45de0185275ac96" dependencies = [ "filetime", "libc", @@ -5252,21 +5863,21 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.7" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" [[package]] name = "tempfile" -version = "3.5.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", - "fastrand", + "fastrand 2.0.0", "redox_syscall 0.3.5", - "rustix 0.37.19", - "windows-sys 0.45.0", + "rustix 0.38.4", + "windows-sys 0.48.0", ] [[package]] @@ -5286,22 +5897,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] @@ -5316,10 +5927,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +checksum = "b79eabcd964882a646b3584543ccabeae7869e9ac32a46f6f22b7a5bd405308b" dependencies = [ + "deranged", "itoa", "serde", "time-core", @@ -5334,9 +5946,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" dependencies = [ "time-core", ] @@ -5353,7 +5965,7 @@ dependencies = [ "pbkdf2 0.11.0", "rand 0.8.5", "rustc-hash", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", "unicode-normalization", "wasm-bindgen", @@ -5377,11 +5989,12 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.2" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio", @@ -5401,7 +6014,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] @@ -5427,11 +6040,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.1", + "rustls 0.21.5", "tokio", ] @@ -5472,9 +6085,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ "serde", "serde_spanned", @@ -5484,20 +6097,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.10" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "indexmap", + "indexmap 2.0.0", "serde", "serde_spanned", "toml_datetime", @@ -5522,11 +6135,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658" +checksum = "55ae70283aba8d2a8b411c695c437fe25b8b5e44e23e780662002fc72fb47a82" dependencies = [ - "bitflags", + "bitflags 2.3.3", "bytes", "futures-core", "futures-util", @@ -5565,13 +6178,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] @@ -5673,6 +6286,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "tt-call" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" + [[package]] name = "twox-hash" version = "1.6.3" @@ -5729,9 +6348,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -5793,20 +6412,20 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", - "idna 0.3.0", + "idna", "percent-encoding", ] [[package]] name = "urlencoding" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "utf8-width" @@ -5822,20 +6441,20 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.3.3" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "serde", ] [[package]] name = "validator" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ad5bf234c7d3ad1042e5252b7eddb2c4669ee23f32c7dd0e9b7705f07ef591" +checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd" dependencies = [ - "idna 0.2.3", + "idna", "lazy_static", "regex", "serde", @@ -5907,11 +6526,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -5929,9 +6547,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -5939,24 +6557,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.36" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -5966,9 +6584,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5976,22 +6594,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-instrument" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "2a47ecb37b9734d1085eaa5ae1a81e60801fd8c28d4cabdd8aedb982021918bc" +dependencies = [ + "parity-wasm", +] [[package]] name = "wasmi" @@ -6001,7 +6628,19 @@ checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" dependencies = [ "parity-wasm", "wasmi-validation", - "wasmi_core", + "wasmi_core 0.2.1", +] + +[[package]] +name = "wasmi" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e61a7006b0fdf24f6bbe8dcfdad5ca1b350de80061fb2827f31c82fbbb9565a" +dependencies = [ + "spin 0.9.8", + "wasmi_arena", + "wasmi_core 0.12.0", + "wasmparser-nostd", ] [[package]] @@ -6013,6 +6652,12 @@ dependencies = [ "parity-wasm", ] +[[package]] +name = "wasmi_arena" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" + [[package]] name = "wasmi_core" version = "0.2.1" @@ -6026,29 +6671,50 @@ dependencies = [ "num-traits", ] +[[package]] +name = "wasmi_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624e6333e861ef49095d2d678b76ebf30b06bf37effca845be7e5b87c90071b7" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + [[package]] name = "wasmparser" -version = "0.100.0" +version = "0.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" +checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" dependencies = [ - "indexmap", + "indexmap 1.9.3", "url", ] +[[package]] +name = "wasmparser-nostd" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" +dependencies = [ + "indexmap-nostd", +] + [[package]] name = "wasmtime" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a222f5fa1e14b2cefc286f1b68494d7a965f4bf57ec04c59bb62673d639af6" +checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" dependencies = [ "anyhow", "bincode", "cfg-if", - "indexmap", + "indexmap 1.9.3", "libc", "log", - "object 0.29.0", + "object 0.30.4", "once_cell", "paste", "psm", @@ -6058,30 +6724,30 @@ dependencies = [ "wasmtime-environ", "wasmtime-jit", "wasmtime-runtime", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "wasmtime-asm-macros" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4407a7246e7d2f3d8fb1cf0c72fda8dbafdb6dd34d555ae8bea0e5ae031089cc" +checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-environ" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" +checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" dependencies = [ "anyhow", "cranelift-entity", - "gimli 0.26.2", - "indexmap", + "gimli", + "indexmap 1.9.3", "log", - "object 0.29.0", + "object 0.30.4", "serde", "target-lexicon", "thiserror", @@ -6091,57 +6757,57 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffaed4f9a234ba5225d8e64eac7b4a5d13b994aeb37353cde2cbeb3febda9eaa" +checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" dependencies = [ - "addr2line 0.17.0", + "addr2line 0.19.0", "anyhow", "bincode", "cfg-if", "cpp_demangle", - "gimli 0.26.2", + "gimli", "log", - "object 0.29.0", + "object 0.30.4", "rustc-demangle", "serde", "target-lexicon", "wasmtime-environ", "wasmtime-jit-icache-coherence", "wasmtime-runtime", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "wasmtime-jit-debug" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" +checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" dependencies = [ "once_cell", ] [[package]] name = "wasmtime-jit-icache-coherence" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a28ae1e648461bfdbb79db3efdaee1bca5b940872e4175390f465593a2e54c" +checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" dependencies = [ "cfg-if", "libc", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "wasmtime-runtime" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e704b126e4252788ccfc3526d4d4511d4b23c521bf123e447ac726c14545217b" +checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" dependencies = [ "anyhow", "cc", "cfg-if", - "indexmap", + "indexmap 1.9.3", "libc", "log", "mach", @@ -6149,18 +6815,18 @@ dependencies = [ "memoffset", "paste", "rand 0.8.5", - "rustix 0.36.14", + "rustix 0.36.15", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "wasmtime-types" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" +checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" dependencies = [ "cranelift-entity", "serde", @@ -6170,9 +6836,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -6210,14 +6876,24 @@ dependencies = [ [[package]] name = "whoami" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" dependencies = [ "wasm-bindgen", "web-sys", ] +[[package]] +name = "wide" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa469ffa65ef7e0ba0f164183697b89b854253fd31aeb92358b7b6155177d62f" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "winapi" version = "0.3.9" @@ -6255,22 +6931,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.48.1", ] [[package]] @@ -6288,7 +6949,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.1", ] [[package]] @@ -6308,9 +6969,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", @@ -6407,9 +7068,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "f46aab759304e4d7b2075a9aecba26228bb073ee8c50db796b2c72c676b5d807" dependencies = [ "memchr", ] @@ -6455,15 +7116,9 @@ checksum = "735a71d46c4d68d71d4b24d03fdc2b98e38cea81730595801db779c04fe80d70" [[package]] name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - -[[package]] -name = "yap" -version = "0.10.0" +version = "1.0.0-rc" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2a7eb6d82a11e4d0b8e6bda8347169aff4ccd8235d039bba7c47482d977dcf7" +checksum = "9ee746ad3851dd3bc40e4a028ab3b00b99278d929e48957bcb2d111874a7e43e" [[package]] name = "zeroize" @@ -6482,7 +7137,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.28", ] [[package]] diff --git a/crates/builder/src/cli.rs b/crates/builder/src/cli.rs index 4888ce9..9de182e 100644 --- a/crates/builder/src/cli.rs +++ b/crates/builder/src/cli.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use clap::{Parser, Subcommand}; /// CLI configuration, provided for the [`clap`] crate. @@ -7,6 +9,10 @@ pub(crate) struct Cli { /// Selected subcommand. #[command(subcommand)] pub command: Command, + + /// Path to configuration file. + #[arg(short, long, value_parser)] + pub config: Option, } /// Available subcommands. diff --git a/crates/builder/src/commands.rs b/crates/builder/src/commands.rs index 0cfda2d..2b85c8d 100644 --- a/crates/builder/src/commands.rs +++ b/crates/builder/src/commands.rs @@ -1,3 +1,4 @@ +/// `serve` subcommand. mod serve; pub use serve::serve; diff --git a/crates/builder/src/main.rs b/crates/builder/src/main.rs index 05384c9..09c9f40 100644 --- a/crates/builder/src/main.rs +++ b/crates/builder/src/main.rs @@ -34,6 +34,9 @@ //! //! See [`log_collector`] for more details. +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + /// CLI configuration and available subcommands. mod cli; @@ -55,7 +58,9 @@ use tracing::info; /// Smart contract builder entrypoint. #[tokio::main] async fn main() -> Result<(), anyhow::Error> { - let config = Config::new()?; + let cli = Cli::parse(); + + let config = Config::new(cli.config)?; logging::init(&config); @@ -67,7 +72,7 @@ async fn main() -> Result<(), anyhow::Error> { let database = Database::connect(&config.database.url).await?; info!("database connection established"); - match Cli::parse().command { + match cli.command { Command::Serve => commands::serve(builder_config, config.storage, database).await?, } diff --git a/crates/builder/src/process/container.rs b/crates/builder/src/process/container.rs index 7780def..1062107 100644 --- a/crates/builder/src/process/container.rs +++ b/crates/builder/src/process/container.rs @@ -10,6 +10,7 @@ use bollard::{ LogOutput, RemoveContainerOptions, }, errors::Error, + image::{CreateImageOptions, ListImagesOptions}, service::MountTypeEnum, service::{ ContainerWaitResponse, HostConfig, Mount, MountVolumeOptions, @@ -20,6 +21,7 @@ use bollard::{ use common::config; use derive_more::{Display, Error, From}; use futures_util::{Stream, TryStreamExt}; +use tracing::info; use crate::process::volume::{Volume, VolumeError}; @@ -51,6 +53,31 @@ pub enum DownloadFromContainerError { FileNotFound, } +/// Supported container images. +pub enum Image<'a> { + /// Unarchive image, produced using Nix. + Unarchive, + + /// Build image, automatically downloaded from Docker registry. + Build { + /// `cargo-contract` version to use during image download process. + version: &'a str, + }, + + /// Artifact rename image, produced using Nix. + Move, +} + +impl<'a> fmt::Display for Image<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Image::Unarchive => write!(f, "stage-unarchive"), + Image::Build { version } => write!(f, "paritytech/contracts-verifiable:{version}"), + Image::Move => write!(f, "stage-move"), + } + } +} + /// A single running Docker container instance. pub struct Container { /// Docker-specific container identifier. @@ -60,32 +87,16 @@ pub struct Container { volume: Volume, } -/// Container environment variables. -pub struct Environment<'a, U: fmt::Display> { - /// Build session file upload token. - pub build_session_token: &'a str, - - /// Rust toolchain version used to build the contract. - pub rustc_version: &'a str, - - /// `cargo-contract` version used to build the contract. - pub cargo_contract_version: &'a str, - - /// S3 pre-signed URL to the source code archive. - pub source_code_url: U, - - /// API server URL used to upload the source code archive contents. - pub api_server_url: &'a str, -} - impl Container { /// Spawn new Docker container with the provided configuration. - pub async fn new( + pub async fn new( config: &config::Builder, client: &Docker, volume: Volume, - env: Environment<'_, U>, - ) -> Result { + name: &str, + image: Image<'_>, + env: Option>, + ) -> Result { // Attempt to isolate container as much as possible. // // The provided container configuration should protect @@ -99,7 +110,7 @@ impl Container { memory_swap: Some(config.memory_swap_limit), // Mount the passed volume as a home directory of a root user. mounts: Some(vec![Mount { - target: Some(String::from("/root")), + target: Some(String::from("/contract")), typ: Some(MountTypeEnum::VOLUME), volume_options: Some(MountVolumeOptions { driver_config: Some(MountVolumeOptionsDriverConfig { @@ -118,33 +129,43 @@ impl Container { ..Default::default() }; - let container = client + let image_str = image.to_string(); + + let cmd = if let Image::Build { .. } = image { + if let Err(err) = Self::ensure_image_exists(client, &image_str).await { + return Err((err, volume)); + } + + Some(vec!["build", "--release"]) + } else { + None + }; + + let container = match client .create_container( Some(CreateContainerOptions { - name: env.build_session_token, - ..Default::default() + name, + platform: Some("linux/amd64"), }), Config { - image: Some("ink-builder"), - // Pass information about the current build session to container - env: Some(vec![ - &format!("SOURCE_CODE_URL={}", env.source_code_url), - &format!("CARGO_CONTRACT_VERSION={}", env.cargo_contract_version), - &format!("RUST_VERSION={}", env.rustc_version), - &format!("BUILD_SESSION_TOKEN={}", env.build_session_token), - &format!("API_SERVER_URL={}", env.api_server_url), - ]), + image: Some(&*image_str), + cmd, + env, host_config: Some(host_config), attach_stdout: Some(true), attach_stderr: Some(true), ..Default::default() }, ) - .await?; + .await + { + Ok(container) => container, + Err(err) => return Err((err, volume)), + }; - client - .start_container::(&container.id, None) - .await?; + if let Err(err) = client.start_container::(&container.id, None).await { + return Err((err, volume)); + } Ok(Self { id: container.id, @@ -181,7 +202,7 @@ impl Container { client: &Docker, buf: &'a mut [u8], ) -> Result<&'a [u8], DownloadFromContainerError> { - self.download_from_container_to_buf(client, "/root/artifacts/ink/main.wasm", buf) + self.download_from_container_to_buf(client, "/contract/target/ink/main.wasm", buf) .await } @@ -193,7 +214,7 @@ impl Container { client: &Docker, buf: &'a mut [u8], ) -> Result<&'a [u8], DownloadFromContainerError> { - self.download_from_container_to_buf(client, "/root/artifacts/ink/main.json", buf) + self.download_from_container_to_buf(client, "/contract/target/ink/main.json", buf) .await } @@ -205,8 +226,8 @@ impl Container { client.wait_container::(&self.id, None) } - /// Remove the current Docker container and close the related [`Volume`]. - pub async fn remove(self, client: &Docker) -> Result<(), ContainerRemoveError> { + /// Remove the current Docker container and retrieve the inner [`Volume`] value. + pub async fn remove(self, client: &Docker) -> Result { client .remove_container( &self.id, @@ -218,7 +239,36 @@ impl Container { ) .await?; - self.volume.close().await?; + Ok(self.volume) + } + + /// Ensure that the image with the provided name exists. + /// + /// If it doesn't, an attempt to pull it from Docker registry will be made. + pub async fn ensure_image_exists(client: &Docker, image: &str) -> Result<(), Error> { + let list = client + .list_images(Some(ListImagesOptions { + filters: HashMap::from([("reference", vec![image])]), + ..Default::default() + })) + .await?; + + if list.is_empty() { + info!(%image, "downloading missing docker image"); + + client + .create_image( + Some(CreateImageOptions { + from_image: image, + ..Default::default() + }), + None, + None, + ) + .map_ok(|_| ()) + .try_collect::<()>() + .await?; + } Ok(()) } diff --git a/crates/builder/src/process/worker.rs b/crates/builder/src/process/worker.rs index 35df96f..94b5fce 100644 --- a/crates/builder/src/process/worker.rs +++ b/crates/builder/src/process/worker.rs @@ -1,18 +1,19 @@ -use std::{convert::identity, io, sync::Arc, time::Duration}; +use std::{sync::Arc, time::Duration}; use bollard::Docker; use common::{config, hash, s3}; use db::{ - build_session, build_session_token, code, + build_session::{self, ProcessedBuildSession}, + build_session_token, code, sea_query::{LockBehavior, LockType, OnConflict}, - source_code, ActiveValue, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, - QuerySelect, TransactionErrorExt, TransactionTrait, + source_code, ActiveValue, ColumnTrait, DatabaseConnection, DatabaseTransaction, DbErr, + EntityTrait, QueryFilter, QuerySelect, TransactionErrorExt, TransactionTrait, }; use derive_more::{Display, Error, From}; -use futures_util::{pin_mut, StreamExt}; +use futures_util::{pin_mut, StreamExt, TryFutureExt}; use itertools::Itertools; use tokio::{sync::mpsc::UnboundedSender, time::timeout}; -use tracing::{error, info, instrument}; +use tracing::{debug, error, instrument}; use crate::{ log_collector::LogEntry, @@ -20,7 +21,7 @@ use crate::{ }; use super::{ - container::{ContainerRemoveError, DownloadFromContainerError, Environment}, + container::{ContainerRemoveError, DownloadFromContainerError, Image}, volume::VolumeError, }; @@ -35,26 +36,6 @@ const UPDATE_PERIOD: Duration = Duration::from_secs(5); pub(crate) enum WorkerError { /// Database-related error. DatabaseError(DbErr), - - /// Docker-related error. - DockerError(bollard::errors::Error), - - /// IO-related error. - IoError(io::Error), - - /// S3 storage-related error. - S3Error(s3::Error), - - /// Volume-related error. - VolumeError(VolumeError), - - /// Unable to acquire a [build session token](db::build_session_token) - #[display(fmt = "missing build session token")] - MissingBuildSessionToken, - - /// Unable to find a [source code](db::source_code) related to the current build session. - #[display(fmt = "missing source code")] - MissingSourceCode, } /// Spawn a worker that will handle incoming build sessions. @@ -86,7 +67,6 @@ pub(crate) async fn spawn( .columns([ build_session::Column::Id, build_session::Column::SourceCodeId, - build_session::Column::RustcVersion, build_session::Column::CargoContractVersion, ]) .filter(build_session::Column::Status.eq(build_session::Status::New)); @@ -101,67 +81,26 @@ pub(crate) async fn spawn( .one(txn) .await? { - let archive_hash = - source_code::Entity::find_by_id(build_session.source_code_id) - .select_only() - .column(source_code::Column::ArchiveHash) - .into_tuple::>() - .one(txn) - .await? - .ok_or(WorkerError::MissingSourceCode)?; - - let token = build_session_token::Entity::find() - .select_only() - .column(build_session_token::Column::Token) - .filter( - build_session_token::Column::BuildSessionId.eq(build_session.id), - ) - .into_tuple::() - .one(txn) - .await? - .ok_or(WorkerError::MissingBuildSessionToken)?; - - let source_code_url = s3::ConfiguredClient::new(&storage_config) - .await - .get_source_code(&archive_hash) - .await?; - - let volume = - Volume::new(&builder_config.images_path, &builder_config.volume_size) - .await?; - - let container = Container::new( - &builder_config, - &docker, - volume, - Environment { - build_session_token: &token, - rustc_version: &build_session.rustc_version, - cargo_contract_version: &build_session.cargo_contract_version, - source_code_url: source_code_url.uri(), - api_server_url: &builder_config.api_server_url, - }, - ) - .await?; - let mut wasm_buf = vec![0; builder_config.wasm_size_limit]; let mut metadata_buf = vec![0; builder_config.metadata_size_limit]; - match timeout( - Duration::from_secs(builder_config.max_build_duration), - handle_session( - log_sender, - build_session.id, - &container, + let val = |wasm_buf, metadata_buf| async { + Instance::new( + &build_session, + &builder_config, &docker, - &mut wasm_buf, - &mut metadata_buf, - ), - ) - .await - .map_err(|_| SessionError::TimedOut) - .and_then(identity) - { + &storage_config, + txn, + ) + .unarchive() + .await? + .build(log_sender) + .await? + .get_files(wasm_buf, metadata_buf) + .await + }; + + match val(&mut wasm_buf, &mut metadata_buf).await { Ok((wasm, metadata)) => { let code_hash = hash::blake2(wasm); @@ -191,9 +130,7 @@ pub(crate) async fn spawn( .exec_without_returning(txn) .await?; } - Err(err) => { - info!(id = %build_session.id, ?err, "build session error"); - + Err(_) => { build_session::Entity::update_many() .filter(build_session::Column::Id.eq(build_session.id)) .col_expr( @@ -205,10 +142,6 @@ pub(crate) async fn spawn( } } - if let Err(err) = container.remove(&docker).await { - error!(?err, "unable to delete container"); - } - Ok(false) } else { Ok(true) @@ -230,9 +163,15 @@ pub(crate) async fn spawn( /// and are usually caused by an incorrect user input. #[derive(Debug, Display, Error, From)] enum SessionError { + /// Database-related error. + DatabaseError(DbErr), + /// Docker-related error. DockerError(bollard::errors::Error), + /// S3 storage-related error. + S3Error(s3::Error), + /// Volume-related error. VolumeError(VolumeError), @@ -242,6 +181,14 @@ enum SessionError { /// Unable to download files from the container. DownloadFromContainerError(DownloadFromContainerError), + /// Unable to acquire a [build session token](db::build_session_token) + #[display(fmt = "missing build session token")] + MissingBuildSessionToken, + + /// Unable to find a [source code](db::source_code) related to the current build session. + #[display(fmt = "missing source code")] + MissingSourceCode, + /// Container finished its execution with a status code. #[display(fmt = "container exited with status code {}", _0)] ContainerExited(#[error(not(source))] i64), @@ -251,20 +198,285 @@ enum SessionError { TimedOut, } +/// Archived build session instance. +struct Instance<'a> { + /// Inner build session database record. + build_session: &'a ProcessedBuildSession, + /// Builder component configuration. + builder_config: &'a config::Builder, + /// Docker RPC client. + docker: &'a Docker, + /// AWS S3 storage configuration. + storage_config: &'a config::Storage, + /// Current database transaction. + txn: &'a DatabaseTransaction, +} + +impl<'a> Instance<'a> { + /// Create new build session [`Instance`]. + fn new( + build_session: &'a ProcessedBuildSession, + builder_config: &'a config::Builder, + docker: &'a Docker, + storage_config: &'a config::Storage, + txn: &'a DatabaseTransaction, + ) -> Self { + Instance { + build_session, + builder_config, + docker, + storage_config, + txn, + } + } + + /// Unarchive user-provided files using a separately launched container instance. + /// + /// This method returns [`UnarchivedInstance`], which can be used to start the build process itself. + #[instrument(skip(self), fields(id = %self.build_session.id), err(level = "info"))] + async fn unarchive(self) -> Result, SessionError> { + let archive_hash = source_code::Entity::find_by_id(self.build_session.source_code_id) + .select_only() + .column(source_code::Column::ArchiveHash) + .into_tuple::>() + .one(self.txn) + .await? + .ok_or(SessionError::MissingSourceCode)?; + + let token = build_session_token::Entity::find() + .select_only() + .column(build_session_token::Column::Token) + .filter(build_session_token::Column::BuildSessionId.eq(self.build_session.id)) + .into_tuple::() + .one(self.txn) + .await? + .ok_or(SessionError::MissingBuildSessionToken)?; + + let source_code_url = s3::ConfiguredClient::new(self.storage_config) + .await + .get_source_code(&archive_hash) + .await?; + + debug!("creating new volume for build session"); + + let volume = Volume::new( + &self.builder_config.images_path, + &self.builder_config.volume_size, + ) + .await?; + + debug!("spawning container for the unarchiving process"); + + let container = match Container::new( + self.builder_config, + self.docker, + volume, + &format!("unarchive-{}", self.build_session.id), + Image::Unarchive, + Some(vec![ + &format!("BUILD_SESSION_TOKEN={token}"), + &format!("SOURCE_CODE_URL={}", source_code_url.uri()), + &format!("API_SERVER_URL={}", self.builder_config.api_server_url), + ]), + ) + .await + { + Ok(container) => container, + Err((err, volume)) => { + volume.close().await?; + return Err(err.into()); + } + }; + + let volume = wait_and_remove(container, self.docker, self.builder_config).await?; + + debug!("unarchiving process completed successfully"); + + Ok(UnarchivedInstance { + build_session: self.build_session, + builder_config: self.builder_config, + docker: self.docker, + volume, + }) + } +} + +/// Build session instance with unarchived user files. +struct UnarchivedInstance<'a> { + /// Inner build session database record. + build_session: &'a ProcessedBuildSession, + /// Builder component configuration. + builder_config: &'a config::Builder, + /// Docker RPC client. + docker: &'a Docker, + /// Inner volume with unarchived source code. + volume: Volume, +} + +impl<'a> UnarchivedInstance<'a> { + /// Start build process for the current build session instance. + #[instrument(skip(self, log_sender), fields(id = %self.build_session.id), err(level = "info"))] + pub async fn build( + self, + log_sender: UnboundedSender, + ) -> Result, SessionError> { + debug!("spawning container for building purposes"); + + let container = match Container::new( + self.builder_config, + self.docker, + self.volume, + &format!("build-session-{}", self.build_session.id), + Image::Build { + version: &self.build_session.cargo_contract_version, + }, + None, + ) + .await + { + Ok(container) => container, + Err((err, volume)) => { + volume.close().await?; + return Err(err.into()); + } + }; + + let volume = handle_session( + log_sender, + self.build_session.id, + container, + self.docker, + self.builder_config, + ) + .await?; + + debug!("container built successfully"); + + Ok(BuiltInstance { + build_session: self.build_session, + builder_config: self.builder_config, + docker: self.docker, + volume, + }) + } +} + +/// Build session with WASM and metadata artifacts available +struct BuiltInstance<'a> { + /// Inner build session database record. + build_session: &'a ProcessedBuildSession, + /// Builder component configuration. + builder_config: &'a config::Builder, + /// Docker RPC client. + docker: &'a Docker, + /// Inner volume with unarchived source code. + volume: Volume, +} + +impl<'a> BuiltInstance<'a> { + /// Rename artifacts files and write them into the provided buffers. + /// + /// This methods returns an [`Err`] if the provided buffers are insufficient in size to write + /// build artifacts. + #[instrument(skip(self, wasm_buf, metadata_buf), fields(id = %self.build_session.id), err(level = "info"))] + async fn get_files<'b>( + self, + wasm_buf: &'b mut [u8], + metadata_buf: &'b mut [u8], + ) -> Result<(&'b [u8], &'b [u8]), SessionError> { + debug!("spawning container for file rename purposes"); + + let container = match Container::new( + self.builder_config, + self.docker, + self.volume, + &format!("move-{}", self.build_session.id), + Image::Move, + None, + ) + .await + { + Ok(container) => container, + Err((err, volume)) => { + volume.close().await?; + return Err(err.into()); + } + }; + + let outcome = wait(&container, self.docker, self.builder_config) + .and_then(|_| async { + let wasm = container.wasm_file(self.docker, wasm_buf).await?; + let metadata = container.metadata_file(self.docker, metadata_buf).await?; + + debug!( + wasm_size = %wasm.len(), + metadata_size = %metadata.len(), + "retrieved WASM blob and JSON metadata successfully" + ); + + Ok((wasm, metadata)) + }) + .await; + + container.remove(self.docker).await?.close().await?; + + outcome + } +} + +/// Wait for the provided [`Container`] to finish running. +/// +/// This function returns an [`Err`] if container returns non-zero exit code. +async fn wait( + container: &Container, + docker: &Docker, + builder_config: &config::Builder, +) -> Result<(), SessionError> { + match timeout( + Duration::from_secs(builder_config.max_build_duration), + container.events(docker).next(), + ) + .await + .map_err(|_| SessionError::TimedOut)? + { + Some(Ok(_)) | None => Ok(()), + Some(Err(bollard::errors::Error::DockerContainerWaitError { code, .. })) => { + Err(SessionError::ContainerExited(code)) + } + Some(Err(err)) => Err(err.into()), + } +} + +/// Wait for the provided [`Container`] to finish running and automatically delete it afterwards. +/// +/// If an error occurs during the deletion process, this function will automatically attempt to close the backing [`Volume`]. +async fn wait_and_remove( + container: Container, + docker: &Docker, + builder_config: &config::Builder, +) -> Result { + let outcome = wait(&container, docker, builder_config).await; + + let volume = container.remove(docker).await?; + + if let Err(err) = outcome { + volume.close().await?; + Err(err) + } else { + Ok(volume) + } +} + /// Handle a single build session. /// -/// Returns a tuple of WASM blob bytes and JSON metadata bytes if the -/// contract build is successful, [`SessionError`] otherwise. +/// Returns the backing volume with WASM and metadata artifacts, [`SessionError`] otherwise. async fn handle_session<'a>( log_sender: UnboundedSender, build_session_id: i64, - container: &Container, + container: Container, docker: &Docker, - wasm_buf: &'a mut [u8], - metadata_buf: &'a mut [u8], -) -> Result<(&'a [u8], &'a [u8]), SessionError> { - let mut events = container.events(docker); - + builder_config: &config::Builder, +) -> Result { let logs = tokio_stream::StreamExt::chunks_timeout( container.logs(docker).await?, 10, @@ -273,6 +485,10 @@ async fn handle_session<'a>( pin_mut!(logs); + let wait_future = wait_and_remove(container, docker, builder_config); + + pin_mut!(wait_future); + loop { tokio::select! { Some(chunk) = logs.next() => { @@ -290,17 +506,8 @@ async fn handle_session<'a>( error!(%e, "unable to send log entry") } }, - Some(event) = events.next() => match event { - Ok(_) => { - let wasm = container.wasm_file(docker, wasm_buf).await?; - let metadata = container.metadata_file(docker, metadata_buf).await?; - - return Ok((wasm, metadata)); - }, - Err(bollard::errors::Error::DockerContainerWaitError { code, .. }) => { - return Err(SessionError::ContainerExited(code)); - }, - Err(err) => return Err(err.into()) + val = &mut wait_future => { + return val; } } } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 3b864de..e5d2f4b 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -11,17 +11,35 @@ blake2 = "0.10.6" byte-unit = { version = "4.0.19", default-features = false } figment = { version = "0.10.8", default-features = false, features = ["env", "toml"] } futures-util = { version = "0.3.28", optional = true } -itertools = { version = "0.10.5", optional = true } hex = "0.4.3" -pallet-contracts-primitives = { version = "23.0.0", optional = true } -parity-scale-codec = { version = "3.5.0", optional = true } +lru = { version = "0.11.0", optional = true } serde = { version = "1.0.162", features = ["derive"] } -subxt = { version = "0.28.0", optional = true } tracing-core = { version = "0.1.30", optional = true } tracing-subscriber = { version = "0.3.17", optional = true } +frame-metadata = { version = "15.1", default-features = false, features = ["v14", "serde_full", "decode"], optional = true } +parity-scale-codec = { version = "3.6.3", optional = true } +pallet-contracts = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true } +pallet-contracts-primitives = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true } +substrate-api-client = { git = "https://github.com/scs/substrate-api-client", branch = "polkadot-v0.9.43", default-features = false, features = ["jsonrpsee-client", "contracts-xt"], optional = true } + [features] logging = ["tracing-core", "tracing-subscriber"] s3 = ["aws-config", "aws-sdk-s3"] -rpc = ["futures-util", "itertools", "pallet-contracts-primitives", "parity-scale-codec", "subxt"] +rpc = [ + "lru", + "frame-metadata", + "futures-util", + "parity-scale-codec", + "pallet-contracts", + "pallet-contracts-primitives", + "sp-core", + "sp-version", + "substrate-api-client" +] test-utils = [] + +[dev-dependencies] +tokio = { version = "1", features = ["rt-multi-thread", "macros"] } diff --git a/crates/common/artifacts/astar.scale b/crates/common/artifacts/astar.scale deleted file mode 100644 index a7c93c817be6f6a32f76eb11efec0921946c416d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 193455 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 diff --git a/crates/common/src/config.rs b/crates/common/src/config.rs index 49ab705..76853f9 100644 --- a/crates/common/src/config.rs +++ b/crates/common/src/config.rs @@ -24,6 +24,7 @@ pub struct Server { pub address: SocketAddr, } +/// Implementation of [`serde`]'s deserializer for [`FromStr`] types. #[cfg(feature = "logging")] fn deserialize_from_str<'de, T, D>(deserializer: D) -> Result where @@ -113,11 +114,11 @@ fn default_metadata_size_limit() -> usize { } fn default_memory_limit() -> i64 { - n_gib_bytes!(8) as i64 + n_gib_bytes!(4) as i64 } fn default_memory_swap_limit() -> i64 { - n_gib_bytes!(8) as i64 + n_gib_bytes!(4) as i64 } fn default_volume_size() -> String { @@ -180,9 +181,9 @@ impl Config { /// See [`Env`] for more details on how to use environment variables configuration. /// /// [`Env`]: figment::providers::Env - pub fn new() -> Result { + pub fn new(path: Option) -> Result { Figment::new() - .merge(Toml::file("Config.toml")) + .merge(Toml::file(path.unwrap_or(PathBuf::from("Config.toml")))) .merge(Env::prefixed("CONFIG_").split("_")) .extract() } diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 6a62ed3..95c6e9b 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -19,6 +19,5 @@ pub mod logging; #[cfg(feature = "s3")] pub mod s3; -/// Substrate node RPC utilities. #[cfg(feature = "rpc")] pub mod rpc; diff --git a/crates/common/src/logging.rs b/crates/common/src/logging.rs index 26a6528..614937f 100644 --- a/crates/common/src/logging.rs +++ b/crates/common/src/logging.rs @@ -13,6 +13,7 @@ pub fn init(config: &Config) { let target_filters = Targets::new() .with_target("sqlx", Level::WARN) + .with_target("substrate_api_client", Level::WARN) .with_default(config.logging.level); tracing_subscriber::registry() diff --git a/crates/common/src/rpc.rs b/crates/common/src/rpc.rs new file mode 100644 index 0000000..23207e6 --- /dev/null +++ b/crates/common/src/rpc.rs @@ -0,0 +1,374 @@ +//! Substrate node RPC utilities. +//! +//! This module provides various methods for communicating with Substrate nodes +//! that support `pallet-contracts`, allowing you to query data without worrying about +//! node specifics. +//! +//! # Metadata handling +//! +//! As node developers may release new updates, we constantly check for metadata version changes +//! when querying nodes. +//! +//! When metadata version change is detected, we fetch new metadata information from a node +//! while caching it in the process. + +use std::{convert::identity, num::NonZeroUsize}; + +use frame_metadata::RuntimeMetadataPrefixed; +use futures_util::{ + stream::{self, try_unfold}, + Stream, StreamExt, TryStreamExt, +}; +use lru::LruCache; +use pallet_contracts::Determinism; +use pallet_contracts_primitives::ContractExecResult; +use parity_scale_codec::{Compact, Decode, Encode}; +use sp_core::crypto::AccountId32; +use sp_version::RuntimeVersion; +use substrate_api_client::{ + ac_compose_macros::rpc_params, + ac_node_api::{Events, Metadata, StaticEvent}, + ac_primitives::{ + Bytes, Config, PolkadotConfig, RpcParams, StorageKey, SubstrateKitchensinkConfig, H256, + }, + rpc::{Request, Subscribe}, + storage_key, Api, Error, GetChainInfo, GetStorage, +}; + +pub use parity_scale_codec; +pub use sp_core; +pub use substrate_api_client; + +/// Default page size for fetching data by storage key prefix. +pub const PAGE_SIZE: u32 = 10; + +/// WASM blob information received from an RPC node. +#[derive(Decode)] +struct PrefabWasmModule { + _instruction_weights_version: Compact, + _initial: Compact, + _maximum: Compact, + /// WASM bytecode value. + code: Vec, + _determinism: Determinism, +} + +/// Deployed contract information from an RPC node. +#[derive(Decode)] +pub struct ContractInfo { + _trie_id: Vec, + /// Code hash associated with the current contract. + pub code_hash: H256, + _storage_bytes: u32, + _storage_items: u32, + _storage_byte_deposit: u128, + _storage_item_deposit: u128, + _storage_base_deposit: u128, +} + +/// Get a [`Block`] information for the provided block hash. +/// +/// If the provided hash is [`None`], the latest block is retrieved. +/// +/// [`Block`]: Config::Block +pub async fn block( + api: &Api, + at: Option, +) -> Result::Block>, Error> { + api.get_block(at).await +} + +/// Get information on the stored code at the provided block hash. +/// +/// This method returns an asynchronous [`Stream`] of [`StorageKey`] (which can be decoded to receive the code hash value) +/// and WASM blob bytes. +pub async fn pristine_code_root( + api: &Api, + at: H256, +) -> Result)>, Error>> + '_, Error> { + let prefix = api + .get_storage_map_key_prefix("Contracts", "CodeStorage") + .await?; + + Ok(paged_key_values::<_, PrefabWasmModule, _, _>(prefix, api, at, |module| module.code).await) +} + +/// Get WASM blob for the provided code hash at the provided block hash. +/// +/// This method returns WASM blob bytes if present in the provided block. +pub async fn pristine_code( + api: &Api, + at: H256, + code_hash: H256, +) -> Result>, Error> { + api.get_storage_map::<_, PrefabWasmModule>("Contracts", "CodeStorage", code_hash, Some(at)) + .await + .map(|val| val.map(|module| module.code)) +} + +/// Get information on all available contracts at the provided block hash. +/// +/// This method returns an asynchronous [`Stream`] of [`StorageKey`] (which can be decoded to receive the contract address value) +/// and associated contract information. +pub async fn contract_info_of_root( + api: &Api, + at: H256, +) -> Result, Error>> + '_, Error> { + let prefix = api + .get_storage_map_key_prefix("Contracts", "ContractInfoOf") + .await?; + + Ok(paged_key_values(prefix, api, at, identity).await) +} + +/// Get information about the specific contract at the provided block hash. +/// +/// This method returns associated contract information if present in the provided block. +pub async fn contract_info_of( + api: &Api, + at: H256, + account_id: &AccountId32, +) -> Result, Error> { + api.get_storage_map("Contracts", "ContractInfoOf", account_id, Some(at)) + .await +} + +/// Get UNIX timestamp in milliseconds for the provided block hash. +pub async fn block_timestamp_millis( + api: &Api, + at: H256, +) -> Result { + Ok(api + .get_storage("Timestamp", "Now", Some(at)) + .await? + .expect("timestamp is always expected to be present")) +} + +/// Call the contract with the provided [`AccountId32`] and raw call data. +/// +/// Provided raw call data should match the ABI of the contract. +pub async fn call_contract( + api: &Api, + contract: AccountId32, + data: Vec, +) -> Result::Balance, ()>, Error> { + #[derive(Encode)] + pub struct CallRequest { + origin: AccountId32, + dest: AccountId32, + value: u128, + gas_limit: Option, + storage_deposit_limit: Option, + input_data: Vec, + } + + let request = CallRequest { + // Dummy address + origin: contract.clone(), + dest: contract, + value: 0, + gas_limit: None, + storage_deposit_limit: None, + input_data: data, + }; + + let mut params = RpcParams::new(); + + params + .insert("ContractsApi_call") + .map_err(|val| Error::Other(Box::new(val)))?; + params + .insert(format!("0x{}", hex::encode(request.encode()))) + .map_err(|val| Error::Other(Box::new(val)))?; + + let bytes: String = api.client().request("state_call", params).await?; + + let result = ContractExecResult::decode( + &mut &*hex::decode(bytes.strip_prefix("0x").unwrap_or(&bytes)) + .map_err(|val| Error::Other(Box::new(val)))?, + )?; + + Ok(result) +} + +/// Node metadata cache. +#[derive(Debug)] +pub struct MetadataCache { + cache: LruCache<(u32, u32, u32), Metadata>, +} + +impl MetadataCache { + /// Create new [`MetadataCache`]. + pub fn new() -> Self { + Default::default() + } + + /// Get metadata associated with the provided block hash. + /// + /// This method requests node runtime version corresponding to the provided block, + /// and either fetches it from node or retrieves from cache. + pub async fn metadata( + &mut self, + api: &Api, + at: H256, + ) -> Result { + let RuntimeVersion { + authoring_version, + spec_version, + impl_version, + .. + } = api + .client() + .request("state_getRuntimeVersion", rpc_params![at]) + .await?; + + if let Some(metadata) = self + .cache + .get(&(authoring_version, spec_version, impl_version)) + { + Ok(metadata.clone()) + } else { + let metadata_bytes: Bytes = api + .client() + .request("state_getMetadata", rpc_params![Some(at)]) + .await?; + + let runtime_metadata = + RuntimeMetadataPrefixed::decode(&mut metadata_bytes.0.as_slice())?; + let metadata: Metadata = runtime_metadata.try_into()?; + + self.cache.push( + (authoring_version, spec_version, impl_version), + metadata.clone(), + ); + + Ok(metadata) + } + } +} + +impl Default for MetadataCache { + fn default() -> Self { + Self { + cache: LruCache::new(NonZeroUsize::new(5).unwrap()), + } + } +} + +/// Fetch events associated with the provided block hash. +/// +/// Since events layout may differ between different runtime upgrades, +/// this method accepts [`MetadataCache`] to correctly query node for the corresponding metadata. +pub async fn events( + api: &Api, + at: H256, + metadata_cache: &mut MetadataCache, +) -> Result, Error> { + let key = storage_key("System", "Events"); + let event_bytes = api + .get_opaque_storage_by_key(key, Some(at)) + .await? + .ok_or(Error::BlockNotFound)?; + + Ok(Events::new( + metadata_cache.metadata(api, at).await?, + Default::default(), + event_bytes, + )) +} + +/// Contract instantiation event. +#[derive(Decode)] +pub struct Instantiated { + /// [`AccountId32`] value of the deployer. + pub deployer: AccountId32, + + /// [`AccountId32`] value of the contract itself. + pub contract: AccountId32, +} + +impl StaticEvent for Instantiated { + const PALLET: &'static str = "Contracts"; + const EVENT: &'static str = "Instantiated"; +} + +/// WASM code upload event. +#[derive(Decode)] +pub struct CodeStored { + /// Code hash value of the uploaded WASM code. + pub code_hash: H256, +} + +impl StaticEvent for CodeStored { + const PALLET: &'static str = "Contracts"; + const EVENT: &'static str = "CodeStored"; +} + +/// Associated code hash of a contract was changed. +#[derive(Decode)] +pub struct ContractCodeUpdated { + /// [`AccountId32`] value of the associated contract. + pub contract: AccountId32, + + /// New code hash value associated with the current contract. + pub new_code_hash: H256, + + /// Previous code hash value. + pub old_code_hash: H256, +} + +impl StaticEvent for ContractCodeUpdated { + const PALLET: &'static str = "Contracts"; + const EVENT: &'static str = "ContractCodeUpdated"; +} + +/// Contract termination event. +#[derive(Decode)] +pub struct Terminated { + /// [`AccountId32`] value of a contract that got terminated. + pub contract: AccountId32, + _beneficiary: AccountId32, +} + +impl StaticEvent for Terminated { + const PALLET: &'static str = "Contracts"; + const EVENT: &'static str = "Terminated"; +} + +// Get storage keys and values with the provided prefix, mapping values in process. +async fn paged_key_values T + 'static>( + prefix: StorageKey, + api: &Api, + at: H256, + map: F, +) -> impl Stream, Error>> + '_ { + try_unfold( + (None, prefix, map), + move |(start_key, prefix, mut map)| async move { + let storage_keys = api + .get_storage_keys_paged(Some(prefix.clone()), PAGE_SIZE, start_key, Some(at)) + .await?; + + if storage_keys.is_empty() { + return Ok(None); + } + + let start_key = storage_keys.last().cloned(); + + let values = stream::iter(storage_keys) + .then(move |storage_key| async move { + let value = api + .get_storage_by_key(storage_key.clone(), Some(at)) + .await? + .expect("unable to find value corresponding to the provided storage key"); + + Result::<_, Error>::Ok((storage_key, value)) + }) + .map_ok(|(key, val)| (key, map(val))) + .try_collect() + .await?; + + Result::<_, Error>::Ok(Some((values, (start_key, prefix, map)))) + }, + ) +} diff --git a/crates/common/src/rpc/mod.rs b/crates/common/src/rpc/mod.rs deleted file mode 100644 index 549ef2b..0000000 --- a/crates/common/src/rpc/mod.rs +++ /dev/null @@ -1,369 +0,0 @@ -//! Schema abstraction over Substrate node RPC. -//! -//! # Schemas -//! -//! By introducing the concept of schemas we can utilize [`subxt`]'s typed -//! RPC bindings while simultaneously providing other crates with abstraction -//! to communicate with different nodes over a unified API. -//! -//! The core component of this module is the `Schema` enum, which allows -//! users to pick a preferred schema for communication. Be aware, that a schema from one -//! network may be supported by another network, even though their names are different. -//! -//! After creating an instance of `Schema`, you may use various methods such as -//! `Schema::block`, `Schema::pristine_code`, etc. to receive relevant information -//! without worrying about node specifics. - -/// Supported schemas. -pub mod schemas; - -use std::{ - fmt::{self, Display, Formatter}, - marker::PhantomData, - pin::Pin, - str::FromStr, -}; - -use futures_util::{stream::try_unfold, Stream, TryStreamExt}; -use itertools::Itertools; -use pallet_contracts_primitives::ContractExecResult; -use parity_scale_codec::{Decode, Encode}; -use subxt::{ - blocks::Block, - client::OnlineClientT, - events::Events, - metadata::DecodeWithMetadata, - storage::{KeyIter, StorageKey}, - utils::{AccountId32, H256}, - Config, Error, -}; - -pub use parity_scale_codec; -pub use subxt; - -/// Default page size for fetching data with [`subxt`]. -pub const PAGE_SIZE: u32 = 10; - -/// Substrate node RPC schema. -pub enum Schema { - /// Astar schema, which can be initialized from an "astar" string. - Astar, - - #[doc(hidden)] - __Config(PhantomData), -} - -impl Copy for Schema {} - -impl Clone for Schema { - fn clone(&self) -> Self { - match self { - Self::Astar => Self::Astar, - _ => unreachable!(), - } - } -} - -impl FromStr for Schema { - type Err = InvalidSchema; - - fn from_str(s: &str) -> Result { - match s { - "astar" => Ok(Self::Astar), - _ => Err(InvalidSchema), - } - } -} - -/// An error that indicates that an invalid schema was provided. -#[derive(Debug)] -pub struct InvalidSchema; - -impl Display for InvalidSchema { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "invalid schema") - } -} - -impl std::error::Error for InvalidSchema {} - -/// Macro utilities to generate schema mappers. -/// -/// See methods below for usage demonstration. -macro_rules! schema_map { - (fetch $self:ident, $client:ident, $at:ident, $($network:ident, $schema:ident => $address:ident, $mapper:expr, [$($val:expr),+]),+) => { - match $self { - $(Schema::$network => { - let address = schemas::$schema::storage() - .contracts() - .$address($($val),+); - - $client.storage() - .at($at) - .fetch(&address) - .await? - .map($mapper) - }),+ - _ => unreachable!() - } - }; - - (stream $self:ident, $client:ident, $at:ident, $($network:ident, $schema:ident => $address:ident, $mapper:expr),+) => { - match $self { - $(Schema::$network => { - let address = schemas::$schema::storage() - .contracts() - .$address(); - - Box::pin( - key_iter_to_stream( - $client.storage() - .at($at) - .iter(address, PAGE_SIZE) - .await? - ) - .map_ok($mapper), - ) - }),+ - _ => unreachable!() - } - }; -} - -impl Schema { - /// Get a [`Block`] information for the provided block hash. - /// - /// If the provided hash is [`None`], the latest block is retrieved. - pub async fn block>( - &self, - client: &C, - at: Option, - ) -> Result, Error> { - if let Some(hash) = at { - client.blocks().at(hash).await - } else { - client.blocks().at_latest().await - } - } - - /// Get information on the stored code at the provided block hash. - pub async fn pristine_code_root>( - &self, - client: &C, - at: T::Hash, - ) -> Result), Error>> + Send>>, Error> - { - Ok(schema_map!( - stream self, client, at, - Astar, astar => pristine_code_root, |(key, wasm)| (key, wasm.0) - )) - } - - /// Get WASM blob for the provided code hash at the provided block hash. - pub async fn pristine_code>( - &self, - client: &C, - at: T::Hash, - code_hash: &H256, - ) -> Result>, Error> { - Ok(schema_map!( - fetch self, client, at, - Astar, astar => pristine_code, |wasm| wasm.0, [code_hash] - )) - } - - /// Get information on all available contracts at the provided block hash. - pub async fn contract_info_of_root>( - &self, - client: &C, - at: T::Hash, - ) -> Result> + Send>>, Error> - { - Ok(schema_map!( - stream self, client, at, - Astar, astar => contract_info_of_root, |(key, info)| (key, ContractInfo { code_hash: info.code_hash }) - )) - } - - /// Get information about the specific contract at the provided block hash. - pub async fn contract_info_of>( - &self, - client: &C, - at: T::Hash, - account_id: &AccountId32, - ) -> Result, Error> { - Ok(schema_map!( - fetch self, client, at, - Astar, astar => contract_info_of, |info| ContractInfo { code_hash: info.code_hash }, [account_id] - )) - } - - /// Get UNIX timestamp in milliseconds for the provided block hash. - pub async fn block_timestamp_millis>( - &self, - client: &C, - at: T::Hash, - ) -> Result { - Ok(match self { - Schema::Astar => client - .storage() - .at(at) - .fetch(&schemas::astar::storage().timestamp().now()) - .await? - .unwrap_or(0), - _ => unreachable!(), - }) - } - - /// Call the contract with the provided [`AccountId32`] and raw call data. - /// - /// Provided raw call data should match the ABI of the contract. - pub async fn call_contract>( - &self, - client: &C, - contract: AccountId32, - data: Vec, - ) -> Result, Error> { - #[derive(Encode)] - pub struct CallRequest { - origin: AccountId32, - dest: AccountId32, - value: u128, - gas_limit: Option, - storage_deposit_limit: Option, - input_data: Vec, - } - - let request = CallRequest { - // Dummy address - origin: contract.clone(), - dest: contract, - value: 0, - gas_limit: None, - storage_deposit_limit: None, - input_data: data, - }; - - let response = client - .rpc() - .state_call("ContractsApi_call", Some(&request.encode()), None) - .await?; - - Ok(ContractExecResult::decode(&mut &*response.0)?) - } - - /// Map schema-specific event types into generic contract-related events. - /// - /// This method will return only the information about the events related to - /// the passed `needle` argument. You can use [`Iterator::filter_map`] to filter - /// returned events after calling this method. - pub fn events<'e>( - &self, - events: &'e Events, - needle: ContractEvent, - ) -> Box> + 'e> { - macro_rules! boxed_events { - ($($network:ident => $schema:ident, [$($event:ident => $mapper:expr),+]),+) => { - match (self, needle) { - $($((Schema::$network, ContractEvent::$event) => { - Box::new( - events - .find::() - .map_ok($mapper) - ) - }),+),+ - _ => unreachable!() - } - }; - } - - boxed_events!( - Astar => astar, [ - CodeStored => |event| ContractEventData::CodeStored { - code_hash: event.code_hash, - }, - Instantiated => |event| ContractEventData::Instantiated { - contract: event.contract, - deployer: event.deployer, - }, - ContractCodeUpdated => |event| ContractEventData::ContractCodeUpdated { - contract: event.contract, - new_code_hash: event.new_code_hash, - }, - Terminated => |event| ContractEventData::Terminated { - contract: event.contract - } - ] - ) - } -} - -/// Generic contract info. -pub struct ContractInfo { - /// Code hash used for the contract. - pub code_hash: H256, -} - -/// Generic contract events. -/// -/// This enum is used to filter relevant events using [`Schema::events`] method. -pub enum ContractEvent { - /// Search for WASM blob uploads. - CodeStored, - - /// Search for contract instantiations. - Instantiated, - - /// Search for contract code hash updates. - ContractCodeUpdated, - - /// Search for contract terminations. - Terminated, -} - -/// Contract event data. -pub enum ContractEventData { - /// New WASM blob was uploaded. - CodeStored { - /// Code hash of the uploaded WASM blob. - code_hash: H256, - }, - - /// New contract was instantiated. - Instantiated { - /// Contract account id. - contract: AccountId32, - - /// Account id of an entity that deployed the contract. - deployer: AccountId32, - }, - - /// Contract hash was updated. - ContractCodeUpdated { - /// Related contract account id. - contract: AccountId32, - - /// New code hash for the related contract. - new_code_hash: H256, - }, - - /// Contract was terminated. - Terminated { - /// Account id of a terminated contract. - contract: AccountId32, - }, -} - -/// Transform a [`KeyIter`] into an asynchronous [`Stream`]. -fn key_iter_to_stream( - key_iter: KeyIter, -) -> impl Stream> -where - C: Config, - Client: OnlineClientT, - ReturnTy: DecodeWithMetadata, -{ - try_unfold(key_iter, |mut state| async move { - state.next().await.map(|val| val.map(|val| (val, state))) - }) -} diff --git a/crates/common/src/rpc/schemas.rs b/crates/common/src/rpc/schemas.rs deleted file mode 100644 index 5539f9f..0000000 --- a/crates/common/src/rpc/schemas.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[subxt::subxt(runtime_metadata_path = "artifacts/astar.scale")] -pub mod astar {} diff --git a/crates/db/src/build_session.rs b/crates/db/src/build_session.rs index 9301d34..a46fb1e 100644 --- a/crates/db/src/build_session.rs +++ b/crates/db/src/build_session.rs @@ -33,9 +33,6 @@ pub struct Model { /// `cargo-contract` tooling version. pub cargo_contract_version: String, - /// Rust tooling version. - pub rustc_version: String, - /// WASM blob code hash, if the contract build was successful. pub code_hash: Option>, @@ -118,6 +115,5 @@ impl ActiveModelBehavior for ActiveModel {} pub struct ProcessedBuildSession { pub id: i64, pub source_code_id: i64, - pub rustc_version: String, pub cargo_contract_version: String, } diff --git a/crates/db/src/node.rs b/crates/db/src/node.rs index 1aec7a5..4fe0481 100644 --- a/crates/db/src/node.rs +++ b/crates/db/src/node.rs @@ -1,7 +1,7 @@ //! Supported network instance. //! //! This model represents a single network with information about an RPC node, -//! its schema, last confirmed block for event client and optionally a payment contract +//! last confirmed block for event client and optionally a payment contract //! that can be used to acquire membership fees. use sea_orm::entity::prelude::*; @@ -19,9 +19,6 @@ pub struct Model { /// RPC node WebSocket URL. pub url: String, - /// Node schema used to communicate with an RPC node. - pub schema: String, - /// Payment contract address. /// /// [`None`] if node doesn't provide such a contract. diff --git a/crates/event_client/src/cli.rs b/crates/event_client/src/cli.rs index 4d4daec..b18aee3 100644 --- a/crates/event_client/src/cli.rs +++ b/crates/event_client/src/cli.rs @@ -1,8 +1,17 @@ +/// `initialize` subcommand. mod initialize; + +/// `traverse` subcommand. mod traverse; + +/// `update_contract` subcommand. mod update_contract; + +/// `watch` subcommand. mod watch; +use std::path::PathBuf; + use clap::{Parser, Subcommand}; pub use initialize::initialize; @@ -17,6 +26,10 @@ pub(crate) struct Cli { /// Selected subcommand. #[command(subcommand)] pub command: Command, + + /// Path to configuration file. + #[clap(short, long, value_parser)] + pub config: Option, } /// Supported subcommands. @@ -30,16 +43,16 @@ pub(crate) enum Command { /// Node WebSocket URL url: String, - /// Schema name, that identifies the node's ABI. - schema: String, - /// Address of a contract that accepts membership payments. #[clap(long)] payment_address: Option, }, /// Traverse old blocks of the provided node for old events. - Traverse { name: String }, + Traverse { + /// Node name. + name: String, + }, /// Update payment contract address. UpdateContract { @@ -51,5 +64,8 @@ pub(crate) enum Command { }, /// Watch node for new blocks to discover contract events. - Watch { name: String }, + Watch { + /// Node name. + name: String, + }, } diff --git a/crates/event_client/src/cli/initialize.rs b/crates/event_client/src/cli/initialize.rs index f86e816..3f6d9b8 100644 --- a/crates/event_client/src/cli/initialize.rs +++ b/crates/event_client/src/cli/initialize.rs @@ -1,15 +1,16 @@ -use std::str::FromStr; +use std::{pin::pin, str::FromStr}; use common::rpc::{ - subxt::{self, utils::AccountId32, OnlineClient, PolkadotConfig}, - InvalidSchema, Schema, PAGE_SIZE, + self, + sp_core::crypto::AccountId32, + substrate_api_client::{self, ac_primitives::Block, rpc::JsonrpseeClient, Api}, }; use db::{ code, contract, node, sea_query::OnConflict, ActiveValue, DatabaseConnection, DbErr, EntityTrait, TransactionErrorExt, TransactionTrait, }; use derive_more::{Display, Error, From}; -use futures_util::{TryFutureExt, TryStreamExt}; +use futures_util::TryStreamExt; use crate::utils::{extract_code_hash, extract_twox_account_id}; @@ -20,10 +21,8 @@ pub enum InitializeError { DatabaseError(DbErr), /// Substrate RPC-related error. - RpcError(subxt::Error), - - /// User provided invalid schema name. - Schema(InvalidSchema), + #[display(fmt = "rpc error: {:?}", _0)] + RpcError(#[error(ignore)] substrate_api_client::Error), /// Invalid payment contract account id was provided. #[display(fmt = "invalid account id for payment contract")] @@ -46,42 +45,38 @@ pub async fn initialize( database: DatabaseConnection, name: String, url: String, - schema_name: String, payment_address: Option, ) -> Result<(), InitializeError> { - let api = OnlineClient::::from_url(&url).await?; + let client = JsonrpseeClient::new(&url).map_err(substrate_api_client::Error::RpcClient)?; + let api = Api::new(client).await?; - let schema = Schema::from_str(&schema_name)?; + let latest_block = rpc::block(&api, None) + .await? + .expect("at least one block is expected"); - let latest_block = schema.block(&api, None).await?; + let block_hash = latest_block.hash(); let payment_address = payment_address .as_deref() .map(AccountId32::from_str) .transpose() .map_err(|_| InitializeError::InvalidPaymentAddress)? - .map(|addr| addr.0.to_vec()); - - // Attempt to add all the information in a single transaction. - // - // Confirmed block value is set to the value of latest block number - // we acquired earlier. - database - .transaction(|txn| { + .map(|addr| <[u8; 32]>::from(addr).to_vec()); + + let node = database + .transaction::<_, _, InitializeError>(|txn| { Box::pin(async move { let node = node::Entity::insert(node::ActiveModel { name: ActiveValue::Set(name), url: ActiveValue::Set(url), - schema: ActiveValue::Set(schema_name), payment_contract: ActiveValue::Set(payment_address), - confirmed_block: ActiveValue::Set(latest_block.number() as i64), + confirmed_block: ActiveValue::Set(latest_block.header.number as i64), ..Default::default() }) .on_conflict( OnConflict::column(node::Column::Name) .update_columns([ node::Column::Url, - node::Column::Schema, node::Column::PaymentContract, node::Column::ConfirmedBlock, ]) @@ -90,62 +85,67 @@ pub async fn initialize( .exec_with_returning(txn) .await?; - schema - .pristine_code_root(&api, latest_block.hash()) - .await? - .err_into::() - .map_ok(|(key, wasm)| code::ActiveModel { - hash: ActiveValue::Set(extract_code_hash(key)), - code: ActiveValue::Set(wasm), - }) - .try_chunks(PAGE_SIZE as usize) - .map_err(|err| err.1) - .and_then(|chunk| { - code::Entity::insert_many(chunk) - .on_conflict( - OnConflict::column(code::Column::Hash) - .do_nothing() - .to_owned(), - ) - .exec_without_returning(txn) - .map_ok(|_| ()) - .err_into() - }) - .try_collect() + Ok(node) + }) + }) + .await + .into_raw_result()?; + + let mut wasm_blobs = pin!(rpc::pristine_code_root(&api, block_hash).await?); + + while let Some(chunk) = wasm_blobs.try_next().await? { + database + .transaction::<_, _, InitializeError>(|txn| { + Box::pin(async move { + code::Entity::insert_many(chunk.into_iter().map(|(key, wasm)| { + code::ActiveModel { + hash: ActiveValue::Set(extract_code_hash(key)), + code: ActiveValue::Set(wasm), + } + })) + .on_conflict( + OnConflict::column(code::Column::Hash) + .do_nothing() + .to_owned(), + ) + .exec_without_returning(txn) .await?; - schema - .contract_info_of_root(&api, latest_block.hash()) - .await? - .err_into::() - .map_ok(|(key, contract)| contract::ActiveModel { - code_hash: ActiveValue::Set(contract.code_hash.0.to_vec()), - node_id: ActiveValue::Set(node.id), - address: ActiveValue::Set(extract_twox_account_id(key)), - ..Default::default() - }) - .try_chunks(PAGE_SIZE as usize) - .map_err(|err| err.1) - .and_then(|chunk| { - contract::Entity::insert_many(chunk) - .on_conflict( - OnConflict::columns([ - contract::Column::NodeId, - contract::Column::Address, - ]) - .do_nothing() - .to_owned(), - ) - .exec_without_returning(txn) - .map_ok(|_| ()) - .err_into() - }) - .try_collect() + Ok(()) + }) + }) + .await + .into_raw_result()?; + } + + let mut contracts = pin!(rpc::contract_info_of_root(&api, block_hash).await?); + + while let Some(chunk) = contracts.try_next().await? { + database + .transaction::<_, _, InitializeError>(|txn| { + Box::pin(async move { + contract::Entity::insert_many(chunk.into_iter().map(|(key, contract)| { + contract::ActiveModel { + code_hash: ActiveValue::Set(contract.code_hash.0.to_vec()), + node_id: ActiveValue::Set(node.id), + address: ActiveValue::Set(extract_twox_account_id(key)), + ..Default::default() + } + })) + .on_conflict( + OnConflict::columns([contract::Column::NodeId, contract::Column::Address]) + .do_nothing() + .to_owned(), + ) + .exec_without_returning(txn) .await?; - Ok(()) + Ok(()) + }) }) - }) - .await - .into_raw_result() + .await + .into_raw_result()?; + } + + Ok(()) } diff --git a/crates/event_client/src/cli/traverse.rs b/crates/event_client/src/cli/traverse.rs index da64609..34c2730 100644 --- a/crates/event_client/src/cli/traverse.rs +++ b/crates/event_client/src/cli/traverse.rs @@ -1,8 +1,13 @@ -use std::str::FromStr; - use common::rpc::{ - subxt::{self, Config, Error, OnlineClient, PolkadotConfig}, - ContractEvent, ContractEventData, InvalidSchema, Schema, + self, + sp_core::{ByteArray, H256}, + substrate_api_client::{ + self, + ac_primitives::PolkadotConfig, + rpc::{JsonrpseeClient, Request}, + Api, Error, + }, + Instantiated, MetadataCache, }; use db::{ contract, node, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, @@ -21,10 +26,8 @@ pub enum TraverseError { DatabaseError(DbErr), /// Substrate RPC-related error. - RpcError(subxt::Error), - - /// User provided invalid schema name. - Schema(InvalidSchema), + #[display(fmt = "rpc error: {:?}", _0)] + RpcError(#[error(ignore)] substrate_api_client::Error), /// The provided node name is incorrect. #[display(fmt = "node not found")] @@ -50,24 +53,30 @@ pub async fn traverse(database: DatabaseConnection, name: String) -> Result<(), .await? .ok_or(TraverseError::NodeNotFound)?; - let api = OnlineClient::::from_url(&node.url).await?; - - let schema = Schema::from_str(&node.schema)?; + let client = JsonrpseeClient::new(&node.url).map_err(substrate_api_client::Error::RpcClient)?; + let api = Api::new(client).await?; - let stream = block_mapping_stream(0..=node.confirmed_block as u64, &api); + let stream = block_mapping_stream(0..=node.confirmed_block as u32, &api); pin_mut!(stream); + let mut metadata_cache = MetadataCache::new(); + while let Some((_, block_hash)) = stream.try_next().await? { - if let Ok(block_data) = parse_block(&api, &schema, block_hash).await { + if let Ok(block_data) = parse_block(&api, block_hash, &mut metadata_cache).await { database .transaction::<_, _, TraverseError>(|txn| { Box::pin(async move { - for (contract, deployer) in block_data.instantiations { + for instantiation in block_data.instantiations { contract::Entity::update_many() - .col_expr(contract::Column::Owner, (&deployer[..]).into()) + .col_expr( + contract::Column::Owner, + (instantiation.deployer.as_slice()).into(), + ) .filter(contract::Column::NodeId.eq(node.id)) - .filter(contract::Column::Address.eq(&contract[..])) + .filter( + contract::Column::Address.eq(instantiation.contract.as_slice()), + ) .exec(txn) .await?; } @@ -86,26 +95,18 @@ pub async fn traverse(database: DatabaseConnection, name: String) -> Result<(), /// Parsed block data. struct BlockData { /// Smart contract instantiations found in block. - instantiations: Vec<([u8; 32], [u8; 32])>, + instantiations: Vec, } -/// Attempt to parse block associated with the provided block hash using the provided schema. -async fn parse_block( - api: &OnlineClient, - schema: &Schema, - block_hash: T::Hash, +/// Attempt to parse block associated with the provided block hash. +async fn parse_block( + api: &Api, + block_hash: H256, + metadata_cache: &mut MetadataCache, ) -> Result { - let events = schema.block(api, Some(block_hash)).await?.events().await?; - - let instantiations = schema - .events(&events, ContractEvent::Instantiated) - .filter_map_ok(|event| match event { - ContractEventData::Instantiated { contract, deployer } => { - Some((contract.0, deployer.0)) - } - _ => None, - }) - .try_collect()?; + let events = rpc::events(api, block_hash, metadata_cache).await?; + + let instantiations = events.find().try_collect()?; Ok(BlockData { instantiations }) } diff --git a/crates/event_client/src/cli/update_contract.rs b/crates/event_client/src/cli/update_contract.rs index 9edcd5f..a51cb8d 100644 --- a/crates/event_client/src/cli/update_contract.rs +++ b/crates/event_client/src/cli/update_contract.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use common::rpc::subxt::utils::AccountId32; +use common::rpc::sp_core::crypto::AccountId32; use db::{ node, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, TransactionErrorExt, TransactionTrait, @@ -36,7 +36,7 @@ pub async fn update_contract( .map(AccountId32::from_str) .transpose() .map_err(|_| UpdateContractError::InvalidPaymentAddress)? - .map(|addr| addr.0.to_vec()); + .map(|addr| <[u8; 32]>::from(addr).to_vec()); database .transaction(|txn| { diff --git a/crates/event_client/src/cli/watch.rs b/crates/event_client/src/cli/watch.rs index 878fe3d..0ef6af8 100644 --- a/crates/event_client/src/cli/watch.rs +++ b/crates/event_client/src/cli/watch.rs @@ -1,11 +1,15 @@ -use std::{ - future::{self, ready}, - str::FromStr, -}; +use std::{future::ready, iter}; use common::rpc::{ - subxt::{self, blocks::Block, config::Header, Config, OnlineClient, PolkadotConfig}, - ContractEvent, ContractEventData, InvalidSchema, Schema, + self, + sp_core::ByteArray, + substrate_api_client::{ + self, + ac_primitives::{Block, Config, Header, PolkadotConfig}, + rpc::{HandleSubscription, JsonrpseeClient, Request}, + Api, GetChainInfo, SubscribeChain, + }, + CodeStored, ContractCodeUpdated, Instantiated, MetadataCache, Terminated, }; use db::{ code, contract, event, node, sea_query::OnConflict, ActiveModelTrait, ActiveValue, ColumnTrait, @@ -13,7 +17,7 @@ use db::{ TransactionErrorExt, TransactionTrait, }; use derive_more::{Display, Error, From}; -use futures_util::{pin_mut, stream::FuturesUnordered, TryStreamExt}; +use futures_util::{pin_mut, stream, TryStreamExt}; use itertools::Itertools; use tracing::{debug, info}; @@ -26,14 +30,12 @@ pub enum WatchError { DatabaseError(DbErr), /// Substrate RPC-related error. - RpcError(subxt::Error), + #[display(fmt = "rpc error: {:?}", _0)] + RpcError(#[error(ignore)] substrate_api_client::Error), /// JSON serialization error. JsonError(serde_json::Error), - /// User provided invalid schema name. - Schema(InvalidSchema), - /// The provided node name is incorrect. #[display(fmt = "node not found")] NodeNotFound, @@ -58,37 +60,46 @@ pub async fn watch(database: DatabaseConnection, name: String) -> Result<(), Wat .await? .ok_or(WatchError::NodeNotFound)?; - let api = OnlineClient::::from_url(&node.url).await?; + let client = JsonrpseeClient::new(&node.url).map_err(substrate_api_client::Error::RpcClient)?; + let api = Api::::new(client).await?; - let schema = Schema::from_str(&node.schema)?; + let mut metadata_cache = MetadataCache::new(); - let subscription = api.blocks().subscribe_finalized().await?; + let mut subscription = api.subscribe_finalized_heads()?; // Attempt to catch-up to the latest block. info!("attempting to catch-up to the latest block"); - let latest = api.blocks().at_latest().await?; + let latest = api + .get_block(None) + .await? + .expect("at least one block is expected"); let stream = block_mapping_stream( - (node.confirmed_block + 1) as u64..=latest.number() as u64, + (node.confirmed_block + 1) as u32..=latest.header.number, &api, ) - .and_then(|(_, hash)| api.blocks().at(hash)); + .try_filter_map(|(_, hash)| rpc::block(&api, Some(hash))); pin_mut!(stream); while let Some(block) = stream.try_next().await? { - debug!(block_number = %block.number(), "found a block to catch-up to"); - node = process_block(schema, node, &database, &api, block).await?; + debug!(block_number = %block.header().number(), "found a block to catch-up to"); + node = process_block(node, &database, &api, block.header(), &mut metadata_cache).await?; } // Proceed with the subscription, since an attempt to traverse missed blocks was already made. info!("processing new blocks from now on"); - let confirmed_block = node.confirmed_block; - let mut stream = - subscription.try_filter(|block| future::ready(block.number() as i64 > confirmed_block)); - while let Some(block) = stream.try_next().await? { - debug!(block_number = %block.number(), "found new block"); - node = process_block(schema, node, &database, &api, block).await?; + let confirmed_block = node.confirmed_block as u32; + let mut subscription_iter = + iter::from_fn(|| subscription.next()).filter_ok(|header| header.number() > confirmed_block); + + while let Some(header) = subscription_iter + .next() + .transpose() + .map_err(substrate_api_client::Error::RpcClient)? + { + debug!(block_number = %header.number(), "found new block"); + node = process_block(node, &database, &api, &header, &mut metadata_cache).await?; } Ok(()) @@ -99,44 +110,33 @@ pub async fn watch(database: DatabaseConnection, name: String) -> Result<(), Wat /// /// Returns new [`node::Model`], which represents an updated node /// with up-to-date confirmed block counter. -async fn process_block( - schema: Schema, +async fn process_block( node: node::Model, database: &DatabaseConnection, - api: &OnlineClient, - block: Block>, -) -> Result -where - T: Config + Send + Sync, - T::Header: Header, -{ + api: &Api, + block_header: &::Header, + metadata_cache: &mut MetadataCache, +) -> Result { let mut active_node: node::ActiveModel = node.clone().into(); - let block_millis = schema.block_timestamp_millis(api, block.hash()).await?; + let block_hash = block_header.hash(); + let block_number = block_header.number(); + + let block_millis = rpc::block_timestamp_millis(api, block_hash).await?; let raw_timestamp = unix_ts::Timestamp::from_millis(block_millis); let offset_timestamp = OffsetDateTime::from_unix_timestamp(raw_timestamp.seconds()) .expect("invalid timestamp was provided"); let block_timestamp = PrimitiveDateTime::new(offset_timestamp.date(), offset_timestamp.time()); - let events = block.events().await?; + let events = rpc::events(api, block_hash, metadata_cache).await?; - let code_uploads = schema - .events(&events, ContractEvent::CodeStored) - .filter_map_ok(|event| match event { - ContractEventData::CodeStored { code_hash } => Some(code_hash), - _ => None, - }) - .map_ok(|code_hash| { - let hash = block.hash(); - - async move { - schema - .pristine_code(api, hash, &code_hash) - .await - .map(|code| (code_hash.0, code)) - } + let code_uploads = stream::iter(events.find::()) + .err_into() + .and_then(|CodeStored { code_hash }| async move { + rpc::pristine_code(api, block_hash, code_hash) + .await + .map(|code| (code_hash.0, code)) }) - .try_collect::<_, FuturesUnordered<_>, _>()? .try_filter_map(|(hash, code)| ready(Ok(code.map(|val| (hash, val))))) .map_ok(|(hash, code)| code::ActiveModel { hash: ActiveValue::Set(hash.to_vec()), @@ -145,50 +145,43 @@ where .try_collect::>() .await?; - let instantiations = schema - .events(&events, ContractEvent::Instantiated) - .filter_map_ok(|event| match event { - ContractEventData::Instantiated { contract, deployer } => Some((contract, deployer)), - _ => None, - }) - .map_ok(|(contract, deployer)| async { - schema - .contract_info_of(api, block.hash(), &contract) + let instantiations = stream::iter(events.find::()) + .err_into() + .and_then(|Instantiated { deployer, contract }| async move { + rpc::contract_info_of(api, block_hash, &contract) .await .map(|info| (contract, deployer, info)) }) - .try_collect::<_, FuturesUnordered<_>, _>()? .try_filter_map(|(contract, deployer, info)| { ready(Ok(info.map(|val| (contract, deployer, val)))) }) .map_ok(|(contract, deployer, info)| contract::ActiveModel { code_hash: ActiveValue::Set(info.code_hash.0.to_vec()), node_id: ActiveValue::Set(node.id), - address: ActiveValue::Set(contract.0.to_vec()), - owner: ActiveValue::Set(Some(deployer.0.to_vec())), + address: ActiveValue::Set(contract.as_slice().to_vec()), + owner: ActiveValue::Set(Some(deployer.as_slice().to_vec())), ..Default::default() }) .try_collect::>() .await?; - let code_hash_updates: Vec<_> = schema - .events(&events, ContractEvent::ContractCodeUpdated) - .filter_map_ok(|event| match event { - ContractEventData::ContractCodeUpdated { - contract, - new_code_hash, - } => Some((contract.0, new_code_hash.0)), - _ => None, - }) - .try_collect()?; - - let terminations: Vec<_> = schema - .events(&events, ContractEvent::Terminated) - .filter_map_ok(|event| match event { - ContractEventData::Terminated { contract } => Some(contract.0), - _ => None, - }) - .try_collect()?; + let code_hash_updates: Vec<_> = events + .find::() + .map_ok( + |ContractCodeUpdated { + contract, + new_code_hash, + .. + }| { (contract, new_code_hash) }, + ) + .try_collect() + .map_err(substrate_api_client::Error::NodeApi)?; + + let terminations: Vec<_> = events + .find::() + .map_ok(|Terminated { contract, .. }| contract) + .try_collect() + .map_err(substrate_api_client::Error::NodeApi)?; database .transaction(|txn| { @@ -237,7 +230,7 @@ where for (contract, new_code_hash) in code_hash_updates { event::ActiveModel { node_id: ActiveValue::Set(node.id), - account: ActiveValue::Set(contract.to_vec()), + account: ActiveValue::Set(contract.as_slice().to_vec()), event_type: ActiveValue::Set(event::EventType::CodeHashUpdate), body: ActiveValue::Set(serde_json::to_string( &event::EventBody::CodeHashUpdate { @@ -253,7 +246,7 @@ where contract::Entity::update_many() .col_expr(contract::Column::CodeHash, (&new_code_hash[..]).into()) .filter(contract::Column::NodeId.eq(node.id)) - .filter(contract::Column::Address.eq(&contract[..])) + .filter(contract::Column::Address.eq(contract.as_slice())) .exec(txn) .await?; } @@ -264,7 +257,7 @@ where event::Entity::insert_many(terminations.iter().map(|model| { event::ActiveModel { node_id: ActiveValue::Set(node.id), - account: ActiveValue::Set(model.to_vec()), + account: ActiveValue::Set(model.as_slice().to_vec()), event_type: ActiveValue::Set(event::EventType::Termination), body: ActiveValue::Set(termination_body.clone()), block_timestamp: ActiveValue::Set(block_timestamp), @@ -278,13 +271,13 @@ where .filter(contract::Column::NodeId.eq(node.id)) .filter( contract::Column::Address - .is_in(terminations.iter().map(|val| &val[..])), + .is_in(terminations.iter().map(|val| val.as_slice())), ) .exec(txn) .await?; } - active_node.confirmed_block = ActiveValue::Set(block.number() as i64); + active_node.confirmed_block = ActiveValue::Set(block_number as i64); Ok(active_node.update(txn).await?) }) diff --git a/crates/event_client/src/main.rs b/crates/event_client/src/main.rs index f4606fb..312fe9b 100644 --- a/crates/event_client/src/main.rs +++ b/crates/event_client/src/main.rs @@ -41,6 +41,9 @@ //! [`traverse`]: cli::traverse //! [`update_contract`]: cli::update_contract +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + /// CLI general configuration and subcommands. mod cli; @@ -56,7 +59,9 @@ use tracing::info; /// Event client entrypoint. #[tokio::main] async fn main() -> Result<(), anyhow::Error> { - let config = Config::new()?; + let cli = Cli::parse(); + + let config = Config::new(cli.config)?; logging::init(&config); @@ -64,13 +69,12 @@ async fn main() -> Result<(), anyhow::Error> { let database = Database::connect(&config.database.url).await?; info!("database connection established"); - match Cli::parse().command { + match cli.command { Command::Initialize { name, url, - schema, payment_address, - } => cli::initialize(database, name, url, schema, payment_address).await?, + } => cli::initialize(database, name, url, payment_address).await?, Command::Traverse { name } => cli::traverse(database, name).await?, Command::UpdateContract { name, diff --git a/crates/event_client/src/utils.rs b/crates/event_client/src/utils.rs index e74ba4b..245caa4 100644 --- a/crates/event_client/src/utils.rs +++ b/crates/event_client/src/utils.rs @@ -1,8 +1,6 @@ -use common::rpc::subxt::{ - self, - client::OnlineClientT, - rpc::types::{BlockNumber, NumberOrHex}, - Config, +use common::rpc::{ + sp_core::H256, + substrate_api_client::{ac_primitives::PolkadotConfig, rpc::Request, Api, Error, GetChainInfo}, }; use futures_util::{stream, Stream, StreamExt, TryStreamExt}; @@ -37,21 +35,15 @@ pub(crate) fn extract_code_hash>(key: T) -> Vec { /// Get a mapping stream from block number to block hash. /// /// The stream may skip blocks, to which an RPC node did not provide a hash. -pub(crate) fn block_mapping_stream< - 'a, - I: IntoIterator + 'a, - T: Config, - C: OnlineClientT, ->( +pub(crate) fn block_mapping_stream<'a, I: IntoIterator + 'a, C: Request>( range: I, - api: &'a C, -) -> impl Stream> + 'a { + api: &'a Api, +) -> impl Stream> + 'a { stream::iter(range.into_iter()) .map(Ok) .try_filter_map(move |block_number| async move { Ok(api - .rpc() - .block_hash(Some(BlockNumber::from(NumberOrHex::from(block_number)))) + .get_block_hash(Some(block_number)) .await? .map(|hash| (block_number, hash))) }) @@ -59,9 +51,9 @@ pub(crate) fn block_mapping_stream< #[cfg(test)] mod tests { - use common::rpc::subxt::ext::{ - sp_core::ByteArray, - sp_runtime::{app_crypto::Ss58Codec, AccountId32}, + use common::rpc::sp_core::{ + crypto::{AccountId32, Ss58Codec}, + ByteArray, }; #[test] diff --git a/crates/migration/src/cli.rs b/crates/migration/src/cli.rs index f58a6db..52cd4f6 100644 --- a/crates/migration/src/cli.rs +++ b/crates/migration/src/cli.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use clap::Parser; use sea_orm_cli::MigrateSubcommands; @@ -5,4 +7,8 @@ use sea_orm_cli::MigrateSubcommands; pub(crate) struct Cli { #[clap(subcommand)] pub command: Option, + + /// Path to configuration file. + #[clap(short, long, value_parser)] + pub config: Option, } diff --git a/crates/migration/src/lib.rs b/crates/migration/src/lib.rs index e01087b..5e62ffc 100644 --- a/crates/migration/src/lib.rs +++ b/crates/migration/src/lib.rs @@ -13,6 +13,8 @@ mod m20220101_000010_create_build_session_tokens_table; mod m20220101_000011_create_logs_table; mod m20220101_000012_create_cli_tokens_table; mod m20220101_000013_create_events_table; +mod m20220101_000014_remove_node_schema; +mod m20220101_000015_remove_rust_version; pub(crate) use m20220101_000001_create_users_table::Users; pub(crate) use m20220101_000003_create_authentication_tokens_table::AuthenticationTokens; @@ -39,6 +41,8 @@ impl MigratorTrait for Migrator { Box::new(m20220101_000011_create_logs_table::Migration), Box::new(m20220101_000012_create_cli_tokens_table::Migration), Box::new(m20220101_000013_create_events_table::Migration), + Box::new(m20220101_000014_remove_node_schema::Migration), + Box::new(m20220101_000015_remove_rust_version::Migration), ] } } diff --git a/crates/migration/src/m20220101_000014_remove_node_schema.rs b/crates/migration/src/m20220101_000014_remove_node_schema.rs new file mode 100644 index 0000000..451bcb1 --- /dev/null +++ b/crates/migration/src/m20220101_000014_remove_node_schema.rs @@ -0,0 +1,36 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(Nodes::Table) + .drop_column(Nodes::Schema) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(Nodes::Table) + .add_column(ColumnDef::new(Nodes::Schema).string().not_null()) + .to_owned(), + ) + .await + } +} + +/// Learn more at https://docs.rs/sea-query#iden +#[derive(Iden)] +enum Nodes { + Table, + Schema, +} diff --git a/crates/migration/src/m20220101_000015_remove_rust_version.rs b/crates/migration/src/m20220101_000015_remove_rust_version.rs new file mode 100644 index 0000000..d9469a1 --- /dev/null +++ b/crates/migration/src/m20220101_000015_remove_rust_version.rs @@ -0,0 +1,54 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(BuildSessions::Table) + .drop_column(BuildSessions::RustcVersion) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(BuildSessions::Table) + .add_column( + ColumnDef::new(BuildSessions::RustcVersion) + .string() + .not_null() + .default("0.0.0"), + ) + .to_owned(), + ) + .await?; + + manager + .alter_table( + Table::alter() + .table(BuildSessions::Table) + .modify_column( + ColumnDef::new(BuildSessions::RustcVersion) + .string() + .not_null(), + ) + .to_owned(), + ) + .await + } +} + +/// Learn more at https://docs.rs/sea-query#iden +#[derive(Iden)] +pub(crate) enum BuildSessions { + Table, + RustcVersion, +} diff --git a/crates/migration/src/main.rs b/crates/migration/src/main.rs index b0afc35..7a96730 100644 --- a/crates/migration/src/main.rs +++ b/crates/migration/src/main.rs @@ -12,7 +12,7 @@ use tracing::info; async fn main() -> Result<(), Box> { let cli = Cli::parse(); - let config = Config::new()?; + let config = Config::new(cli.config)?; info!("connecting to database"); let db = Database::connect(&config.database.url).await?; diff --git a/crates/patron/Cargo.toml b/crates/patron/Cargo.toml index 7586740..43ea2e7 100644 --- a/crates/patron/Cargo.toml +++ b/crates/patron/Cargo.toml @@ -17,6 +17,7 @@ open = "4.1.0" rand = "0.8.5" reqwest = { version = "0.11.17", default-features = false, features = ["blocking", "json", "multipart", "rustls-tls-webpki-roots"] } serde = { version = "1.0.163", features = ["derive"] } +serde_json = "1.0.97" tempfile = "3.5.0" toml = { version = "0.7.3", default-features = false, features = ["display"] } walkdir = "2.3.3" diff --git a/crates/patron/src/commands.rs b/crates/patron/src/commands.rs index 1f5c92d..4c5fd32 100644 --- a/crates/patron/src/commands.rs +++ b/crates/patron/src/commands.rs @@ -1,11 +1,19 @@ /// `auth` subcommand. mod auth; +/// `build` subcommand. +mod build; + /// `deploy` subcommand. mod deploy; +/// `verify` subcommand. +mod verify; + pub(crate) use auth::auth; +pub(crate) use build::build; pub(crate) use deploy::deploy; +pub(crate) use verify::verify; use std::path::PathBuf; @@ -30,8 +38,14 @@ pub(crate) enum Commands { /// Authenticate using the browser flow. Auth(Auth), - /// Start the deployment process. + /// Start the build and deployment process. Deploy(Deploy), + + /// Build the contract remotely without the initial deployment. + Build(Build), + + /// Verify remotely built contract with locally built one. + Verify(Verify), } /// `auth` subcommand configuration. @@ -81,3 +95,31 @@ pub struct Deploy { #[clap(allow_hyphen_values = true)] cargo_contract_flags: Vec, } + +/// `build` subcommand configuration. +#[derive(Args)] +pub struct Build { + /// Always start new build sessions, even if the source code was verified previously. + #[arg(short, long)] + force_new_build_sessions: bool, + + /// Path where to output a newly built contract WASM blob. + #[arg(short, long)] + wasm_path: Option, + + /// Path where to output a newly built contract JSON metadata. + #[arg(short, long)] + metadata_path: Option, + + /// Path where to output a bundled JSON, which contains both WASM and metadata. + #[arg(short, long)] + bundle_path: Option, +} + +/// `verify` subcommand configuration. +#[derive(Args)] +pub struct Verify { + /// Always start new build sessions, even if the source code was verified previously. + #[arg(short, long)] + force_new_build_sessions: bool, +} diff --git a/crates/patron/src/commands/build.rs b/crates/patron/src/commands/build.rs new file mode 100644 index 0000000..0b5a1e2 --- /dev/null +++ b/crates/patron/src/commands/build.rs @@ -0,0 +1,119 @@ +use std::{ + fs::{self, File}, + io::{self, Read, Seek, SeekFrom}, + path::PathBuf, +}; + +use derive_more::{Display, Error, From}; +use indicatif::ProgressBar; +use serde_json::Value; +use tempfile::PersistError; + +use crate::{ + commands::Build, + config::{AuthenticationConfig, AuthenticationConfigError, ProjectConfig}, + process::{remote_build, FinishedBuildSession, RemoteBuildError}, +}; + +/// Directory, where build artifacts will be stored. +const TARGET_DIR: &str = "./target/ink"; + +/// Default path used to save WASM blob. +const DEFAULT_WASM_PATH: &str = "./target/ink/contract.wasm"; + +/// Default path used to save JSON metadata. +const DEFAULT_METADATA_PATH: &str = "./target/ink/contract.json"; + +/// Default path used to save bundled contract file. +const DEFAULT_BUNDLE_PATH: &str = "./target/ink/bundle.contract"; + +/// `build` subcommand errors. +#[derive(Debug, Display, From, Error)] +pub(crate) enum BuildError { + /// Authentication configuration error. + Authentication(AuthenticationConfigError), + + /// Unable to parse the project configuration with [`figment`]. + Figment(figment::Error), + + /// IO-related error. + Io(io::Error), + + /// Metadata JSON parsing error. + Json(serde_json::Error), + + /// Remote build process error. + BuildProcessError(RemoteBuildError), + + /// Unable to move temporary files onto a new location. + PersistError(PersistError), + + /// Invalid metadata object. + #[display(fmt = "unable to retrieve the 'source' key from the metadata JSON")] + InvalidMetadataObject, +} + +/// Build flow entrypoint. +pub(crate) fn build( + Build { + force_new_build_sessions, + wasm_path, + metadata_path, + bundle_path, + }: Build, +) -> Result<(), BuildError> { + let auth_config = AuthenticationConfig::new()?; + let project_config = ProjectConfig::new()?; + + let progress = ProgressBar::new_spinner(); + + let FinishedBuildSession { + mut wasm_file, + mut metadata_file, + .. + } = remote_build( + &auth_config, + &project_config, + &progress, + force_new_build_sessions, + )?; + + if wasm_path.is_none() || metadata_path.is_none() || bundle_path.is_none() { + fs::create_dir_all(TARGET_DIR)?; + } + + wasm_file.seek(SeekFrom::Start(0))?; + let mut wasm_buf = Vec::new(); + wasm_file.read_to_end(&mut wasm_buf)?; + + metadata_file.seek(SeekFrom::Start(0))?; + let mut metadata: Value = serde_json::from_reader(&metadata_file)?; + let wasm_hex = format!("0x{}", hex::encode(&wasm_buf)); + metadata["source"] + .as_object_mut() + .ok_or(BuildError::InvalidMetadataObject)? + .insert("wasm".into(), Value::String(wasm_hex)); + + // Ensure that cross-boundary filesystem copies are supported + // by manually calling fs::copy. + wasm_file.seek(SeekFrom::Start(0))?; + + fs::copy( + &mut wasm_file, + wasm_path.unwrap_or(PathBuf::from(DEFAULT_WASM_PATH)), + )?; + + metadata_file.seek(SeekFrom::Start(0))?; + + fs::copy( + &mut metadata_file, + metadata_path.unwrap_or(PathBuf::from(DEFAULT_METADATA_PATH)), + )?; + + serde_json::to_writer( + File::create(bundle_path.unwrap_or(PathBuf::from(DEFAULT_BUNDLE_PATH)))?, + &metadata, + )?; + + Ok(()) +} diff --git a/crates/patron/src/commands/deploy.rs b/crates/patron/src/commands/deploy.rs index 7c70d91..1e3abb8 100644 --- a/crates/patron/src/commands/deploy.rs +++ b/crates/patron/src/commands/deploy.rs @@ -1,84 +1,23 @@ use std::{ - io::{self, BufRead, BufReader, Read, Seek}, + io, process::{Command, Stdio}, - time::Duration, }; -use common::hash; use derive_more::{Display, Error, From}; use indicatif::ProgressBar; -use reqwest::blocking::multipart::{Form, Part}; -use serde::{Deserialize, Serialize}; -use tempfile::NamedTempFile; use crate::{ - archiver::{build_zip_archive, ArchiverError}, commands::Deploy, config::{AuthenticationConfig, AuthenticationConfigError, ProjectConfig}, + process::{ + ensure_cargo_contract_exists, remote_build, CargoContractInstallError, + FinishedBuildSession, RemoteBuildError, + }, }; -/// `cargo-contract` repository used to install the potentially missing `cargo-contract` binary. -const CARGO_CONTRACT_REPO: &str = "https://github.com/paritytech/cargo-contract"; - /// Default value passed to weight configuration flags of the `cargo-contract`. const DEFAULT_WEIGHT_VAL: u64 = 10_000_000_000; -/// JSON response body with the code hash of a cached build session that matches some source code. -#[derive(Deserialize)] -struct ExistingCodeHashResponse { - /// Code hash hex-encoded value. - code_hash: String, -} - -/// JSON response body returned by build session creation and source code upload requests. -#[derive(Deserialize)] -struct CreateResponse { - /// Resource identifier. - id: i64, -} - -/// JSON request body that is used to create a new build session. -#[derive(Serialize)] -struct BuildSessionCreateRequest<'a> { - /// Source code identifier to build from. - source_code_id: i64, - - /// Preferred Rust toolchain version. - rustc_version: &'a str, - - /// Preferred `cargo-contract` version. - cargo_contract_version: &'a str, -} - -/// JSON response body with the status of an initiated build session. -#[derive(Deserialize)] -struct BuildSessionStatus { - /// Current build session status. - /// - /// For an enumeration of supported values see the `db` crate documentation. - status: String, - - /// Build session code hash, if the build was completed successfully. - code_hash: Option, -} - -/// JSON response body with build session logs. -#[derive(Deserialize)] -struct BuildSessionLogs { - /// Contained build session logs. - logs: Vec, -} - -/// A single build session log entry. -#[derive(Deserialize)] -struct BuildSessionLog { - /// Log entry identifier, that can be used to paginate over build session logs. - id: i64, - - /// Log entry text value. - text: String, -} - /// `deploy` subcommand errors. #[derive(Debug, Display, From, Error)] pub(crate) enum DeployError { @@ -91,24 +30,19 @@ pub(crate) enum DeployError { /// IO-related error. Io(io::Error), - /// HTTP client error. - Http(reqwest::Error), - - /// Zip archiver error. - #[display(fmt = "unable to create zip archive: {}", _0)] - Archiver(ArchiverError), - /// [`which`] crate was unable to determine location of the `cargo` binary file. #[display(fmt = "unable to locate cargo: {}", _0)] Which(which::Error), - /// Unable to install `cargo-contract` binary. - #[display(fmt = "unable to install cargo-contract")] - CargoContractInstallError, + /// Unable to install `cargo-contract`. + CargoContractInstallError(CargoContractInstallError), /// Contract could not be instantiated from the downloaded WASM blob. #[display(fmt = "unable to instantiate a contract")] InstantiationError, + + /// Remote build process error. + RemoteBuildError(RemoteBuildError), } /// Deployment flow entrypoint. @@ -127,169 +61,24 @@ pub(crate) fn deploy( let auth_config = AuthenticationConfig::new()?; let project_config = ProjectConfig::new()?; - let server_path = auth_config.server_path(); + let progress = ProgressBar::new_spinner(); let cargo = which::which("cargo")?; - let pg = ProgressBar::new_spinner(); - - pg.enable_steady_tick(Duration::from_millis(150)); - pg.set_message("Archiving..."); - - let mut archive_file = NamedTempFile::new()?; - - build_zip_archive(&mut archive_file, &pg)?; - - let mut archive_buf = Vec::with_capacity(archive_file.stream_position()? as usize); - archive_file.seek(std::io::SeekFrom::Start(0))?; - archive_file.read_to_end(&mut archive_buf)?; - let archive_hash = hex::encode(hash::blake2(&archive_buf)); - - pg.set_message("Retrieving existing build session..."); + ensure_cargo_contract_exists(&cargo, &project_config.cargo_contract_version, &progress)?; - let response = reqwest::blocking::Client::new() - .get(format!("{server_path}/buildSessions/latest/{archive_hash}")) - .bearer_auth(auth_config.token()) - .send()?; - - let code_hash = if response.status().is_success() && !force_new_build_sessions { - let json: ExistingCodeHashResponse = response.json()?; - json.code_hash - } else { - let source_code_body = Form::new().part( - "archive", - Part::file(archive_file.path())?.mime_str("application/zip")?, - ); - - pg.set_message("Uploading source code..."); - - let source_code_upload: CreateResponse = reqwest::blocking::Client::new() - .post(format!("{server_path}/sourceCode")) - .bearer_auth(auth_config.token()) - .multipart(source_code_body) - .send()? - .error_for_status()? - .json()?; - - pg.set_message("Creating build session..."); - - let build_session_create: CreateResponse = reqwest::blocking::Client::new() - .post(format!("{server_path}/buildSessions")) - .bearer_auth(auth_config.token()) - .json(&BuildSessionCreateRequest { - source_code_id: source_code_upload.id, - rustc_version: &project_config.rustc_version, - cargo_contract_version: &project_config.cargo_contract_version, - }) - .send()? - .error_for_status()? - .json()?; - - let mut log_position = 0; - - pg.set_message("Awaiting for build to finish..."); - - loop { - let logs: BuildSessionLogs = reqwest::blocking::Client::new() - .get(format!( - "{server_path}/buildSessions/logs/{}", - build_session_create.id - )) - .query(&[("position", log_position)]) - .bearer_auth(auth_config.token()) - .send()? - .error_for_status()? - .json()?; - - for log in &logs.logs { - pg.suspend(|| print!("{}", log.text)); - } - - if let Some(log) = logs.logs.last() { - log_position = log.id; - } - - let build_session_status: BuildSessionStatus = reqwest::blocking::Client::new() - .get(format!( - "{server_path}/buildSessions/status/{}", - build_session_create.id - )) - .bearer_auth(auth_config.token()) - .send()? - .error_for_status()? - .json()?; - - match ( - &*build_session_status.status, - build_session_status.code_hash, - ) { - ("completed", Some(code_hash)) => break code_hash, - ("failed", _) => { - pg.finish_with_message("Build failed."); - return Ok(()); - } - _ => {} - } - - std::thread::sleep(Duration::from_secs(3)); - } - }; - - pg.set_message("Installing cargo-contract..."); - - let cargo_contract_version = Command::new(&cargo) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .args(["contract", "--version"]) - .spawn()? - .wait()?; - - if !cargo_contract_version.success() { - let mut install_command = Command::new(&cargo) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .args([ - "install", - "cargo-contract", - "--git", - CARGO_CONTRACT_REPO, - "--tag", - &format!("v{}", &project_config.cargo_contract_version), - ]) - .spawn()?; - - let logs = BufReader::new(install_command.stderr.take().unwrap()); - - for log in logs.lines() { - pg.println(log?); - } - - if !install_command.wait()?.success() { - return Err(DeployError::CargoContractInstallError); - } - } - - let mut wasm_file = tempfile::Builder::new().suffix(".wasm").tempfile()?; - let mut metadata_file = tempfile::Builder::new().suffix(".json").tempfile()?; - - reqwest::blocking::Client::new() - .get(format!("{server_path}/buildSessions/wasm/{}", code_hash)) - .bearer_auth(auth_config.token()) - .send()? - .error_for_status()? - .copy_to(wasm_file.as_file_mut())?; - - reqwest::blocking::Client::new() - .get(format!( - "{server_path}/buildSessions/metadata/{}", - code_hash - )) - .bearer_auth(auth_config.token()) - .send()? - .error_for_status()? - .copy_to(metadata_file.as_file_mut())?; + let FinishedBuildSession { + wasm_file, + metadata_file, + code_hash, + } = remote_build( + &auth_config, + &project_config, + &progress, + force_new_build_sessions, + )?; - pg.set_message("Deploying..."); + progress.set_message("Deploying..."); let mut upload_command = Command::new(&cargo); @@ -354,7 +143,7 @@ pub(crate) fn deploy( return Err(DeployError::InstantiationError); } - pg.finish_with_message(format!( + progress.finish_with_message(format!( "Contract uploaded: {}/codeHash/{}", auth_config.web_path(), code_hash diff --git a/crates/patron/src/commands/verify.rs b/crates/patron/src/commands/verify.rs new file mode 100644 index 0000000..4c08507 --- /dev/null +++ b/crates/patron/src/commands/verify.rs @@ -0,0 +1,91 @@ +use std::{ + fs::File, + io::{self, Read}, + process::{Command, Stdio}, +}; + +use common::hash::blake2; +use derive_more::{Display, Error, From}; +use indicatif::ProgressBar; + +use crate::{ + commands::Verify, + config::{AuthenticationConfig, AuthenticationConfigError, ProjectConfig}, + process::{ + ensure_cargo_contract_exists, remote_build, CargoContractInstallError, + FinishedBuildSession, RemoteBuildError, + }, +}; + +/// `verify` subcommand errors. +#[derive(Debug, Display, From, Error)] +pub(crate) enum VerifyError { + /// Authentication configuration error. + Authentication(AuthenticationConfigError), + + /// Unable to parse the project configuration with [`figment`]. + Figment(figment::Error), + + /// IO-related error. + Io(io::Error), + + /// Remote build process error. + BuildProcessError(RemoteBuildError), + + /// [`which`] crate was unable to determine location of the `cargo` binary file. + #[display(fmt = "unable to locate cargo: {}", _0)] + Which(which::Error), + + /// Unable to install `cargo-contract`. + CargoContractInstallError(CargoContractInstallError), +} + +/// Verify flow entrypoint. +pub(crate) fn verify( + Verify { + force_new_build_sessions, + }: Verify, +) -> Result<(), VerifyError> { + let auth_config = AuthenticationConfig::new()?; + let project_config = ProjectConfig::new()?; + + let progress = ProgressBar::new_spinner(); + + let cargo = which::which("cargo")?; + + ensure_cargo_contract_exists(&cargo, &project_config.cargo_contract_version, &progress)?; + + let FinishedBuildSession { code_hash, .. } = remote_build( + &auth_config, + &project_config, + &progress, + force_new_build_sessions, + )?; + + println!("Remote code hash: 0x{code_hash}"); + + progress.finish_with_message("Remote build finished. Proceeding with the local build..."); + + Command::new(&cargo) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .args(["contract", "build", "--verifiable", "--release"]) + .spawn()? + .wait()?; + + let mut wasm_buf = Vec::new(); + + File::open("./target/ink/hello.wasm")?.read_to_end(&mut wasm_buf)?; + + let local_code_hash = hex::encode(blake2(&wasm_buf)); + + println!("Local code hash: 0x{local_code_hash}"); + + if local_code_hash == code_hash { + println!("Code hashes are matching."); + } else { + println!("Code hashes do not match."); + } + + Ok(()) +} diff --git a/crates/patron/src/config.rs b/crates/patron/src/config.rs index 02a854e..dec5cb0 100644 --- a/crates/patron/src/config.rs +++ b/crates/patron/src/config.rs @@ -109,9 +109,6 @@ impl AuthenticationConfig { pub struct ProjectConfig { /// `cargo-contract` package version. pub cargo_contract_version: String, - - /// Rust toolchain version. - pub rustc_version: String, } impl ProjectConfig { diff --git a/crates/patron/src/main.rs b/crates/patron/src/main.rs index f44d0cd..2244ebc 100644 --- a/crates/patron/src/main.rs +++ b/crates/patron/src/main.rs @@ -7,6 +7,9 @@ //! paths, we need to ignore directories which are most likely to be unused during builds, //! such as the `target` directory and hidden entries (for example, `.git`). +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + use clap::Parser; use commands::{Cli, Commands}; @@ -19,6 +22,9 @@ mod commands; /// CLI-specific configuration (authentication, project). mod config; +/// Remote build process implementation. +mod process; + /// CLI entrypoint. fn main() -> Result<(), anyhow::Error> { let cli = Cli::parse(); @@ -26,6 +32,8 @@ fn main() -> Result<(), anyhow::Error> { match cli.command { Commands::Auth(args) => commands::auth(args)?, Commands::Deploy(args) => commands::deploy(args)?, + Commands::Build(args) => commands::build(args)?, + Commands::Verify(args) => commands::verify(args)?, } Ok(()) diff --git a/crates/patron/src/process.rs b/crates/patron/src/process.rs new file mode 100644 index 0000000..9b3be84 --- /dev/null +++ b/crates/patron/src/process.rs @@ -0,0 +1,297 @@ +use std::{ + io::{self, BufRead, BufReader, Read, Seek}, + path::Path, + process::{Command, Stdio}, + time::Duration, +}; + +use common::hash; +use derive_more::{Display, Error, From}; +use indicatif::ProgressBar; +use reqwest::blocking::multipart::{Form, Part}; +use serde::{Deserialize, Serialize}; +use tempfile::NamedTempFile; + +use crate::{ + archiver::{build_zip_archive, ArchiverError}, + config::{AuthenticationConfig, ProjectConfig}, +}; + +/// `cargo-contract` repository used to install the potentially missing `cargo-contract` binary. +const CARGO_CONTRACT_REPO: &str = "https://github.com/paritytech/cargo-contract"; + +/// JSON response body with the code hash of a cached build session that matches some source code. +#[derive(Deserialize)] +struct ExistingCodeHashResponse { + /// Code hash hex-encoded value. + code_hash: String, +} + +/// JSON response body returned by build session creation and source code upload requests. +#[derive(Deserialize)] +struct CreateResponse { + /// Resource identifier. + id: i64, +} + +/// JSON request body that is used to create a new build session. +#[derive(Serialize)] +struct BuildSessionCreateRequest<'a> { + /// Source code identifier to build from. + source_code_id: i64, + + /// Preferred `cargo-contract` version. + cargo_contract_version: &'a str, +} + +/// JSON response body with the status of an initiated build session. +#[derive(Deserialize)] +struct BuildSessionStatus { + /// Current build session status. + /// + /// For an enumeration of supported values see the `db` crate documentation. + status: String, + + /// Build session code hash, if the build was completed successfully. + code_hash: Option, +} + +/// JSON response body with build session logs. +#[derive(Deserialize)] +struct BuildSessionLogs { + /// Contained build session logs. + logs: Vec, +} + +/// A single build session log entry. +#[derive(Deserialize)] +struct BuildSessionLog { + /// Log entry identifier, that can be used to paginate over build session logs. + id: i64, + + /// Log entry text value. + text: String, +} + +/// `deploy` subcommand errors. +#[derive(Debug, Display, From, Error)] +pub(crate) enum RemoteBuildError { + /// IO-related error. + Io(io::Error), + + /// HTTP client error. + Http(reqwest::Error), + + /// Zip archiver error. + #[display(fmt = "unable to create zip archive: {}", _0)] + Archiver(ArchiverError), + + /// Build session failed. + #[display(fmt = "unable to finish this build session")] + BuildFailed, +} + +/// Finished remote build session. +pub(crate) struct FinishedBuildSession { + /// Downloaded WASM blob from a remote build session. + pub wasm_file: NamedTempFile, + + /// Downloaded JSON metadata from a remote build session. + pub metadata_file: NamedTempFile, + + /// Code hash value of a resulted WASM blob. + pub code_hash: String, +} + +/// Start remote build process. +/// +/// This method returns two [`NamedTempFile`]'s which correspond to a WASM +/// blob and a JSON metadata. +pub(crate) fn remote_build( + auth_config: &AuthenticationConfig, + project_config: &ProjectConfig, + progress: &ProgressBar, + force_new_build_sessions: bool, +) -> Result { + let server_path = auth_config.server_path(); + + progress.enable_steady_tick(Duration::from_millis(150)); + progress.set_message("Archiving..."); + + let mut archive_file = NamedTempFile::new()?; + + build_zip_archive(&mut archive_file, progress)?; + + let mut archive_buf = Vec::with_capacity(archive_file.stream_position()? as usize); + archive_file.seek(std::io::SeekFrom::Start(0))?; + archive_file.read_to_end(&mut archive_buf)?; + let archive_hash = hex::encode(hash::blake2(&archive_buf)); + + progress.set_message("Retrieving existing build session..."); + + let response = reqwest::blocking::Client::new() + .get(format!("{server_path}/buildSessions/latest/{archive_hash}")) + .bearer_auth(auth_config.token()) + .send()?; + + let code_hash = if response.status().is_success() && !force_new_build_sessions { + let json: ExistingCodeHashResponse = response.json()?; + json.code_hash + } else { + let source_code_body = Form::new().part( + "archive", + Part::file(archive_file.path())?.mime_str("application/zip")?, + ); + + progress.set_message("Uploading source code..."); + + let source_code_upload: CreateResponse = reqwest::blocking::Client::new() + .post(format!("{server_path}/sourceCode")) + .bearer_auth(auth_config.token()) + .multipart(source_code_body) + .send()? + .error_for_status()? + .json()?; + + progress.set_message("Creating build session..."); + + let build_session_create: CreateResponse = reqwest::blocking::Client::new() + .post(format!("{server_path}/buildSessions")) + .bearer_auth(auth_config.token()) + .json(&BuildSessionCreateRequest { + source_code_id: source_code_upload.id, + cargo_contract_version: &project_config.cargo_contract_version, + }) + .send()? + .error_for_status()? + .json()?; + + let mut log_position = 0; + + progress.set_message("Awaiting for build to finish..."); + + loop { + let logs: BuildSessionLogs = reqwest::blocking::Client::new() + .get(format!( + "{server_path}/buildSessions/logs/{}", + build_session_create.id + )) + .query(&[("position", log_position)]) + .bearer_auth(auth_config.token()) + .send()? + .error_for_status()? + .json()?; + + for log in &logs.logs { + progress.suspend(|| print!("{}", log.text)); + } + + if let Some(log) = logs.logs.last() { + log_position = log.id; + } + + let build_session_status: BuildSessionStatus = reqwest::blocking::Client::new() + .get(format!( + "{server_path}/buildSessions/status/{}", + build_session_create.id + )) + .bearer_auth(auth_config.token()) + .send()? + .error_for_status()? + .json()?; + + match ( + &*build_session_status.status, + build_session_status.code_hash, + ) { + ("completed", Some(code_hash)) => break code_hash, + ("failed", _) => { + progress.finish_with_message("Build failed."); + return Err(RemoteBuildError::BuildFailed); + } + _ => {} + } + + std::thread::sleep(Duration::from_secs(3)); + } + }; + + let mut wasm_file = tempfile::Builder::new().suffix(".wasm").tempfile()?; + let mut metadata_file = tempfile::Builder::new().suffix(".json").tempfile()?; + + reqwest::blocking::Client::new() + .get(format!("{server_path}/buildSessions/wasm/{}", code_hash)) + .bearer_auth(auth_config.token()) + .send()? + .error_for_status()? + .copy_to(wasm_file.as_file_mut())?; + + reqwest::blocking::Client::new() + .get(format!( + "{server_path}/buildSessions/metadata/{}", + code_hash + )) + .bearer_auth(auth_config.token()) + .send()? + .error_for_status()? + .copy_to(metadata_file.as_file_mut())?; + + Ok(FinishedBuildSession { + wasm_file, + metadata_file, + code_hash, + }) +} + +/// Errors that may occur during the `cargo-contract` installation phase. +#[derive(Debug, Display, From, Error)] +pub(crate) enum CargoContractInstallError { + /// IO-related error. + Io(io::Error), + + /// Unable to install `cargo-contract`. + InstallationError, +} + +/// Ensure `cargo-contract` exists, installing it automatically if it isn't. +pub(crate) fn ensure_cargo_contract_exists( + cargo: &Path, + cargo_contract_version: &str, + progress: &ProgressBar, +) -> Result<(), CargoContractInstallError> { + progress.set_message("Installing cargo-contract..."); + + let cargo_contract_exists = Command::new(cargo) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .args(["contract", "--version"]) + .spawn()? + .wait()?; + + if !cargo_contract_exists.success() { + let mut install_command = Command::new(cargo) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .args([ + "install", + "cargo-contract", + "--git", + CARGO_CONTRACT_REPO, + "--tag", + &format!("v{}", cargo_contract_version), + ]) + .spawn()?; + + let logs = BufReader::new(install_command.stderr.take().unwrap()); + + for log in logs.lines() { + progress.println(log?); + } + + if !install_command.wait()?.success() { + return Err(CargoContractInstallError::InstallationError); + } + } + + Ok(()) +} diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index f68b749..03b2814 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml @@ -20,7 +20,6 @@ schemars = "0.8.12" serde = { version = "1.0.162", features = ["derive"] } serde_plain = "1.0.1" serde_json = "1.0.96" -sp-core = "20.0.0" tracing = "0.1.37" tokio = { version = "1.28.1", features = ["rt-multi-thread", "macros"] } validator = { version = "0.16.0", features = ["derive"] } diff --git a/crates/server/src/handlers/auth/login.rs b/crates/server/src/handlers/auth/login.rs index 148b371..09fa1b1 100644 --- a/crates/server/src/handlers/auth/login.rs +++ b/crates/server/src/handlers/auth/login.rs @@ -7,6 +7,10 @@ use axum::{ Json, }; use axum_derive_error::ErrorResponse; +use common::rpc::sp_core::{ + sr25519::{Pair, Public, Signature}, + Pair as _, +}; use db::{ cli_token, public_key, sea_query::OnConflict, token, ActiveValue, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, QuerySelect, TransactionErrorExt, @@ -16,10 +20,6 @@ use derive_more::{Display, Error, From}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_json::Value; -use sp_core::{ - sr25519::{Pair, Public, Signature}, - Pair as _, -}; use crate::schema::example_error; @@ -183,6 +183,7 @@ mod tests { http::{Request, StatusCode}, }; use common::config::Config; + use common::rpc::sp_core::crypto::{AccountId32, Ss58Codec}; use db::{ cli_token, public_key, token::TOKEN_LENGTH, user, ActiveValue, DatabaseConnection, EntityTrait, @@ -192,7 +193,6 @@ mod tests { thread_rng, }; use serde_json::json; - use sp_core::crypto::{AccountId32, Ss58Codec}; use tower::{Service, ServiceExt}; const ACCOUNT_ID: &str = "5FeLhJAs4CUHqpWmPDBLeL7NLAoHsB2ZuFZ5Mk62EgYemtFj"; diff --git a/crates/server/src/handlers/build_sessions/create.rs b/crates/server/src/handlers/build_sessions/create.rs index 401e9c4..a12c544 100644 --- a/crates/server/src/handlers/build_sessions/create.rs +++ b/crates/server/src/handlers/build_sessions/create.rs @@ -17,7 +17,7 @@ use validator::Validate; use crate::{auth::AuthenticatedUserId, schema::example_error, validation::ValidatedJson}; -/// Regular expression to match stable versions of Rust toolchain and `cargo-contract`. +/// Regular expression to match stable versions of `cargo-contract`. /// /// Currently, this regex does not support any nightly or unstable versions of the previously mentioned tooling. static VERSION_REGEX: Lazy = Lazy::new(|| { @@ -53,11 +53,6 @@ pub(super) struct BuildSessionCreateRequest { #[validate(regex = "VERSION_REGEX")] #[schemars(example = "crate::schema::example_cargo_contract_version")] cargo_contract_version: String, - - /// Rust tooling version. - #[validate(regex = "VERSION_REGEX")] - #[schemars(example = "crate::schema::example_rustc_version")] - rustc_version: String, } /// JSON response body. @@ -105,7 +100,6 @@ pub(super) async fn create( user_id: ActiveValue::Set(Some(current_user.id())), source_code_id: ActiveValue::Set(request.source_code_id), cargo_contract_version: ActiveValue::Set(request.cargo_contract_version), - rustc_version: ActiveValue::Set(request.rustc_version), ..Default::default() }) .exec_with_returning(txn) @@ -196,7 +190,6 @@ mod tests { .body(Body::from_json(json!({ "source_code_id": source_code_id, "cargo_contract_version": "3.0.0", - "rustc_version": "1.69.0" }))) .unwrap(), ) @@ -224,7 +217,6 @@ mod tests { .body(Body::from_json(json!({ "source_code_id": source_code_id, "cargo_contract_version": "abc-1.2.3", - "rustc_version": "1.69.0" }))) .unwrap(), ) @@ -250,7 +242,6 @@ mod tests { .body(Body::from_json(json!({ "source_code_id": 123, "cargo_contract_version": "3.0.0", - "rustc_version": "1.69.0" }))) .unwrap(), ) diff --git a/crates/server/src/handlers/build_sessions/details.rs b/crates/server/src/handlers/build_sessions/details.rs index 70656dd..1781f5b 100644 --- a/crates/server/src/handlers/build_sessions/details.rs +++ b/crates/server/src/handlers/build_sessions/details.rs @@ -29,10 +29,6 @@ pub struct BuildSessionInfo { /// Version of `cargo-contract` used to build the contract. #[schemars(example = "crate::schema::example_cargo_contract_version")] pub cargo_contract_version: String, - - /// Version of Rust toolchain used to build the contract. - #[schemars(example = "crate::schema::example_rustc_version")] - pub rustc_version: String, } /// Errors that may occur during the detail preview process. @@ -73,7 +69,6 @@ pub(super) async fn details( .columns([ build_session::Column::SourceCodeId, build_session::Column::CargoContractVersion, - build_session::Column::RustcVersion, ]) .filter(build_session::Column::CodeHash.eq(&code_hash.0[..])) .order_by_desc(build_session::Column::CreatedAt) @@ -121,7 +116,6 @@ mod tests { source_code_id: ActiveValue::Set(source_code_id), status: ActiveValue::Set(build_session::Status::New), cargo_contract_version: ActiveValue::Set(String::from("3.0.0")), - rustc_version: ActiveValue::Set(String::from("1.69.0")), code_hash: ActiveValue::Set(Some(vec![0; 32])), ..Default::default() }) @@ -149,7 +143,6 @@ mod tests { assert_json!(response.json().await, { "source_code_id": 1, - "rustc_version": "1.69.0", "cargo_contract_version": "3.0.0" }); } diff --git a/crates/server/src/handlers/build_sessions/latest.rs b/crates/server/src/handlers/build_sessions/latest.rs index eb9fa7d..99da32a 100644 --- a/crates/server/src/handlers/build_sessions/latest.rs +++ b/crates/server/src/handlers/build_sessions/latest.rs @@ -138,7 +138,6 @@ mod tests { source_code_id: ActiveValue::Set(source_code_id), status: ActiveValue::Set(build_session::Status::Completed), cargo_contract_version: ActiveValue::Set(String::from("3.0.0")), - rustc_version: ActiveValue::Set(String::from("1.69.0")), code_hash: ActiveValue::Set(Some(vec![0; 32])), ..Default::default() }) diff --git a/crates/server/src/handlers/build_sessions/list.rs b/crates/server/src/handlers/build_sessions/list.rs index 4f26995..36dbecb 100644 --- a/crates/server/src/handlers/build_sessions/list.rs +++ b/crates/server/src/handlers/build_sessions/list.rs @@ -159,7 +159,6 @@ mod tests { source_code_id: ActiveValue::Set(source_code_id), status: ActiveValue::Set(build_session::Status::Completed), cargo_contract_version: ActiveValue::Set(String::from("3.0.0")), - rustc_version: ActiveValue::Set(String::from("1.69.0")), code_hash: ActiveValue::Set(Some(vec![0; 32])), ..Default::default() }) @@ -173,7 +172,6 @@ mod tests { source_code_id: ActiveValue::Set(source_code_id), status: ActiveValue::Set(build_session::Status::New), cargo_contract_version: ActiveValue::Set(String::from("3.0.0")), - rustc_version: ActiveValue::Set(String::from("1.69.0")), ..Default::default() }) .exec_with_returning(db) diff --git a/crates/server/src/handlers/build_sessions/logs.rs b/crates/server/src/handlers/build_sessions/logs.rs index 4c0328a..7e4b280 100644 --- a/crates/server/src/handlers/build_sessions/logs.rs +++ b/crates/server/src/handlers/build_sessions/logs.rs @@ -177,7 +177,6 @@ mod tests { source_code_id: ActiveValue::Set(source_code_id), status: ActiveValue::Set(build_session::Status::Completed), cargo_contract_version: ActiveValue::Set(String::from("3.0.0")), - rustc_version: ActiveValue::Set(String::from("1.69.0")), code_hash: ActiveValue::Set(Some(vec![0; 32])), ..Default::default() }) diff --git a/crates/server/src/handlers/build_sessions/metadata.rs b/crates/server/src/handlers/build_sessions/metadata.rs index a124b12..848c0fe 100644 --- a/crates/server/src/handlers/build_sessions/metadata.rs +++ b/crates/server/src/handlers/build_sessions/metadata.rs @@ -107,7 +107,6 @@ mod tests { source_code_id: ActiveValue::Set(source_code_id), status: ActiveValue::Set(build_session::Status::Completed), cargo_contract_version: ActiveValue::Set(String::from("3.0.0")), - rustc_version: ActiveValue::Set(String::from("1.69.0")), code_hash: ActiveValue::Set(Some(vec![0; 32])), metadata: ActiveValue::Set(Some( serde_json::to_vec(&json! ({ diff --git a/crates/server/src/handlers/build_sessions/status.rs b/crates/server/src/handlers/build_sessions/status.rs index a9ec3c7..ed85e9f 100644 --- a/crates/server/src/handlers/build_sessions/status.rs +++ b/crates/server/src/handlers/build_sessions/status.rs @@ -114,7 +114,6 @@ mod tests { source_code_id: ActiveValue::Set(source_code_id), status: ActiveValue::Set(build_session::Status::Completed), cargo_contract_version: ActiveValue::Set(String::from("3.0.0")), - rustc_version: ActiveValue::Set(String::from("1.69.0")), code_hash: ActiveValue::Set(Some(vec![0; 32])), ..Default::default() }) diff --git a/crates/server/src/handlers/contracts/details.rs b/crates/server/src/handlers/contracts/details.rs index 9fe2b15..9de8f8e 100644 --- a/crates/server/src/handlers/contracts/details.rs +++ b/crates/server/src/handlers/contracts/details.rs @@ -7,6 +7,10 @@ use axum::{ Json, }; use axum_derive_error::ErrorResponse; +use common::rpc::sp_core::{ + crypto::{AccountId32, Ss58Codec}, + ByteArray, +}; use db::{ contract, node, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, QuerySelect, TransactionErrorExt, TransactionTrait, @@ -15,10 +19,6 @@ use derive_more::{Display, Error, From}; use schemars::JsonSchema; use serde::Serialize; use serde_json::Value; -use sp_core::{ - crypto::{AccountId32, Ss58Codec}, - ByteArray, -}; use crate::{hex_hash::HexHash, schema::example_error}; @@ -141,15 +141,14 @@ mod tests { http::{Request, StatusCode}, }; use common::config::Config; + use common::rpc::sp_core::crypto::AccountId32; use db::{code, contract, node, ActiveValue, DatabaseConnection, EntityTrait}; - use sp_core::crypto::AccountId32; use tower::ServiceExt; async fn create_test_env(db: &DatabaseConnection) { let node = node::Entity::insert(node::ActiveModel { name: ActiveValue::Set(String::from("test")), url: ActiveValue::Set(String::from("ws://localhost:9944")), - schema: ActiveValue::Set(String::from("test")), confirmed_block: ActiveValue::Set(0), ..Default::default() }) diff --git a/crates/server/src/handlers/contracts/events.rs b/crates/server/src/handlers/contracts/events.rs index e40981a..b2f703d 100644 --- a/crates/server/src/handlers/contracts/events.rs +++ b/crates/server/src/handlers/contracts/events.rs @@ -6,6 +6,7 @@ use axum::{ Json, }; use axum_derive_error::ErrorResponse; +use common::rpc::sp_core::ByteArray; use db::{ event, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, PrimitiveDateTime, QueryFilter, QueryOrder, QuerySelect, @@ -14,7 +15,6 @@ use derive_more::{Display, Error, From}; use futures_util::TryStreamExt; use schemars::JsonSchema; use serde::Serialize; -use sp_core::ByteArray; use super::WrappedAccountId32; @@ -83,18 +83,17 @@ mod tests { use assert_json::assert_json; use axum::{body::Body, http::Request}; use common::config::Config; + use common::rpc::sp_core::crypto::AccountId32; use db::{ code, contract, event, node, ActiveValue, DatabaseConnection, EntityTrait, OffsetDateTime, PrimitiveDateTime, }; - use sp_core::crypto::AccountId32; use tower::ServiceExt; async fn create_test_env(db: &DatabaseConnection) { let node = node::Entity::insert(node::ActiveModel { name: ActiveValue::Set(String::from("test")), url: ActiveValue::Set(String::from("ws://localhost:9944")), - schema: ActiveValue::Set(String::from("test")), confirmed_block: ActiveValue::Set(0), ..Default::default() }) diff --git a/crates/server/src/handlers/contracts/mod.rs b/crates/server/src/handlers/contracts/mod.rs index 7ee8459..7366237 100644 --- a/crates/server/src/handlers/contracts/mod.rs +++ b/crates/server/src/handlers/contracts/mod.rs @@ -7,11 +7,12 @@ mod events; use std::sync::Arc; use aide::axum::{routing::get_with, ApiRouter}; +use common::rpc::sp_core::crypto::AccountId32; use db::DatabaseConnection; use schemars::JsonSchema; use serde::Deserialize; -use sp_core::crypto::AccountId32; +/// [`AccountId32`] wrapper for OAPI documentation purposes. #[derive(Deserialize, JsonSchema)] #[serde(transparent)] struct WrappedAccountId32( diff --git a/crates/server/src/handlers/files/upload.rs b/crates/server/src/handlers/files/upload.rs index e725392..7b72bd7 100644 --- a/crates/server/src/handlers/files/upload.rs +++ b/crates/server/src/handlers/files/upload.rs @@ -148,7 +148,6 @@ mod tests { source_code_id: ActiveValue::Set(source_code_id), status: ActiveValue::Set(build_session::Status::New), cargo_contract_version: ActiveValue::Set(String::from("3.0.0")), - rustc_version: ActiveValue::Set(String::from("1.69.0")), ..Default::default() }) .exec_with_returning(db) diff --git a/crates/server/src/handlers/keys/delete.rs b/crates/server/src/handlers/keys/delete.rs index 12042b4..93da20f 100644 --- a/crates/server/src/handlers/keys/delete.rs +++ b/crates/server/src/handlers/keys/delete.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use aide::{transform::TransformOperation, OperationIo}; use axum::{extract::State, Extension, Json}; use axum_derive_error::ErrorResponse; +use common::rpc::sp_core::sr25519::Public; use db::{ public_key, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, TransactionErrorExt, TransactionTrait, @@ -10,7 +11,6 @@ use db::{ use derive_more::{Display, Error, From}; use schemars::JsonSchema; use serde::Deserialize; -use sp_core::sr25519::Public; use crate::auth::AuthenticatedUserId; @@ -73,9 +73,9 @@ mod tests { http::{Request, StatusCode}, }; use common::config::Config; + use common::rpc::sp_core::crypto::{AccountId32, Ss58Codec}; use db::{public_key, token, user, ActiveValue, DatabaseConnection, EntityTrait}; use serde_json::json; - use sp_core::crypto::{AccountId32, Ss58Codec}; use tower::Service; const ACCOUNT_ID: &str = "5FeLhJAs4CUHqpWmPDBLeL7NLAoHsB2ZuFZ5Mk62EgYemtFj"; diff --git a/crates/server/src/handlers/keys/list.rs b/crates/server/src/handlers/keys/list.rs index 43afbc4..ef84e5d 100644 --- a/crates/server/src/handlers/keys/list.rs +++ b/crates/server/src/handlers/keys/list.rs @@ -6,6 +6,7 @@ use axum::{ Extension, Json, }; use axum_derive_error::ErrorResponse; +use common::rpc::sp_core::crypto::AccountId32; use db::{ public_key, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, QuerySelect, }; @@ -13,7 +14,6 @@ use derive_more::{Display, Error, From}; use futures_util::TryStreamExt; use schemars::JsonSchema; use serde::Serialize; -use sp_core::crypto::AccountId32; use crate::{auth::AuthenticatedUserId, pagination::Pagination}; diff --git a/crates/server/src/handlers/keys/verify.rs b/crates/server/src/handlers/keys/verify.rs index ae92400..aa0df93 100644 --- a/crates/server/src/handlers/keys/verify.rs +++ b/crates/server/src/handlers/keys/verify.rs @@ -3,6 +3,10 @@ use std::sync::Arc; use aide::{transform::TransformOperation, OperationIo}; use axum::{extract::State, http::StatusCode, Extension, Json}; use axum_derive_error::ErrorResponse; +use common::rpc::sp_core::{ + sr25519::{Pair, Public, Signature}, + Pair as _, +}; use db::{ public_key, user, ActiveValue, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, QuerySelect, SelectExt, TransactionErrorExt, TransactionTrait, @@ -11,10 +15,6 @@ use derive_more::{Display, Error, From}; use schemars::JsonSchema; use serde::Deserialize; use serde_json::Value; -use sp_core::{ - sr25519::{Pair, Public, Signature}, - Pair as _, -}; use crate::{auth::AuthenticatedUserId, schema::example_error}; diff --git a/crates/server/src/handlers/payment/check.rs b/crates/server/src/handlers/payment/check.rs index cbeb172..1e69cb1 100644 --- a/crates/server/src/handlers/payment/check.rs +++ b/crates/server/src/handlers/payment/check.rs @@ -1,16 +1,14 @@ -use std::{array::TryFromSliceError, fmt, str::FromStr, sync::Arc}; +use std::{array::TryFromSliceError, sync::Arc}; use aide::{transform::TransformOperation, OperationIo}; use axum::{extract::State, http::StatusCode, Extension, Json}; use axum_derive_error::ErrorResponse; -use common::{ - hash::blake2, - rpc::{ - parity_scale_codec::{self, Decode}, - subxt::{self, ext::sp_runtime::DispatchError, OnlineClient, PolkadotConfig}, - InvalidSchema, Schema, - }, -}; +use common::hash::blake2; +use common::rpc::parity_scale_codec::Decode; +use common::rpc::sp_core::crypto::AccountId32; +use common::rpc::substrate_api_client::rpc::JsonrpseeClient; +use common::rpc::substrate_api_client::Api; +use common::rpc::{self, parity_scale_codec, substrate_api_client}; use db::{ node, public_key, user, ActiveValue, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, QuerySelect, SelectExt, TransactionErrorExt, TransactionTrait, @@ -20,7 +18,8 @@ use ink_metadata::LangError; use schemars::JsonSchema; use serde::Deserialize; use serde_json::Value; -use sp_core::crypto::AccountId32; +use tokio::runtime::Handle; +use tokio::task::JoinError; use crate::{auth::AuthenticatedUserId, schema::example_error}; @@ -43,14 +42,9 @@ pub(super) enum PaymentCheckError { /// Database-related error. DatabaseError(DbErr), - /// Invalid schema name is stored inside of a database. - Schema(InvalidSchema), - /// Substrate RPC-related error. - Rpc(subxt::Error), - - /// Contract call dispatch error. - Dispatch(WrappedDispatchError), + #[display(fmt = "substrate rpc error: {:?}", _0)] + Rpc(#[error(ignore)] substrate_api_client::Error), /// SCALE codec error. Scale(parity_scale_codec::Error), @@ -58,6 +52,9 @@ pub(super) enum PaymentCheckError { /// Contract address stored inside of a database is invalid. ContractAddress(TryFromSliceError), + /// Unable to spawn Tokio task to handle RPC calls. + JoinError(JoinError), + /// Contract call error. #[display(fmt = "unable to call the contract")] CallError, @@ -93,18 +90,6 @@ pub(super) enum PaymentCheckError { PaidAlready, } -/// [`DispatchError`] wrapper that provides an implementation of [`std::error::Error`] trait. -#[derive(Debug)] -pub(super) struct WrappedDispatchError(DispatchError); - -impl fmt::Display for WrappedDispatchError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.0) - } -} - -impl std::error::Error for WrappedDispatchError {} - /// Generate OAPI documentation for the [`check`] handler. pub(super) fn docs(op: TransformOperation) -> TransformOperation { op.summary("Check membership payment with the provided node.") @@ -151,38 +136,41 @@ pub(super) async fn check( return Err(PaymentCheckError::InvalidKey); } - let (url, schema_name, contract) = node::Entity::find_by_id(request.node_id) + let (url, contract) = node::Entity::find_by_id(request.node_id) .select_only() - .columns([ - node::Column::Url, - node::Column::Schema, - node::Column::PaymentContract, - ]) - .into_tuple::<(String, String, Option>)>() + .columns([node::Column::Url, node::Column::PaymentContract]) + .into_tuple::<(String, Option>)>() .one(txn) .await? .ok_or(PaymentCheckError::InvalidNodeId)?; let contract = contract.ok_or(PaymentCheckError::NodeWithoutPayments)?; - let rpc = OnlineClient::::from_url(url).await?; - let schema = Schema::from_str(&schema_name)?; - // Make sure this matches the ABI of the check message. let mut data = Vec::with_capacity(36); data.extend_from_slice(&blake2("check".as_bytes())[0..4]); data.extend_from_slice(request.account.as_ref()); - let raw_response = schema - .call_contract( - &rpc, - subxt::utils::AccountId32(contract.as_slice().try_into()?), - data, - ) - .await? - .result - .map_err(WrappedDispatchError)? - .data; + let raw_response = tokio::task::spawn_blocking(|| { + Handle::current().block_on(async move { + let client = JsonrpseeClient::new(&url) + .map_err(substrate_api_client::Error::RpcClient)?; + let api = Api::new(client).await?; + + let val = rpc::call_contract( + &api, + AccountId32::new(contract.as_slice().try_into()?), + data, + ) + .await?; + + Result::<_, PaymentCheckError>::Ok(val) + }) + }) + .await?? + .result + .map_err(|_| PaymentCheckError::CallError)? + .data; let response: Result = Decode::decode(&mut &*raw_response)?; diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index aedffb4..dca1aa4 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs @@ -8,6 +8,9 @@ //! Request body size limiting is necessary to ensure that you don't get overwhelmed with //! source code archive uploads while using a self-hosted environment. +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + /// API authentication middleware and helpers. mod auth; @@ -44,7 +47,7 @@ use tracing::info; /// API server entrypoint. #[tokio::main] async fn main() -> Result<(), anyhow::Error> { - let config = Config::new()?; + let config = Config::new(None)?; logging::init(&config); diff --git a/crates/server/src/schema.rs b/crates/server/src/schema.rs index 2a5ae7c..74c4e6f 100644 --- a/crates/server/src/schema.rs +++ b/crates/server/src/schema.rs @@ -1,13 +1,13 @@ use std::fmt::Display; use axum::response::IntoResponse; -use db::{build_session, event::EventBody}; -use serde_json::{json, Value}; -use sp_core::{ +use common::rpc::sp_core::{ crypto::{AccountId32, Ss58Codec}, sr25519::{Pair, Public, Signature}, Pair as _, }; +use db::{build_session, event::EventBody}; +use serde_json::{json, Value}; use crate::hex_hash::HexHash; @@ -42,7 +42,6 @@ generate_examples!( database_identifier, i64, 1; hex_hash, HexHash, HexHash([200; 32]); cargo_contract_version, String, String::from("3.0.1"); - rustc_version, String, String::from("1.70.0"); build_session_status, build_session::Status, build_session::Status::Completed; log_position, Option, Some(40); log_entry, String, String::from("Compiling futures-util v0.3.28"); diff --git a/docs/cli.md b/docs/cli.md index 3616efd..33bdf0d 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -26,10 +26,9 @@ The build process itself is done on a remote server, but the deployment process safe and to facilitate possible air-gapped deployments. First of all, you need to create a `Deploy.toml` file at the root of your contract source code. -This file describes the Rust toolchain and `cargo-contract` versions that will be used during the build: +This file describes the `cargo-contract` version that will be used during the build: ```toml -rustc_version = "1.69.0" cargo_contract_version = "3.0.1" ``` @@ -60,3 +59,13 @@ patron deploy new --suri //Alice -- --password 123 ``` To get more information, invoke the deploy command with the `--help` flag. + +## Build + +You can also acquire contract's WASM blob and JSON metadata files without the deployment itself +by using the `build` subcommand which, by default, outputs `contract.wasm` and `contract.json` files +to the `./target/ink` directory. + +You can modify the output directory with `--wasm_path` and `--metadata_path` flags. + +See `--help` flag output for more information. diff --git a/docs/self-hosted.md b/docs/self-hosted.md index 708e00a..8636d9f 100644 --- a/docs/self-hosted.md +++ b/docs/self-hosted.md @@ -210,11 +210,10 @@ This component provides users with the information about events on-chain. To initially fill the database with contract and code data use the `initialize` command: ```sh -./event_client initialize my_node wss://node.example.com:443/ astar +./event_client initialize my_node wss://node.example.com:443/ ``` -`initialize` command accepts the node name, node URL, and node schema, which is used to -correctly communicate with the node. +`initialize` command accepts the node name and the node URL. You may also optionally pass `--payment-address` flag to enable membership payments using a separate smart contract. See the ["Membership smart contract ABI"](#membership-smart-contract-abi) for more information on that. diff --git a/flake.lock b/flake.lock index 60413f4..e731ada 100644 --- a/flake.lock +++ b/flake.lock @@ -14,11 +14,11 @@ ] }, "locked": { - "lastModified": 1684468982, - "narHash": "sha256-EoC1N5sFdmjuAP3UOkyQujSOT6EdcXTnRw8hPjJkEgc=", + "lastModified": 1688772518, + "narHash": "sha256-ol7gZxwvgLnxNSZwFTDJJ49xVY5teaSvF7lzlo3YQfM=", "owner": "ipetkov", "repo": "crane", - "rev": "99de890b6ef4b4aab031582125b6056b792a4a30", + "rev": "8b08e96c9af8c6e3a2b69af5a7fa168750fcf88e", "type": "github" }, "original": { @@ -48,11 +48,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1681202837, - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", "owner": "numtide", "repo": "flake-utils", - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", "type": "github" }, "original": { @@ -63,11 +63,11 @@ }, "nix-filter": { "locked": { - "lastModified": 1681154353, - "narHash": "sha256-MCJ5FHOlbfQRFwN0brqPbCunLEVw05D/3sRVoNVt2tI=", + "lastModified": 1687178632, + "narHash": "sha256-HS7YR5erss0JCaUijPeyg2XrisEb959FIct3n2TMGbE=", "owner": "numtide", "repo": "nix-filter", - "rev": "f529f42792ade8e32c4be274af6b6d60857fbee7", + "rev": "d90c75e8319d0dd9be67d933d8eb9d0894ec9174", "type": "github" }, "original": { @@ -78,11 +78,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1684385584, - "narHash": "sha256-O7y0gK8OLIDqz+LaHJJyeu09IGiXlZIS3+JgEzGmmJA=", + "lastModified": 1689008574, + "narHash": "sha256-VFMgyHDiqsGDkRg73alv6OdHJAqhybryWHv77bSCGIw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "48a0fb7aab511df92a17cf239c37f2bd2ec9ae3a", + "rev": "4a729ce4b1fe5ec4fffc71c67c96aa5184ebb462", "type": "github" }, "original": { @@ -111,11 +111,11 @@ ] }, "locked": { - "lastModified": 1684462813, - "narHash": "sha256-YFphDnxzXtLLExXjiR9bUVF4isI/MKiC4HMboh2ZSOc=", + "lastModified": 1689042658, + "narHash": "sha256-p7cQAFNt5kX19sZvK74CmY0nTrtujpZg6sZUiV1ntAk=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "e2ceeaa7f9334c5d732323b6fec363229da4f382", + "rev": "d7181bb2237035df17cab9295c95f987f5c527e6", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 64cd1c5..1005f0a 100644 --- a/flake.nix +++ b/flake.nix @@ -74,7 +74,7 @@ ]; }; - rustToolchain = pkgs.rust-bin.stable.latest.default.override { + rustToolchain = pkgs.rust-bin.nightly."2023-05-22".default.override { extensions = [ "rustc" "cargo" @@ -106,21 +106,20 @@ }); base = import ./nix/base.nix {inherit pkgs system;}; - - cargo-contract = import ./nix/cargo-contract.nix { - inherit craneLib pkgs; - - version = "3.0.1"; - sha256 = "sha256-4PeEq1iAZPm90hcAJnM5B6Bwj24vSc3X1BGcUB109n0="; - }; in { devShell = pkgs.mkShell { - buildInputs = - [ - rustToolchain - ] - ++ commonArgs.buildInputs - ++ commonArgs.nativeBuildInputs; + packages = [rustToolchain]; + inputsFrom = [commonArgs]; + }; + + apps.loadDockerImages = flake-utils.lib.mkApp { + drv = pkgs.writeShellScriptBin "load-docker-images" (let + mkStages = stages: + pkgs.lib.concatMapStringsSep + "\n" (stage: "docker load < ${self.packages.${system}.docker.build-stages.${stage}}") + stages; + in + mkStages (builtins.attrNames self.packages.${system}.docker.build-stages)); }; packages = { @@ -132,7 +131,7 @@ }); docker = { - ink-builder = import ./nix/ink-builder.nix {inherit base cargo-contract pkgs;}; + build-stages = import ./nix/build-stages.nix {inherit base pkgs;}; server = import ./nix/server.nix { inherit base pkgs; bins = self.packages.${system}.default; diff --git a/nix/build-stages.nix b/nix/build-stages.nix new file mode 100644 index 0000000..10aad1a --- /dev/null +++ b/nix/build-stages.nix @@ -0,0 +1,54 @@ +{ + base, + pkgs, +}: let + mkStageImage = stage: script: + pkgs.dockerTools.buildImage { + name = "stage-${stage}"; + tag = "latest"; + + fromImage = base; + + config = { + Env = [ + "BUILD_SESSION_TOKEN" + "SOURCE_CODE_URL" + "API_SERVER_URL" + ]; + + WorkingDir = "/contract"; + + Cmd = ["${pkgs.writeShellScript "stage-${stage}-script" script}"]; + }; + }; +in { + unarchive = mkStageImage "unarchive" (let + inherit (pkgs) coreutils; + + curl = pkgs.lib.getExe pkgs.curl; + unzip = pkgs.lib.getExe pkgs.unzip; + in '' + set -e + + dst=$(${coreutils}/bin/mktemp) + + ${curl} "$SOURCE_CODE_URL" \ + -o $dst + + ${unzip} $dst + + shopt -s globstar + for i in **/*.rs; do + ${curl} -f "$API_SERVER_URL"/files/upload/"$BUILD_SESSION_TOKEN" \ + -F "$i"="@$i" + done + + ${curl} -f "$API_SERVER_URL"/files/seal/"$BUILD_SESSION_TOKEN" \ + -X POST + ''); + + move = mkStageImage "move" '' + mv /contract/target/ink/*.wasm /contract/target/ink/main.wasm + mv /contract/target/ink/*.json /contract/target/ink/main.json + ''; +} diff --git a/nix/cargo-contract.nix b/nix/cargo-contract.nix deleted file mode 100644 index 6352e24..0000000 --- a/nix/cargo-contract.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ - craneLib, - pkgs, - sha256, - version, -}: { - inherit version; - - package = craneLib.buildPackage { - inherit version; - - pname = "cargo-contract"; - - src = pkgs.fetchFromGitHub { - inherit sha256; - - owner = "paritytech"; - repo = "cargo-contract"; - rev = "v${version}"; - }; - - nativeBuildInputs = with pkgs; [ - cmake - ]; - - doCheck = false; - }; -} diff --git a/nix/ink-builder.nix b/nix/ink-builder.nix deleted file mode 100644 index 67efe38..0000000 --- a/nix/ink-builder.nix +++ /dev/null @@ -1,86 +0,0 @@ -{ - base, - cargo-contract, - pkgs, -}: let - script = pkgs.writeShellScript "builder" '' - set -e - - rustup toolchain install $RUST_VERSION \ - --profile minimal \ - --component rust-src - - if [ "$CARGO_CONTRACT_VERSION" = "${cargo-contract.version}" ]; then - mkdir -p /root/.cargo/bin - ln -s ${pkgs.lib.getExe cargo-contract.package} /root/.cargo/bin/cargo-contract - else - CARGO_TARGET_DIR=/root/cargo-contract cargo install cargo-contract \ - --git https://github.com/paritytech/cargo-contract \ - --tag v"$CARGO_CONTRACT_VERSION" - - rm -rf /root/cargo-contract - fi - - mkdir source - - curl "$SOURCE_CODE_URL" \ - -o source.zip - - unzip source.zip \ - -d source - - cd source - - shopt -s globstar - for i in **/*.rs; do - curl -f "$API_SERVER_URL"/files/upload/"$BUILD_SESSION_TOKEN" \ - -F "$i"="@$i" - done - - curl -f "$API_SERVER_URL"/files/seal/"$BUILD_SESSION_TOKEN" \ - -X POST - - CARGO_TARGET_DIR=/root/artifacts cargo contract build \ - --release &> >(${pkgs.lib.getExe pkgs.ansifilter}) - - mv /root/artifacts/ink/*.wasm /root/artifacts/ink/main.wasm - mv /root/artifacts/ink/*.json /root/artifacts/ink/main.json - ''; -in - pkgs.dockerTools.buildImage { - name = "ink-builder"; - tag = "latest"; - - fromImage = base; - - copyToRoot = pkgs.buildEnv { - name = "image-root"; - pathsToLink = ["/bin" "/lib"]; - paths = with pkgs; [ - rustup - gcc - curl - unzip - git - binaryen - ]; - }; - - config = { - Env = [ - "CARGO_CONTRACT_VERSION" - "RUST_VERSION" - "SOURCE_CODE_URL" - "BUILD_SESSION_TOKEN" - "API_SERVER_URL" - ]; - - Volumes = { - "/root" = {}; - }; - - WorkingDir = "/root"; - - Cmd = ["${script}"]; - }; - }