diff --git a/Cargo.lock b/Cargo.lock index 6aa57b8..6522857 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "aho-corasick" version = "1.1.3" @@ -37,9 +22,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -52,37 +37,37 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", - "once_cell", - "windows-sys 0.59.0", + "once_cell_polyfill", + "windows-sys 0.60.2", ] [[package]] @@ -144,9 +129,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" dependencies = [ "axum-core", "bytes", @@ -160,8 +145,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rustversion", - "serde", + "serde_core", "sync_wrapper", "tower", "tower-layer", @@ -170,9 +154,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" dependencies = [ "bytes", "futures-core", @@ -181,27 +165,11 @@ dependencies = [ "http-body-util", "mime", "pin-project-lite", - "rustversion", "sync_wrapper", "tower-layer", "tower-service", ] -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - [[package]] name = "base64" version = "0.21.7" @@ -255,6 +223,24 @@ version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bollard" version = "0.19.1" @@ -332,16 +318,19 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.19.0" +name = "bs58" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] [[package]] -name = "byteorder" -version = "1.5.0" +name = "bumpalo" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytes" @@ -351,9 +340,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.36" +version = "1.2.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" +checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" dependencies = [ "find-msvc-tools", "shlex", @@ -374,14 +363,14 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] name = "clap" -version = "4.5.27" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ "clap_builder", "clap_derive", @@ -389,9 +378,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" dependencies = [ "anstream", "anstyle", @@ -401,9 +390,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck", "proc-macro2", @@ -413,15 +402,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "console" @@ -461,11 +450,30 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "darling" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ "darling_core", "darling_macro", @@ -473,9 +481,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ "fnv", "ident_case", @@ -487,9 +495,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", @@ -498,12 +506,23 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" dependencies = [ "powerfmt", - "serde", + "serde_core", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", ] [[package]] @@ -563,12 +582,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -602,9 +621,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] name = "fnv" @@ -629,9 +648,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -725,6 +744,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -745,20 +774,14 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.7+wasi-0.2.4", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "h2" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -781,9 +804,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "heck" @@ -863,13 +886,14 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", @@ -877,6 +901,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -944,9 +969,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ "base64 0.22.1", "bytes", @@ -983,6 +1008,12 @@ dependencies = [ "tower-service", ] +[[package]] +name = "i256" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8ef3cf3581bc32d50c9e593ff6bad06c85054861ea6cc640660d73490c2c316" + [[package]] name = "iana-time-zone" version = "0.1.64" @@ -1101,9 +1132,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -1138,7 +1169,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.16.0", "serde", "serde_core", ] @@ -1167,17 +1198,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -1217,9 +1237,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ "once_cell", "wasm-bindgen", @@ -1231,8 +1251,13 @@ version = "0.1.0" dependencies = [ "anyhow", "bigdecimal", + "blake2", + "bs58", "clap", + "futures", "hex", + "i256", + "indexmap 2.11.4", "insta", "insta-cmd", "regex", @@ -1240,16 +1265,19 @@ dependencies = [ "secp256k1", "serde", "serde_json", + "serde_yaml", + "sha2", "strum", "testcontainers", "tokio", + "walkdir", ] [[package]] name = "libc" -version = "0.2.175" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libm" @@ -1259,20 +1287,20 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.6" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ "bitflags 2.9.4", "libc", - "redox_syscall 0.5.13", + "redox_syscall 0.5.18", ] [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -1282,19 +1310,18 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "matchit" @@ -1304,9 +1331,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "mime" @@ -1314,15 +1341,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - [[package]] name = "mio" version = "1.0.4" @@ -1430,21 +1448,18 @@ dependencies = [ "autocfg", ] -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "openssl" version = "0.10.73" @@ -1491,9 +1506,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -1501,15 +1516,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.13", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -1583,9 +1598,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -1598,9 +1613,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] @@ -1648,9 +1663,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -1731,27 +1746,27 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags 2.9.4", ] [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", @@ -1760,9 +1775,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -1772,9 +1787,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "722166aa0d7438abbaa4d5cc2c649dac844e8c56d82fb3d33e9c34b5cd268fc6" dependencies = [ "aho-corasick", "memchr", @@ -1783,15 +1798,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "c3160422bbd54dd5ecfdca71e5fd59b7b8fe2b1697ab2baf64f6d05dcc66d298" [[package]] name = "reqwest" -version = "0.12.23" +version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64 0.22.1", "bytes", @@ -1841,30 +1856,24 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags 2.9.4", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.29" +version = "0.23.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" dependencies = [ "log", "once_cell", @@ -1884,7 +1893,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.4.0", + "security-framework 3.5.1", ] [[package]] @@ -1907,9 +1916,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" dependencies = [ "ring", "rustls-pki-types", @@ -1928,13 +1937,22 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2002,9 +2020,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.4.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ "bitflags 2.9.4", "core-foundation 0.10.1", @@ -2025,9 +2043,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -2035,18 +2053,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -2091,9 +2109,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5" dependencies = [ "base64 0.22.1", "chrono", @@ -2102,8 +2120,7 @@ dependencies = [ "indexmap 2.11.4", "schemars 0.9.0", "schemars 1.0.4", - "serde", - "serde_derive", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -2111,9 +2128,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27" dependencies = [ "darling", "proc-macro2", @@ -2121,6 +2138,30 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.11.4", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2129,9 +2170,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -2144,12 +2185,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -2179,9 +2217,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "strsim" @@ -2214,23 +2252,22 @@ dependencies = [ [[package]] name = "strum" -version = "0.26.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", "syn", ] @@ -2294,15 +2331,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.21.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2337,18 +2374,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -2357,11 +2394,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.43" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", + "itoa", "num-conv", "powerfmt", "serde", @@ -2395,31 +2433,43 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", "socket2 0.6.0", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -2438,9 +2488,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -2474,9 +2524,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -2600,6 +2650,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + [[package]] name = "ulid" version = "1.2.1" @@ -2616,6 +2672,12 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" @@ -2639,9 +2701,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -2667,6 +2729,22 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2684,30 +2762,40 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.7+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" dependencies = [ - "wit-bindgen-rt", + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", @@ -2719,9 +2807,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" dependencies = [ "cfg-if", "js-sys", @@ -2732,9 +2820,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2742,9 +2830,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", @@ -2755,18 +2843,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" dependencies = [ "js-sys", "wasm-bindgen", @@ -2788,14 +2876,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.2", + "webpki-roots 1.0.3", ] [[package]] name = "webpki-roots" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" dependencies = [ "rustls-pki-types", ] @@ -2816,6 +2904,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2824,22 +2921,22 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.62.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.0", - "windows-result 0.4.0", - "windows-strings 0.5.0", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -2848,9 +2945,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -2865,9 +2962,9 @@ checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-registry" @@ -2891,11 +2988,11 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -2909,11 +3006,11 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -2940,7 +3037,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", ] [[package]] @@ -2961,18 +3067,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -2983,9 +3090,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -2995,9 +3102,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -3007,9 +3114,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -3019,9 +3126,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -3031,9 +3138,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -3043,9 +3150,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -3055,9 +3162,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -3067,18 +3174,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.4", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" @@ -3088,9 +3192,9 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "xattr" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", "rustix", @@ -3122,19 +3226,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", @@ -3164,9 +3267,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" @@ -3181,9 +3284,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", diff --git a/Cargo.toml b/Cargo.toml index d64f032..0f02152 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,15 +37,23 @@ use-self = "warn" [dependencies] anyhow = "1.0.100" bigdecimal = "0.4.8" +blake2 = "0.10.6" +bs58 = "0.5.1" clap = { version = "4", features = ["derive", "env"] } -regex = "1.11.2" -reqwest = { version = "0.12.23", features = ["json"] } -serde = {version = "1.0.226", features = ["derive"] } -serde_json = "1.0.145" -strum = { version = "0.26.3", features = ["derive"] } +futures = { version = "0.3.31", default-features = false } hex = "0.4.3" +i256 = "0.2.3" +indexmap = {version = "2.11.4", features = ["serde"]} +regex = "1.12.2" +reqwest = { version = "0.12.24", features = ["json"] } secp256k1 = "0.30.0" -tokio = { version = "1.47.1", features = ["full"] } +serde = { version = "1.0.228", features = ["derive"] } +serde_json = { version = "1.0.145", features = ["raw_value"] } +serde_yaml = "0.9.34" +sha2 = "0.10.9" +strum = { version = "0.27.2", features = ["derive"] } +tokio = { version = "1.48.0", features = ["full"] } +walkdir = "2.5.0" [dev-dependencies] insta = { version = "1", features = ["filters"] } diff --git a/README.md b/README.md index e112710..216940b 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ Here are some examples of how to use the Kermit CLI: 4. Compile a contract: ```bash - kermit contracts compile tests/sub_contract.ral --contract-type project + kermit contracts compile tests/contracts/sub_contract.ral --contract-type project ``` You can do again more with kermit. I let you check the `kermit --help`, to take a look on all possibilities diff --git a/src/account/account_struct.rs b/src/account/account_struct.rs new file mode 100644 index 0000000..a37d8d4 --- /dev/null +++ b/src/account/account_struct.rs @@ -0,0 +1,23 @@ +use anyhow::Result; + +use crate::account::{address::Address, signature::PrivateKey}; + +pub struct Account { + pub private_key: Box, + pub address: Address, + pub group: u8, +} + +impl Account { + pub fn new(private_key: Box) -> Result { + let public_key = private_key.get_public_key()?; + let address = Address::new(&public_key)?; + let group = address.group_from_bytes(); + + Ok(Self { + private_key, + address, + group, + }) + } +} diff --git a/src/account/address.rs b/src/account/address.rs new file mode 100644 index 0000000..8edabcf --- /dev/null +++ b/src/account/address.rs @@ -0,0 +1,96 @@ +use anyhow::{Context, Result, bail}; +use blake2::{ + Blake2bVar, + digest::{Update, VariableOutput}, +}; +use bs58; +use serde::{Deserialize, Serialize, Serializer}; + +use crate::common::{djb2, xor_byte}; + +const TOTAL_NUMBER_OF_GROUPS: u8 = 4; + +#[repr(u8)] +#[allow(dead_code)] +pub enum AddressType { + P2PKH = 0x00, + P2MPKH = 0x01, + P2SH = 0x02, + P2C = 0x03, + P2PK = 0x04, + P2HMPK = 0x05, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Address { + pub key: String, + pub full_bytes: Vec, + pub bytes: Vec, +} + +impl Serialize for Address { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.key) + } +} + +impl<'de> Deserialize<'de> for Address { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let key = String::deserialize(deserializer)?; + Self::new(&key).map_err(serde::de::Error::custom) + } +} + +impl Address { + pub fn new(public_key_str: &str) -> Result { + let public_key_bytes = hex::decode(public_key_str).context("Invalid hex")?; + let mut hasher = Blake2bVar::new(32).context("Failed to create Blake2bVar")?; + hasher.update(&public_key_bytes); + let mut hash_bytes = [0u8; 32]; + hasher + .finalize_variable(&mut hash_bytes) + .context("Failed to finalize Blake2bVar")?; + let hash_bytes = &hash_bytes[..32]; + + let mut address_bytes = Vec::with_capacity(1 + 32); + address_bytes.push(AddressType::P2PKH as u8); + address_bytes.extend_from_slice(hash_bytes); + + Ok(Self { + key: bs58::encode(&address_bytes).into_string(), + full_bytes: address_bytes, + bytes: hash_bytes.into(), + }) + } + + pub fn new_b58(b58_str: &str) -> Result { + let address_bytes = bs58::decode(b58_str) + .into_vec() + .context("Invalid base58 string")?; + if address_bytes.len() < 33 { + bail!("Invalid address length"); + } + + let key = b58_str.to_string(); + let full_bytes = address_bytes.clone(); + let bytes = address_bytes[1..].to_vec(); // The length > 1 was already checked + + Ok(Self { + key, + full_bytes, + bytes, + }) + } + + pub fn group_from_bytes(&self) -> u8 { + let hint = djb2(&self.bytes) | 1; + let hash = xor_byte(hint); + hash % TOTAL_NUMBER_OF_GROUPS + } +} diff --git a/src/account/mod.rs b/src/account/mod.rs new file mode 100644 index 0000000..de291fe --- /dev/null +++ b/src/account/mod.rs @@ -0,0 +1,3 @@ +pub mod account_struct; +pub mod address; +pub mod signature; diff --git a/src/account/signature.rs b/src/account/signature.rs new file mode 100644 index 0000000..89c49ec --- /dev/null +++ b/src/account/signature.rs @@ -0,0 +1,64 @@ +use anyhow::{Context, Result, anyhow, bail}; +use secp256k1::{Message, PublicKey, Secp256k1, SecretKey}; + +#[allow(dead_code)] +pub trait PrivateKey { + fn as_hex(&self) -> String; + fn get_public_key(&self) -> Result; + fn sign(&self, tx_id: &str) -> Result; +} + +#[allow(dead_code)] +pub struct GLSecp256k1PrivateKey { + pub hex_key: String, + pub key: SecretKey, +} + +impl PrivateKey for GLSecp256k1PrivateKey { + fn as_hex(&self) -> String { + self.hex_key.clone() + } + + fn get_public_key(&self) -> Result { + let secp = Secp256k1::new(); + let public_key = PublicKey::from_secret_key(&secp, &self.key); + Ok(public_key.to_string()) + } + + fn sign(&self, tx_id: &str) -> Result { + let tx_id_bytes = hex::decode(tx_id)?; + let message = Message::from_digest( + tx_id_bytes + .try_into() + .map_err(|_| anyhow!("Invalid hash length"))?, + ); + + let secp = Secp256k1::new(); + let signature = secp.sign_ecdsa(&message, &self.key); + let serialized = signature.serialize_compact(); + let signature = hex::encode(serialized); + + Ok(signature) + } +} + +#[allow(dead_code)] +impl GLSecp256k1PrivateKey { + pub fn new(key: &str) -> Result { + if !Self::is_valid(key) { + bail!("Invalid private key format"); + } + + let private_key = SecretKey::from_slice(&hex::decode(key).context("Invalid hex key")?) + .context("Failed to create secret key")?; + + Ok(Self { + key: private_key, + hex_key: key.to_string(), + }) + } + + fn is_valid(hex_key: &str) -> bool { + hex_key.len() == 64 && hex_key.chars().all(|c| c.is_ascii_hexdigit()) + } +} diff --git a/src/addresses.rs b/src/addresses.rs index 4df310c..817c817 100644 --- a/src/addresses.rs +++ b/src/addresses.rs @@ -1,11 +1,11 @@ use anyhow::Result; use clap::Parser; -use crate::common::{get, print_output}; +use crate::common::{check_network, get, print_output}; /// CLI arguments for `kermit addresses`. #[derive(Parser)] -pub(crate) enum AddressesSubcommands { +pub enum AddressesSubcommands { /// Get the balance of an address. #[command(visible_alias = "b")] Balance { @@ -28,7 +28,9 @@ pub(crate) enum AddressesSubcommands { } impl AddressesSubcommands { - pub(crate) async fn run(self, url: String) -> Result<()> { + pub async fn run(self, url: &str) -> Result<()> { + check_network(url).await?; + let endpoint = match self { Self::Balance { address, mem_pool } => { format!("/addresses/{address}/balance?mempool={mem_pool}") @@ -47,7 +49,7 @@ impl AddressesSubcommands { }, }; - let output = get(&url, &endpoint).await?; + let output = get(url, &endpoint).await?; print_output(output)?; Ok(()) diff --git a/src/args.rs b/src/args.rs index e5f2f2d..4fae6bf 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,14 +1,19 @@ use clap::{Parser, Subcommand, ValueHint}; use crate::{ - addresses::AddressesSubcommands, blockflow::BlockflowSubcommands, - contracts::ContractsSubcommands, infos::InfosSubcommands, miners::MinersSubcommands, - transactions::TransactionsSubcommands, utils::UtilsSubcommands, wallets::WalletsSubcommands, + addresses::AddressesSubcommands, + blockflow::BlockflowSubcommands, + contracts::{ContractsSubcommands, NetworkType}, + infos::InfosSubcommands, + miners::MinersSubcommands, + transactions::TransactionsSubcommands, + utils::UtilsSubcommands, + wallets::WalletsSubcommands, }; #[derive(Parser)] #[command(version)] -pub(crate) struct Kermit { +pub struct Kermit { #[clap(long, short, env, value_hint = ValueHint::Url, default_value = "http://localhost:22973")] pub url: String, @@ -18,7 +23,7 @@ pub(crate) struct Kermit { } #[derive(Subcommand)] -pub(crate) enum KermitSubcommand { +pub enum KermitSubcommand { /// Address management utilities. #[command(visible_alias = "a")] Addresses { @@ -38,6 +43,19 @@ pub(crate) enum KermitSubcommand { Contracts { #[command(subcommand)] command: ContractsSubcommands, + + /// Path to the config YAML file + #[arg(long, short, default_value = "./alephium.config.yaml")] + config_file_path: String, + + /// Create the config file for contracts automatically if not set + #[arg(long, default_value_t = false)] + auto_create_config_file: bool, + + /// Network type may trigger a different behavior in contract + /// operations. Choose accordingly + #[arg(long, short, value_enum, default_value_t = NetworkType::Dev)] + network: NetworkType, }, /// Infos about node and hashrate. diff --git a/src/blockflow.rs b/src/blockflow.rs index 5799f3c..3ae8ea3 100644 --- a/src/blockflow.rs +++ b/src/blockflow.rs @@ -1,11 +1,11 @@ use anyhow::Result; use clap::Parser; -use crate::common::{get, print_output}; +use crate::common::{check_network, get, print_output}; /// CLI arguments for `kermit blockflow`. #[derive(Parser)] -pub(crate) enum BlockflowSubcommands { +pub enum BlockflowSubcommands { /// List blocks on the given time interval. #[command(visible_alias = "bs")] Blocks { from_ts: i64, to_ts: Option }, @@ -62,7 +62,9 @@ pub(crate) enum BlockflowSubcommands { } impl BlockflowSubcommands { - pub(crate) async fn run(self, url: &str) -> Result<()> { + pub async fn run(self, url: &str) -> Result<()> { + check_network(url).await?; + let endpoint = match self { Self::Blocks { from_ts, to_ts } => { let mut endpoint = format!("/blockflow/blocks?fromTs={from_ts}"); diff --git a/src/common/crypto.rs b/src/common/crypto.rs new file mode 100644 index 0000000..ff2d084 --- /dev/null +++ b/src/common/crypto.rs @@ -0,0 +1,29 @@ +pub fn djb2(bytes: &[u8]) -> i32 { + let mut hash: i32 = 5381; + for &byte in bytes { + hash = (hash << 5).wrapping_add(hash).wrapping_add(byte.into()); + } + hash +} + +pub const fn xor_byte(int_value: i32) -> u8 { + let byte0 = ((int_value >> 24) & 0xff) as u8; + let byte1 = ((int_value >> 16) & 0xff) as u8; + let byte2 = ((int_value >> 8) & 0xff) as u8; + let byte3 = (int_value & 0xff) as u8; + byte0 ^ byte1 ^ byte2 ^ byte3 +} + +pub fn is_hex_string(input: &str) -> bool { + input.starts_with("0x") + && (input.len() - 2).is_multiple_of(2) + && input[2..].chars().all(|c| c.is_ascii_hexdigit()) +} + +pub fn is_b58(input: &str) -> bool { + input.starts_with("b58:") + && input[4..].chars().all(|c| { + c.is_ascii_alphanumeric() + && "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".contains(c) + }) +} diff --git a/src/common/fs.rs b/src/common/fs.rs new file mode 100644 index 0000000..f00dec4 --- /dev/null +++ b/src/common/fs.rs @@ -0,0 +1,40 @@ +use std::{ + fs, + io::{ErrorKind, Write}, +}; + +use anyhow::{Context, Result}; + +pub fn read_file(path: &str) -> Result { + fs::read_to_string(path).context(match fs::metadata(path) { + Ok(metadata) if metadata.is_dir() => { + format!("Path points to a directory, not a file: {path}") + }, + Err(e) if e.kind() == ErrorKind::NotFound => { + format!("File does not exist at path: {path}") + }, + Err(e) if e.kind() == ErrorKind::PermissionDenied => { + format!("No permission to read the file at path: {path}") + }, + Ok(_) => "".to_string(), + Err(_) => format!("Failed to access the file at path: {path}"), + }) +} + +pub fn write_file(path: &str, contents: &str) -> Result<()> { + let mut file = fs::File::create(path).context(match fs::metadata(path) { + Ok(metadata) if metadata.is_dir() => { + format!("Path points to a directory, not a file: {path}") + }, + Err(e) if e.kind() == ErrorKind::NotFound => { + format!("File does not exist at path: {path}") + }, + Err(e) if e.kind() == ErrorKind::PermissionDenied => { + format!("No permission to write to the file at path: {path}") + }, + Ok(_) => "".to_string(), + Err(_) => format!("Failed to access the file at path: {path}"), + })?; + file.write_all(contents.as_bytes()) + .context(format!("Failed to write to file at path: {path}")) +} diff --git a/src/common/mod.rs b/src/common/mod.rs index 6d781ec..b58518e 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,5 +1,14 @@ -mod reqwest; -pub(crate) use reqwest::*; +mod crypto; +pub use crypto::*; + +mod fs; +pub use fs::*; + +mod network; +pub use network::*; mod print; -pub(crate) use print::*; +pub use print::*; + +mod reqwest; +pub use reqwest::*; diff --git a/src/common/network.rs b/src/common/network.rs new file mode 100644 index 0000000..4b21594 --- /dev/null +++ b/src/common/network.rs @@ -0,0 +1,21 @@ +use anyhow::{Result, bail}; +use serde_json::Value; + +use crate::common::reqwest::get; + +async fn is_network_alive(url: &str) -> bool { + get::(url, "/infos/version").await.is_ok() +} + +// See if the network is available before continuing +pub async fn check_network(url: &str) -> Result<()> { + if reqwest::Url::parse(url).is_err() { + bail!("Invalid node URL: {url}"); + } + + if !is_network_alive(url).await { + bail!("Network is not reachable: {url}"); + } + + Ok(()) +} diff --git a/src/common/print.rs b/src/common/print.rs index 444fdcc..cad29f7 100644 --- a/src/common/print.rs +++ b/src/common/print.rs @@ -1,7 +1,7 @@ use anyhow::Result; use serde_json::Value; -pub(crate) fn print_output(output: Option) -> Result<()> { +pub fn print_output(output: Option) -> Result<()> { if let Some(output) = output { serde_json::to_writer_pretty(std::io::stdout(), &output)?; println!(); diff --git a/src/common/reqwest.rs b/src/common/reqwest.rs index 6fa12c8..3f831f6 100644 --- a/src/common/reqwest.rs +++ b/src/common/reqwest.rs @@ -8,9 +8,8 @@ struct Error { } /// Perform a GET request to the given URL -pub(crate) async fn get(url: &str, endpoint: &str) -> Result> { +pub async fn get(url: &str, endpoint: &str) -> Result> { let client = Client::new(); - let url = format!("{url}{endpoint}"); let res = client @@ -28,7 +27,8 @@ pub(crate) async fn get(url: &str, endpoint: &str) -> Resul } let data = if res.content_length() > Some(0) { - Some(res.json().await?) + let data = res.json().await?; + Some(data) } else { None }; @@ -37,7 +37,7 @@ pub(crate) async fn get(url: &str, endpoint: &str) -> Resul } /// Perform a POST request to the given URL -pub(crate) async fn post( +pub async fn post( url: &str, endpoint: &str, body: U, @@ -62,7 +62,8 @@ pub(crate) async fn post( } let data = if res.content_length() > Some(0) { - Some(res.json().await?) + let data = res.json().await?; + Some(data) } else { None }; @@ -71,7 +72,7 @@ pub(crate) async fn post( } /// Perform a PUT request to the given URL -pub(crate) async fn put( +pub async fn put( url: &str, endpoint: &str, body: U, @@ -96,7 +97,8 @@ pub(crate) async fn put( } let data = if res.content_length() > Some(0) { - Some(res.json().await?) + let data = res.json().await?; + Some(data) } else { None }; @@ -105,7 +107,7 @@ pub(crate) async fn put( } /// Perform a DELETE request to the given URL -pub(crate) async fn delete(url: &str, endpoint: &str) -> Result> { +pub async fn delete(url: &str, endpoint: &str) -> Result> { let client = Client::new(); let url = format!("{url}{endpoint}"); @@ -125,7 +127,8 @@ pub(crate) async fn delete(url: &str, endpoint: &str) -> Re } let data = if res.content_length() > Some(0) { - Some(res.json().await?) + let data = res.json().await?; + Some(data) } else { None }; diff --git a/src/config/config_contracts.rs b/src/config/config_contracts.rs new file mode 100644 index 0000000..913a2b6 --- /dev/null +++ b/src/config/config_contracts.rs @@ -0,0 +1,79 @@ +use std::collections::HashMap; + +use serde::{ + Deserialize, Deserializer, Serialize, + de::{self, Error}, +}; +use serde_yaml::Value; + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Asset { + pub atto_alph_amount: String, + pub tokens: Option>, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct InputAsset { + pub address: String, + pub asset: Asset, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Token { + pub id: String, + pub amount: String, +} + +#[derive(Debug, Clone, Serialize)] +pub enum HelperFieldType { + String(String), + Array(Vec), + Structure(HashMap), +} + +impl<'de> Deserialize<'de> for HelperFieldType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = Value::deserialize(deserializer)?; + Ok(match value { + Value::String(s) => Self::String(s), + Value::Bool(b) => Self::String(b.to_string()), + Value::Number(n) => Self::String(n.to_string()), + Value::Mapping(s) => { + let map: Result, _> = s + .into_iter() + .map(|(k, v)| { + let k = k + .as_str() + .ok_or_else(|| Error::custom("Key is not a string"))?; + let v = Self::deserialize(v) + .map_err(|_| Error::custom("Failed to deserialize value"))?; + Ok((k.to_string(), v)) + }) + .collect(); + Self::Structure(map?) + }, + Value::Sequence(seq) => { + let arr: Result, _> = seq + .into_iter() + .map(|v| Self::deserialize(v).map_err(Error::custom)) + .collect(); + Self::Array(arr?) + }, + _ => return Err(de::Error::custom("unsupported type for HelperFieldType")), + }) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ConfigContract { + pub initial_fields: HashMap, // can be anything + pub initial_asset: Asset, + pub input_assets: Vec, +} diff --git a/src/config/config_struct.rs b/src/config/config_struct.rs new file mode 100644 index 0000000..80b98ca --- /dev/null +++ b/src/config/config_struct.rs @@ -0,0 +1,50 @@ +use std::{collections::HashMap, path::Path}; + +use anyhow::{Context, Result, anyhow}; +use serde::{Deserialize, Serialize}; + +use crate::{ + common::{read_file, write_file}, + config::config_contracts::ConfigContract, +}; + +#[derive(Debug, Deserialize, Serialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct Config { + pub contracts: Option>, +} + +impl Config { + pub fn new(config_path: &str, create_if_not_exist: bool) -> Result { + let path = Path::new(config_path); + let path_str = path.to_str().context("Invalid config file path")?; + + // Create default "alephium.config.yaml" if it doesn't exist + if create_if_not_exist && !path.exists() { + let default_config = Self::default(); + let yaml = serde_yaml::to_string(&default_config) + .map_err(|e| anyhow!("Failed to serialize default config: {e}"))?; + + println!( + "Contract config file not found at {config_path}. Creating default config file\ + at {path_str} ..." + ); + + write_file(path_str, &yaml)?; + + return Ok(default_config); + } + + let config_content = read_file(path_str)?; + + let mut config = serde_yaml::from_str::(&config_content) + .map_err(|e| anyhow!("Failed to parse config file: {config_path} : {e}"))?; + + config.apply_merge()?; + + let config = serde_yaml::from_value::(config) + .map_err(|e| anyhow!("Failed to parse config file: {config_path} : {e}"))?; + + Ok(config) + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..43fe0d1 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,2 @@ +pub mod config_contracts; +pub mod config_struct; diff --git a/src/contract_encoding.rs b/src/contract_encoding.rs deleted file mode 100644 index 854ee9a..0000000 --- a/src/contract_encoding.rs +++ /dev/null @@ -1,54 +0,0 @@ -use anyhow::Result; -use serde_json::Value; - -use crate::contracts::NetworkType; - -fn _build_debug_bytecode(bytecode: &str, bytecode_patch: &str) -> Result { - if bytecode_patch.is_empty() { - return Ok(bytecode.to_string()); - } - - let pattern = regex::Regex::new(r"[=+-][0-9a-f]*")?; - let mut result = String::new(); - let mut index = 0; - - for parts in pattern.find_iter(bytecode_patch) { - let part = parts.as_str(); - let diff_type = part.chars().next().unwrap(); - - match diff_type { - '=' => { - let length = part[1..].parse::().unwrap(); - result.push_str(&bytecode[index..index + length]); - index += length; - }, - '+' => { - result.push_str(&part[1..]); - }, - '-' => { - let length = part[1..].parse::().unwrap(); - index += length; - }, - _ => {}, - } - } - - Ok(result) -} - -pub fn _encode_contract_fields( - byte_code: &str, - byte_code_debug: &str, - network: &NetworkType, - _fields: &Value, -) -> Result { - // If devnet, use build_debug_bytecode to patch the compiled bytecode with - // bytecodeDebugPatch - // Encode the values of the fields and concat it - let _byte_code = match network { - NetworkType::Dev => _build_debug_bytecode(byte_code, byte_code_debug)?, - _ => byte_code.to_string(), - }; - - todo!() -} diff --git a/src/contracts.rs b/src/contracts.rs index 3d6a3ca..38e0571 100644 --- a/src/contracts.rs +++ b/src/contracts.rs @@ -1,240 +1,345 @@ -use std::{fs::File, io::Read, path::Path}; - use anyhow::{Context, Result}; -use clap::{Parser, ValueEnum}; -use regex::Regex; -use reqwest::Client; -use serde_json::{Value, json}; +use clap::{Args, Parser, ValueEnum}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; use strum::Display; -use crate::contract_encoding::_encode_contract_fields; +use crate::{ + account::{ + address::Address, + signature::{GLSecp256k1PrivateKey, PrivateKey}, + }, + common::check_network, + config::config_struct::Config, + contracts_funcs::{ + call::call_contract, + compile::compile, + compile_project::{ + compile_project_structs::{FieldsTypesMapMut, FieldsVec, load_compile_project}, + compile_project_values::config_fields_to_vec, + }, + deploy::deploy_contract, + state::{code, parent, state, sub_contracts, sub_contracts_current_count}, + test::test_contract, + }, +}; #[derive(Clone, Debug, Display, ValueEnum)] -pub enum ContractType { +pub enum CompiledType { Contract, Script, - Project, } -#[derive(Clone, Debug, Display, ValueEnum)] +#[derive(Clone, Copy, Debug, Display, ValueEnum, PartialEq, Eq)] pub enum NetworkType { - Main, - Test, - Dev, + Main = 0, + Test = 1, + Dev = 4, +} + +#[derive(Debug, Clone, Args, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CompilerOptions { + /// Ignore warnings related to external caller checks. + #[arg(long, default_value_t = false)] + pub ignore_check_external_caller_warnings: bool, + + /// Ignore warnings about unused constants in the code. + #[arg(long, default_value_t = false)] + pub ignore_unused_constants_warnings: bool, + + /// Ignore warnings about unused fields in the code. + #[arg(long, default_value_t = false)] + pub ignore_unused_fields_warnings: bool, + + /// Ignore warnings about unused function return values. + #[arg(long, default_value_t = false)] + pub ignore_unused_function_return_warnings: bool, + + /// Ignore warnings about unused private functions in the code. + #[arg(long, default_value_t = false)] + pub ignore_unused_private_functions_warnings: bool, + + /// Ignore warnings about unused variables in the code. + #[arg(long, default_value_t = false)] + pub ignore_unused_variables_warnings: bool, + + /// Ignore warnings related to update fields checks. + #[arg(long, default_value_t = false)] + pub ignore_update_fields_check_warnings: bool, + + /// Skip checks for abstract contracts. + #[arg(long, default_value_t = false)] + pub skip_abstract_contract_check: bool, + + /// Skip running tests during the compilation process. + #[arg(long, default_value_t = false)] + pub skip_tests: bool, +} + +fn parse_key_val(s: &str) -> Result<(String, String), String> { + let parts: Vec<&str> = s.splitn(2, '=').collect(); + if parts.len() != 2 { + return Err(format!("Invalid KEY=VALUE: '{s}'")); + } + if let (Some(key), Some(value)) = (parts.first(), parts.get(1)) { + Ok((key.to_string(), value.to_string())) + } else { + Err("Failed to parse key-value pair".to_string()) + } } #[derive(Parser)] pub enum ContractsSubcommands { - #[command(visible_alias = "c")] + #[command(visible_alias = "comp")] Compile { file_path: String, - #[arg(long, default_value_t = ContractType::Project)] - contract_type: ContractType, - #[arg(long)] - compiler_options_path: Option, + + #[command(flatten)] + compiler_options: CompilerOptions, + + /// Show detailed debug information such as error stack traces. + #[arg(long, default_value_t = false)] + debug: bool, + + /// Enable force recompile. + #[arg(long, default_value_t = false)] + force: bool, }, #[command(visible_alias = "d")] Deploy { - public_key: String, - #[arg(long, default_value_t = NetworkType::Main)] - network: NetworkType, + #[arg(long, value_enum, default_value_t = CompiledType::Contract)] + compiled_type: CompiledType, + contract_name: String, compile_output_path: String, - #[arg(long, default_value_t = ContractType::Project)] - contract_type: ContractType, + #[arg(long, env)] + private_key: String, + + /// "Amount of tokens to issue" + #[arg(long, default_value_t = 100)] + issue_token_amount: u64, }, -} + #[command(visible_alias = "s")] + State { + contract_id: String, + }, + #[command(visible_alias = "t")] + Test { + contract_name: String, + contract_id: String, + compile_output_path: String, + method_name: String, -fn get_file_buffer(file_path: &str) -> Result { - let mut file = File::open(file_path)?; - let mut buffer = String::new(); - file.read_to_string(&mut buffer)?; - Ok(buffer) -} + #[arg(long = "args", value_parser = parse_key_val, num_args = 1..)] + args: Vec<(String, String)>, -pub async fn compile_file( - url: &str, - compiler_options_path: Option<&str>, - file_buffer: &str, - end_point: &str, -) -> Result<()> { - let client = Client::new(); - let url = format!("{url}/contracts{end_point}"); - - let compiler_options = match compiler_options_path { - Some(path) => { - let mut option_file = File::open(&path)?; - let mut buffer = String::new(); - option_file.read_to_string(&mut buffer)?; - buffer.into() - }, - None => { - json!({ - "ignoreUnusedConstantsWarnings": true - }) - }, - }; + /// List of existing contracts to include in the test + #[arg(long = "existing-contracts", value_name = "CONTRACT_ID", num_args = 0..)] + exiting_contracts: Vec, + }, + #[command(visible_alias = "c")] + Call { + contract_name: String, + contract_id: String, + compile_output_path: String, // It assumes the user has the source code + method_name: String, - let body = json!({ - "code": file_buffer, - "compilerOptions": compiler_options - }); + #[arg(long = "args", value_parser = parse_key_val, num_args = 1..)] + args: Vec<(String, String)>, - let response = client.post(url).json(&body).send().await?; - let value: Value = response.json().await?; + #[arg(long, env)] + private_key: String, - serde_json::to_writer_pretty(std::io::stdout(), &value)?; - println!(); + /// List of existing contracts to include in the test + #[arg(value_name = "CONTRACT_ADDRESS", num_args = 0..)] + interested_contracts: Vec, - Ok(()) -} + /// Block hash to use for the call + #[arg(long)] + block_hash: Option, + }, + Code { + code_hash: String, + }, + Parent { + address: String, + }, + SubContracts { + address: String, -pub async fn compile_project( - url: &str, - compiler_options_path: Option<&str>, - file_path: &str, -) -> Result<()> { - let re = Regex::new(r#"^import "[^"./]+/[^"]*[a-z][a-z_0-9]*(\.ral)?"#)?; - - let file_path = Path::new(file_path); - let project_cwd = file_path - .parent() - .context("Invalid file path")? - .canonicalize()?; - - let mut buffer = String::new(); - File::open(file_path)?.read_to_string(&mut buffer)?; - - let full_buffer = buffer - .lines() - .into_iter() - .map(|line| { - if !re.is_match(line) { - return Ok(line.to_string()); - } - let trimmed = line.trim(); - let line = trimmed.split_whitespace().collect::>(); - - let import_file = if let Some(second) = line.get(1) { - second.to_string().trim_matches('"').to_string() - } else { - String::new() - }; - - // handle with missing .ral, concat it - - let path_buf = if import_file.starts_with("std") { - std::env::current_dir()?.join("contracts").join(import_file) - } else { - project_cwd.join(import_file).into() - }; - - let path = path_buf.to_str().context("Invalid path")?; - - Ok(get_file_buffer(path)?) - }) - .collect::>>()?; - - compile_file( - url, - compiler_options_path, - &full_buffer.join("\n"), - "/compile-project", - ) - .await -} + /// Start index for pagination + #[arg(long, default_value_t = 0)] + start: i32, -pub async fn deploy_contract( - url: &str, - public_key: &str, - network: NetworkType, - compile_output_path: &str, -) -> Result<()> { - let client = Client::new(); - let url = format!("{url}/contracts/unsigned-tx/deploy-contract"); - - let compile_output: Value = { - let mut file = File::open(&compile_output_path)?; - let mut buffer = String::new(); - file.read_to_string(&mut buffer)?; - serde_json::from_str(&buffer)? - }; - - let contracts = compile_output["contracts"] - .as_array() - .context("contracts Not an array")? - .get(0) - .context("no contracts in array"); - - for contract in contracts.iter() { - let byte_code = contract["bytecode"] - .as_str() - .context("Bytecode not found")?; - let byte_code_debug = contract["bytecodeDebugPatch"] - .as_str() - .context("Bytecode not found")?; - - let fields = &contract["fields"]; - let final_byte_code = - _encode_contract_fields(byte_code, byte_code_debug, &network, fields)?; - - let body = json!({ - "fromPublicKey": public_key, - "bytecode": final_byte_code, - }); - - let response = client.post(&url).json(&body).send().await?; - let json_response = response.json::().await?; - - println!("Deployment response: {:#?}", json_response); - } + /// Number of results to return + #[arg(long)] + limit: Option, + }, + SubContractsCurrentCount { + address: String, + }, +} - Ok(()) +fn get_contract_initial_fields( + config: &Config, + contract_name: &str, + contract_fields_types: &FieldsTypesMapMut, +) -> Result { + let contracts_map = config + .contracts + .to_owned() + .context("No 'contracts' field in config")?; + let config_contract = contracts_map + .get(contract_name) + .context(format!("Contract '{contract_name}' not found in config"))? + .to_owned(); + + config_fields_to_vec(&config_contract.initial_fields, contract_fields_types) } impl ContractsSubcommands { - pub async fn run(self, url: String) -> Result<()> { - match self { + pub async fn run( + self, + url: &str, + config_file_path: &str, + network_id: NetworkType, + auto_create_config_file: bool, + ) -> Result<()> { + check_network(url).await?; + + let value: Value = match self { Self::Compile { - contract_type, - compiler_options_path, file_path, - } => match contract_type { - ContractType::Contract => { - compile_file( - &url, - compiler_options_path.as_deref(), - &get_file_buffer(&file_path)?, - "/compile-contract", - ) - .await? - }, - ContractType::Project => { - compile_project(&url, compiler_options_path.as_deref(), &file_path).await? - }, - ContractType::Script => { - compile_file( - &url, - compiler_options_path.as_deref(), - &get_file_buffer(&file_path)?, - "/compile-script", - ) - .await? - }, - }, + compiler_options, + debug, + force, + } => compile(url, &file_path, compiler_options, debug, force).await?, Self::Deploy { - contract_type, - public_key, - network, + compiled_type, + contract_name, + compile_output_path, + private_key, + issue_token_amount, + } => { + let compiled_project = load_compile_project(&compile_output_path)?; + let config = Config::new(config_file_path, auto_create_config_file)?; + + match compiled_type { + CompiledType::Contract => { + let contract = compiled_project.get_contract_by_name(&contract_name)?; + + let initial_fields = get_contract_initial_fields( + &config, + &contract_name, + &contract.fields_types, + )?; + + let private_key: Box = + Box::new(GLSecp256k1PrivateKey::new(&private_key)?); + + deploy_contract( + url, + private_key, + network_id, + contract, + initial_fields, + issue_token_amount, + ) + .await? + }, + CompiledType::Script => { + unimplemented!("Script deployment is not implemented yet"); + }, + } + }, + Self::State { contract_id } => state(url, &contract_id).await?, + Self::Test { + method_name, + contract_name, + contract_id, compile_output_path, - } => match contract_type { - // Problems with devnet bytecode, doesn't deploy - ContractType::Contract => { - deploy_contract(&url, &public_key, network, &compile_output_path).await? - }, - _ => unimplemented!("Contract type not supported yet"), + args, + exiting_contracts: existing_contracts, + } => { + let compiled_project = load_compile_project(&compile_output_path)?; + let contract = compiled_project.get_contract_by_name(&contract_name)?; + let config = Config::new(config_file_path, auto_create_config_file)?; + + let contracts_map = config + .contracts + .to_owned() + .context("No 'contracts' field in config")?; + let config_contract = contracts_map + .get(&contract.name) + .context(format!("Contract '{contract_name}' not found in config"))? + .to_owned(); + + let initial_fields = + config_fields_to_vec(&config_contract.initial_fields, &contract.fields_types)?; + + test_contract( + url, + &method_name, + (contract, &contract_id, existing_contracts), + initial_fields, + &config_contract.initial_asset, + &config_contract.input_assets, + args, + ) + .await? + }, + Self::Call { + contract_name, + contract_id, + compile_output_path, + method_name, + args, + private_key, + interested_contracts, + block_hash, + } => { + let compiled_project = load_compile_project(&compile_output_path)?; + let contract = compiled_project.get_contract_by_name(&contract_name)?; + let config = Config::new(config_file_path, auto_create_config_file)?; + + let contracts_map = config + .contracts + .to_owned() + .context("No 'contracts' field in config")?; + let config_contract = contracts_map + .get(&contract.name) + .context(format!("Contract '{contract_name}' not found in config"))? + .to_owned(); + + let private_key: Box = + Box::new(GLSecp256k1PrivateKey::new(&private_key)?); + let address = Address::new(&private_key.get_public_key()?)?; + + call_contract( + url, + &method_name, + (contract, &contract_id, interested_contracts), + &address, + &config_contract.input_assets, + args, + block_hash, + ) + .await? + }, + Self::Code { code_hash } => code(url, &code_hash).await?, + Self::Parent { address } => parent(url, &address).await?, + Self::SubContracts { + address, + start, + limit, + } => sub_contracts(url, &address, start, limit).await?, + Self::SubContractsCurrentCount { address } => { + sub_contracts_current_count(url, &address).await? }, - } + }; + serde_json::to_writer_pretty(std::io::stdout(), &value)?; + println!(); Ok(()) } } diff --git a/src/contracts_funcs/call.rs b/src/contracts_funcs/call.rs new file mode 100644 index 0000000..70f362c --- /dev/null +++ b/src/contracts_funcs/call.rs @@ -0,0 +1,40 @@ +use anyhow::{Context, Result}; +use serde_json::{Value, json}; + +use crate::{ + account::address::Address, + common::post, + config::config_contracts::InputAsset, + contracts_funcs::compile_project::compile_project_structs::{ + CompiledContract, args_to_fields_vec, + }, +}; + +pub async fn call_contract( + url: &str, + method_name: &str, + contract_info: (&CompiledContract, &str, Vec), + address: &Address, + input_assets: &Vec, + args: Vec<(String, String)>, + block_hash: Option, +) -> Result { + let (contract, contract_id, interested_contracts) = contract_info; + let (method, method_index) = contract.get_method(method_name)?; + + let args = args_to_fields_vec(args, &method.params_types)?; + + let body = json!({ + "group": address.group_from_bytes(), + "address": contract_id, + "inputAssets": input_assets, + "methodIndex": method_index, + "args": args, + "interestedContracts": interested_contracts, + "worldStateBlockHash": block_hash + }); + + post(url, "/contracts/call-contract", body) + .await? + .context("Empty reply") +} diff --git a/src/contracts_funcs/compile.rs b/src/contracts_funcs/compile.rs new file mode 100644 index 0000000..4208ad2 --- /dev/null +++ b/src/contracts_funcs/compile.rs @@ -0,0 +1,272 @@ +use std::{ + collections::{HashMap, HashSet}, + path::{Path, PathBuf}, + sync::LazyLock, +}; + +use anyhow::{Context, Result, anyhow, bail}; +use regex::{Error, Regex, RegexBuilder}; +use serde_json::{Value, json}; + +use crate::{ + common::{post, read_file}, + contracts::CompilerOptions, + contracts_funcs::source_info::{SourceInfo, SourceKind}, +}; + +fn load_ral_files(compile_path: &str) -> Result<(Vec, String)> { + let path = Path::new(compile_path); + if path.is_file() { + let dir = path + .parent() + .map(|p| p.to_string_lossy().to_string()) + .context("Failed to get parent directory")?; + return Ok((vec![compile_path.to_string()], dir)); + } + + let dir = Path::new(compile_path); + + let mut ral_files_path = Vec::new(); + for entry in walkdir::WalkDir::new(dir) + .into_iter() + .filter_map(Result::ok) + .filter(|e| e.file_type().is_file()) + { + let file_name = entry.file_name().to_string_lossy(); + if file_name.contains(".ral") { + ral_files_path.push(entry.path().to_string_lossy().to_string()); + } + } + Ok((ral_files_path, compile_path.to_string())) +} + +static SOURCE_KIND_REGEX: LazyLock, Error>> = + LazyLock::new(|| { + let mut m = HashMap::new(); + m.insert( + SourceKind::AbstractContract, + RegexBuilder::new(r"^Abstract Contract ([A-Z][a-zA-Z0-9]*)") + .multi_line(true) + .build()?, + ); + m.insert( + SourceKind::Contract, + RegexBuilder::new(r"^Contract ([A-Z][a-zA-Z0-9]*)") + .multi_line(true) + .build()?, + ); + m.insert( + SourceKind::Interface, + RegexBuilder::new(r"^Interface ([A-Z][a-zA-Z0-9]*)") + .multi_line(true) + .build()?, + ); + m.insert( + SourceKind::Script, + RegexBuilder::new(r"^TxScript ([A-Z][a-zA-Z0-9]*)") + .multi_line(true) + .build()?, + ); + m.insert( + SourceKind::Struct, + RegexBuilder::new(r"struct ([A-Z][a-zA-Z0-9]*)") + .multi_line(true) + .build()?, + ); + Ok(m) + }); + +fn get_source_info( + source_code: &str, + contracts_relative_path: &str, + is_imported: bool, +) -> Result> { + let mut source_infos = Vec::new(); + let source_kind_regex = SOURCE_KIND_REGEX + .as_ref() + .map_err(|e| anyhow!("Failed to compile regex: {e}"))?; + + for (kind, regex) in source_kind_regex.iter() { + for cap in regex.captures_iter(source_code) { + let name = cap + .get(1) + .map(|m| m.as_str().to_string()) + .context("Invalid match")?; + let from_index = cap.get(1).map(|m| m.start()); + let source_info = SourceInfo::new( + *kind, + name, + from_index, + source_code.to_owned(), + contracts_relative_path.to_string(), // contract_relative_path, adjust as needed + is_imported, + ); + source_infos.push(source_info); + } + } + + if source_infos.is_empty() { + let name = Path::new(contracts_relative_path) + .file_stem() + .and_then(|s| s.to_str()) + .context("Failed to extract file stem for constants")? + .to_string(); + + source_infos.push(SourceInfo::new( + SourceKind::Constants, + name, + None, + source_code.to_owned(), + contracts_relative_path.to_string(), + false, + )); + } + Ok(source_infos) +} + +fn get_import_path(cwd_path: &str, import_path: &str) -> Result { + let parts: Vec<&str> = import_path.split('/').collect(); + if parts.len() > 1 && parts[0] == "std" { + let current_dir = std::env::current_dir().context("Failed to get current directory")?; + return Ok(Path::new(¤t_dir) + .join(["contracts", import_path].iter().collect::()) + .to_string_lossy() + .to_string()); + } + + Ok(format!( + "{}/{}", + cwd_path.trim_end_matches('/'), + import_path.trim_start_matches('/') + )) +} + +static REGEX_GET_IMPORT: LazyLock = + LazyLock::new(|| Regex::new(r#"^import\s+"[^"]+"$"#).unwrap()); +static REGEX_CHECK_IMPORT: LazyLock = LazyLock::new(|| Regex::new(r#"^import ""#).unwrap()); + +fn load_file( + path: &str, + contracts_relative_path: &str, + import_file_paths_cache: &HashSet, + is_imported: bool, +) -> Result<(Vec, HashSet)> { + let file_content = read_file(path)?; + + let mut source_content = file_content.clone(); + let mut import_file_paths = vec![]; + let mut import_statements_range = vec![]; + + let mut character_index: usize = 0; + for line in file_content.lines() { + if let Some(mat) = REGEX_GET_IMPORT.find(line) { + let import_stmt = mat.as_str(); + let mut import_path = import_stmt[8..import_stmt.len() - 1].to_string(); + if !import_path.ends_with(".ral") { + import_path.push_str(".ral"); + } + if !import_file_paths_cache.contains(&import_path) { + import_file_paths.push(import_path); + } + + let start = character_index + mat.start(); + let end = character_index + mat.end(); + import_statements_range.push(start..end); + } + character_index += line.len() + 1; // +1 for the newline character + } + + // remove from the end to avoid messing up the indices + for mat in import_statements_range.iter().rev() { + source_content.replace_range(mat.to_owned(), ""); + } + + for (i, line) in source_content.lines().enumerate() { + if REGEX_CHECK_IMPORT.find(line).is_some() { + bail!( + "Invalid import statements, source: {} (line {})", + path, + i + 1 + ); + } + } + + let mut new_import_file_paths_cache = import_file_paths_cache.clone(); + let mut imported_source_infos = Vec::new(); + + for import_path in &import_file_paths { + let import_path = get_import_path(contracts_relative_path, import_path)?; + if new_import_file_paths_cache.contains(&import_path) { + continue; + } + + new_import_file_paths_cache.insert(import_path.clone()); + let (imported_source_info, loaded_file_import_cache) = load_file( + &import_path, + contracts_relative_path, + &new_import_file_paths_cache, + true, + )?; + new_import_file_paths_cache.extend(loaded_file_import_cache); + imported_source_infos.extend(imported_source_info); + } + + let mut source_infos = get_source_info(&source_content, contracts_relative_path, is_imported)?; + source_infos.extend(imported_source_infos); + + Ok((source_infos, new_import_file_paths_cache)) +} + +pub async fn compile( + url: &str, + compile_path: &str, + compiler_options: CompilerOptions, + _debug: bool, // TODO: handle debug and force compilation + _force: bool, +) -> Result { + let (source_file_paths, compile_path) = load_ral_files(compile_path)?; + let mut all_source_infos = Vec::new(); + let mut import_file_paths_cache = HashSet::from([compile_path.clone()]); + + for path in &source_file_paths { + let (source_infos, new_cache) = + load_file(path, &compile_path, &import_file_paths_cache, false)?; + all_source_infos.extend(source_infos); + import_file_paths_cache = new_cache; + } + + if all_source_infos.is_empty() { + bail!("No valid source information found in the provided files."); + } + + // Ensure there is at least one Contract or Script + if !all_source_infos + .iter() + .any(|info| matches!(info.kind, SourceKind::Contract | SourceKind::Script)) + { + bail!("No Contract or Script found in the provided project files."); + } + + // Remove duplicate code + all_source_infos.sort_by_key(|info| info.code_info.source_code_hash.clone()); + all_source_infos.dedup_by_key(|info| info.code_info.source_code_hash.clone()); + + all_source_infos.sort_by_key(|info| info.kind); + + let concatenated_code = all_source_infos + .iter() + .map(|info| info.code_info.source_code.as_str()) + .collect::>() + .join("\n"); + + post( + url, + "/contracts/compile-project", + json!({ + "code": concatenated_code, + "compiler_options": json!(compiler_options) + }), + ) + .await? + .context("Empty reply") +} diff --git a/src/contracts_funcs/compile_project/compile_project_deserialize.rs b/src/contracts_funcs/compile_project/compile_project_deserialize.rs new file mode 100644 index 0000000..b7a6a3e --- /dev/null +++ b/src/contracts_funcs/compile_project/compile_project_deserialize.rs @@ -0,0 +1,134 @@ +use serde::Deserialize; +use serde_json::Value; + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct RawCompileProject { + pub contracts: Vec, + pub scripts: Vec, + pub structs: Option>, + pub constants: Option>, + pub enums: Option>, + pub warnings: Option>, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct RawContract { + pub version: String, + pub name: String, + pub bytecode: String, + pub bytecode_debug_patch: String, + pub code_hash: String, + pub code_hash_debug: String, + pub fields: Fields, + pub functions: Vec, + pub constants: Vec, + pub enums: Vec, + pub events: Vec, + pub warnings: Vec, + pub maps: Option, + pub std_interface_id: Option, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct RawScript { + pub version: String, + pub name: String, + pub bytecode_template: String, + pub bytecode_debug_patch: String, + pub fields: Fields, + pub functions: Vec, + pub warnings: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct StructDef { + pub name: String, + pub field_names: Vec, + pub field_types: Vec, + pub is_mutable: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Fields { + pub names: Vec, + pub types: Vec, + pub is_mutable: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RawFunction { + pub name: String, + pub use_preapproved_assets: bool, + pub use_assets_in_contract: bool, + pub is_public: bool, + pub param_names: Vec, + pub param_types: Vec, + pub param_is_mutable: Vec, + pub return_types: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct Constant { + pub name: String, + pub value: ConstantValue, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct ConstantValue { + pub type_name: Value, + pub value: FieldValueHelper, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct EnumDef { + pub name: String, + pub fields: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct EnumField { + pub name: String, + pub value: FieldValueHelper, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct Event { + pub name: String, + pub field_names: Vec, + pub field_types: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct Maps { + pub names: Vec, + pub types: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +#[allow(dead_code)] +pub struct FieldValueHelper { + #[serde(rename = "type")] + pub type_name: String, + pub value: Value, +} diff --git a/src/contracts_funcs/compile_project/compile_project_structs.rs b/src/contracts_funcs/compile_project/compile_project_structs.rs new file mode 100644 index 0000000..b0bb0a2 --- /dev/null +++ b/src/contracts_funcs/compile_project/compile_project_structs.rs @@ -0,0 +1,378 @@ +use std::collections::HashMap; + +use anyhow::{Context, Result, anyhow, bail}; +use indexmap::IndexMap; +use serde_json::{Value, value::RawValue}; + +use crate::{ + common::{is_hex_string, read_file}, + contracts_funcs::compile_project::{ + compile_project_deserialize::{RawCompileProject, RawContract, RawFunction, StructDef}, + compile_project_values::{RalphValue, TypeName}, + }, +}; + +pub type FieldsTypesMapMut = IndexMap; +pub type FieldsVec = Vec<(RalphValue, bool)>; + +// Return the position of the parameter for the call with the parsed value +// TODO: new system with json like parsing of structures, since currently arrays +// of structure is impossible +fn resolve_rec_type( + name: &str, + value: String, + types: &FieldsTypesMapMut, + override_type: Option<&TypeName>, +) -> Result<(usize, RalphValue)> { + let mut parts = name.splitn(2, '.'); + let name = parts.next().context("Field name cannot be empty")?; + let rest = parts.next(); + + let (type_name, param_index) = if let Some(typename) = override_type { + (typename, 0) // 0 magic value since the index is skipped + } else { + let (param_index, _, (typename, _)) = types.get_full(name).context(anyhow!( + "Type for field '{name}' not found in provided args types map: {types:?}" + ))?; + (typename, param_index) + }; + + match type_name { + TypeName::Structure((_, struct_types)) => { + let rest = rest.context(format!( + "Structure field name cannot be empty for field '{name}', type '{type_name:?}'" + ))?; + let mut sub_parts = rest.splitn(2, '.'); + let nested_struct_name = sub_parts.next().context(format!( + "Structure field name cannot be empty for field '{name}', type '{type_name:?}'" + ))?; + + if let Some(remaining_dots) = sub_parts.next() { + // there are nested structure(s) + if let (sub_index, _, (TypeName::Structure((_, nested_struct_types)), _)) = + struct_types.get_full(nested_struct_name).context(format!( + "Unknown nested structure name in {name}: {nested_struct_name}" + ))? + { + let (sub_nested_index, value) = + resolve_rec_type(remaining_dots, value, nested_struct_types, None)?; + return Ok((param_index + sub_index + sub_nested_index, value)); + } else { + bail!( + "Field '{nested_struct_name}' in structure '{type_name:?}' is not a\ + structure", + ); + } + } + + let (sub_index, value) = resolve_rec_type(rest, value, struct_types, None)?; + Ok((param_index + sub_index, value)) + }, + TypeName::Array((arr_type, arr_size)) => { + // example: [0, 1] + // example: [[0,1], [0,2]] + + // Using serde json to parse the array, only the outer array + let vec_values = serde_json::from_str::>>(&value)? + .into_iter() + .map(|x| x.to_string()) + .collect::>(); + + if vec_values.len() != *arr_size { + bail!( + "Array size mismatch for field '{}': expected {}, found {}", + name, + arr_size, + vec_values.len() + ); + } + + // We don't care about the index recusively since they are already in order + let ralph_values = vec_values + .into_iter() + .map(|v| { + let (_, value) = resolve_rec_type(name, v, types, Some(arr_type))?; + Ok(value) + }) + .collect::>>()?; + + Ok((param_index, RalphValue::Array(ralph_values))) + }, + _ => { + let is_hex_string = is_hex_string(&value); + let ralph_value = if *type_name == TypeName::ByteVec && !is_hex_string { + // Human readable String + value.as_str().try_into()? + } else { + // If it's still a string, but hexified, other types goes here + let value = if is_hex_string { + value.strip_prefix("0x").unwrap_or(&value).to_string() + } else { + value + }; + + RalphValue::from_typename_and_value(type_name, &Value::String(value))? + }; + + Ok((param_index, ralph_value)) + }, + } +} + +fn fields_types_map_len(map: &FieldsTypesMapMut) -> usize { + map.iter() + .map(|(_, (type_name, _))| match type_name { + TypeName::Structure((_, struct_fields)) => fields_types_map_len(struct_fields), + _ => 1, + }) + .sum() +} + +pub fn args_to_fields_vec( + ralph_vec_input: Vec<(String, String)>, + types: &FieldsTypesMapMut, +) -> Result> { + if ralph_vec_input.len() != fields_types_map_len(types) { + bail!( + "Fields count mismatch with initial fields: expected {}, found {}", + types.len(), + ralph_vec_input.len() + ); + } + + let mut unordered_params_vec = ralph_vec_input + .into_iter() + .map(|(name, value)| resolve_rec_type(&name, value, types, None)) + .collect::>>()?; + + unordered_params_vec.sort_by_key(|(pos, _)| *pos); + Ok(unordered_params_vec + .into_iter() + .map(|(_, val)| val) + .collect()) +} + +#[derive(Debug, Clone)] +pub struct Struct { + pub field_names: Vec, + pub field_types: Vec, + pub is_mutable: Vec, +} + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct Function { + pub name: String, + pub use_preapproved_assets: bool, + pub use_assets_in_contract: bool, + pub is_public: bool, + pub params_types: FieldsTypesMapMut, + pub return_types: Vec, +} + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct CompiledContract { + pub version: String, + pub std_interface_id: Option, + pub name: String, + pub bytecode: String, + pub bytecode_debug_patch: String, + pub code_hash: String, + pub code_hash_debug: String, + pub fields_types: FieldsTypesMapMut, + pub functions: Vec, +} + +impl CompiledContract { + pub fn try_from(contract: RawContract, structs: &HashMap) -> Result { + let fields_iter = contract.fields.names.iter().zip( + contract + .fields + .types + .iter() + .zip(contract.fields.is_mutable.iter()), + ); + + let fields_types = fields_iter + .map(|(name, (ty, is_mutable))| { + let ty = ty.as_str().context("Expected field type as string")?; + let type_name = TypeName::from_name_and_structures(ty, structs)?; + Ok((name.clone(), (type_name, *is_mutable))) + }) + .collect::>>()?; + + let functions = contract + .functions + .into_iter() + .map(|raw| Function::from_raw_and_structures(raw, structs)) + .collect::>>()?; + + Ok(Self { + version: contract.version, + std_interface_id: contract.std_interface_id, + name: contract.name, + bytecode: contract.bytecode, + bytecode_debug_patch: contract.bytecode_debug_patch, + code_hash: contract.code_hash, + code_hash_debug: contract.code_hash_debug, + fields_types, + functions, + }) + } + + pub fn get_method(&self, method_name: &str) -> Result<(&Function, usize)> { + let method_index = self + .functions + .iter() + .position(|f| f.name == method_name) + .context(format!( + "Method '{}' not found in contract '{}'", + method_name, self.name + ))?; + let method = &self.functions[method_index]; + Ok((method, method_index)) + } +} + +#[allow(dead_code)] +pub struct Script { + pub version: String, + pub name: String, + pub bytecode_template: String, + pub bytecode_debug_patch: String, + pub fields: FieldsTypesMapMut, +} + +#[allow(dead_code)] +pub struct CompileProject { + pub contracts: Vec, + pub scripts: Vec