From b02c4a6b3ab763fa0911ad264e8bad9ad665483c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C4=B1z=C4=B1r=20Sefa=20=C4=B0rken?= Date: Tue, 28 Apr 2026 21:48:36 +0300 Subject: [PATCH 1/4] feat(tx-evaluation): squashed work for stacked rebase - chore: add devshell tools and CI tweaks for tx evaluation - chore: update workspace deps for tx evaluation - feat(common): add lazy chain config loader and helpers - fix(node): improve CBOR validation error messages - feat(node): add chain config, ledger, and utxo helpers - feat: add bf-tx-evaluator crate with native and external evaluators - feat(platform): add /utils/txs/evaluate endpoints - test: add coverage for tx evaluate endpoints --- .github/workflows/ci.yaml | 1 + Cargo.lock | 1873 ++++++++++------- Cargo.toml | 19 +- crates/common/Cargo.toml | 1 + crates/common/src/chain_config.rs | 97 + crates/common/src/errors.rs | 22 +- crates/common/src/helpers.rs | 116 + crates/common/src/lib.rs | 2 + crates/integration_tests/src/gateway/mod.rs | 2 +- crates/integration_tests/src/platform/mod.rs | 13 +- crates/integration_tests/tests/api/utils.rs | 6 + .../tests/api/utils/txs/evaluate/root.rs | 286 +++ .../tests/api/utils/txs/evaluate/utxos.rs | 140 ++ .../tests/data/supported_endpoints.json | 4 +- .../integration_tests/tests/e2e_websocket.rs | 2 +- crates/integration_tests/tests/eval_test.rs | 3 + .../tests/platform_data_node.rs | 6 +- .../tests/platform_metrics.rs | 2 +- .../integration_tests/tests/platform_root.rs | 2 +- .../tests/platform_submit.rs | 8 +- crates/node/src/cbor/validation.rs | 26 +- crates/node/src/chain_config.rs | 55 + crates/node/src/chain_config_watch.rs | 233 ++ crates/node/src/ledger.rs | 29 + crates/node/src/lib.rs | 4 + crates/node/src/transactions.rs | 14 +- crates/node/src/utxo.rs | 23 + crates/platform/Cargo.toml | 2 +- crates/platform/src/api/tx/submit.rs | 41 +- crates/platform/src/api/utils/txs/evaluate.rs | 1 + .../src/api/utils/txs/evaluate/model.rs | 12 + .../src/api/utils/txs/evaluate/root.rs | 32 +- .../src/api/utils/txs/evaluate/utxos.rs | 43 +- crates/platform/src/main.rs | 2 +- crates/platform/src/server.rs | 17 +- crates/platform/src/server/routes/hidden.rs | 9 +- crates/testgen/src/testgen.rs | 2 + crates/tx_evaluator/Cargo.toml | 28 + crates/tx_evaluator/README.md | 151 ++ crates/tx_evaluator/src/external.rs | 425 ++++ crates/tx_evaluator/src/helper.rs | 93 + crates/tx_evaluator/src/lib.rs | 6 + crates/tx_evaluator/src/model/api.rs | 1169 ++++++++++ crates/tx_evaluator/src/model/mod.rs | 1 + crates/tx_evaluator/src/native.rs | 954 +++++++++ crates/tx_evaluator/src/ogmios5_response.rs | 503 +++++ crates/tx_evaluator/src/wrapper.rs | 242 +++ 47 files changed, 5872 insertions(+), 850 deletions(-) create mode 100644 crates/common/src/chain_config.rs create mode 100644 crates/common/src/helpers.rs create mode 100644 crates/integration_tests/tests/api/utils.rs create mode 100644 crates/integration_tests/tests/api/utils/txs/evaluate/root.rs create mode 100644 crates/integration_tests/tests/api/utils/txs/evaluate/utxos.rs create mode 100644 crates/integration_tests/tests/eval_test.rs create mode 100644 crates/node/src/chain_config.rs create mode 100644 crates/node/src/chain_config_watch.rs create mode 100644 crates/node/src/ledger.rs create mode 100644 crates/node/src/utxo.rs create mode 100644 crates/platform/src/api/utils/txs/evaluate/model.rs create mode 100644 crates/tx_evaluator/Cargo.toml create mode 100644 crates/tx_evaluator/README.md create mode 100644 crates/tx_evaluator/src/external.rs create mode 100644 crates/tx_evaluator/src/helper.rs create mode 100644 crates/tx_evaluator/src/lib.rs create mode 100644 crates/tx_evaluator/src/model/api.rs create mode 100644 crates/tx_evaluator/src/model/mod.rs create mode 100644 crates/tx_evaluator/src/native.rs create mode 100644 crates/tx_evaluator/src/ogmios5_response.rs create mode 100644 crates/tx_evaluator/src/wrapper.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e5c2b991..4ef49a29 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -168,6 +168,7 @@ jobs: timeout-minutes: 10 env: CARDANO_NODE_SOCKET_PATH: /run/cardano-node/node_preview.socket + CARGO_PROFILE_DEV_DEBUG: "0" BLOCKFROST_PREVIEW_PROJECT_ID: ${{ secrets.BLOCKFROST_PREVIEW_PROJECT_ID }} run: nix develop .# --command cargo test --verbose -p blockfrost-platform-integration-tests --no-fail-fast diff --git a/Cargo.lock b/Cargo.lock index ecc0f852..6102da73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "futures-core", "futures-sink", @@ -21,16 +21,15 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44dfe5c9e0004c623edc65391dfd51daa201e7e30ebd9c9bedf873048ec32bc2" +checksum = "f860ee6746d0c5b682147b2f7f8ef036d4f92fe518251a3a35ffa3650eafdf0e" dependencies = [ "actix-codec", "actix-rt", "actix-service", "actix-utils", - "base64", - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "bytestring", "derive_more", @@ -42,12 +41,9 @@ dependencies = [ "httpdate", "itoa", "language-tags", - "local-channel", "mime", "percent-encoding", "pin-project-lite", - "rand 0.9.0", - "sha1", "smallvec", "tokio", "tokio-util", @@ -56,9 +52,9 @@ dependencies = [ [[package]] name = "actix-router" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7" dependencies = [ "bytestring", "cfg-if", @@ -70,9 +66,9 @@ dependencies = [ [[package]] name = "actix-rt" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63" dependencies = [ "futures-core", "tokio", @@ -90,7 +86,7 @@ dependencies = [ "futures-core", "futures-util", "mio", - "socket2 0.5.9", + "socket2 0.5.10", "tokio", "tracing", ] @@ -117,9 +113,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.11.0" +version = "4.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea" +checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf" dependencies = [ "actix-codec", "actix-http", @@ -148,7 +144,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "smallvec", - "socket2 0.5.9", + "socket2 0.6.3", "time", "tracing", "url", @@ -156,18 +152,18 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aes" @@ -177,26 +173,26 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -207,12 +203,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -224,9 +214,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -239,37 +229,37 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", - "once_cell", - "windows-sys 0.59.0", + "once_cell_polyfill", + "windows-sys 0.61.2", ] [[package]] @@ -278,6 +268,15 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -298,9 +297,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" @@ -313,7 +312,7 @@ dependencies = [ "bytes", "form_urlencoded", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body", "http-body-util", "hyper", @@ -340,13 +339,13 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body", "http-body-util", "mime", @@ -359,9 +358,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", @@ -369,7 +368,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-link 0.2.1", ] [[package]] @@ -386,9 +385,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bech32" @@ -420,11 +419,11 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.70.1" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -433,7 +432,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -463,18 +462,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] -name = "bitcoin-internals" -version = "0.2.0" +name = "bitcoin-io" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin_hashes" -version = "0.13.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" dependencies = [ - "bitcoin-internals", + "bitcoin-io", "hex-conservative", ] @@ -486,22 +485,22 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "blake3" -version = "1.8.3" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "cpufeatures", + "cpufeatures 0.3.0", ] [[package]] @@ -522,6 +521,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + [[package]] name = "blockfrost" version = "1.2.3" @@ -571,7 +579,7 @@ dependencies = [ "tokio", "tokio-tungstenite", "tokio-util", - "toml 1.0.2+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", "tower", "tracing", "tungstenite", @@ -606,6 +614,7 @@ dependencies = [ "blockfrost-platform-common", "blockfrost-platform-data-node", "blockfrost-platform-node", + "blockfrost-platform-tx-evaluator", "cardano-serialization-lib", "chrono", "clap", @@ -625,7 +634,6 @@ dependencies = [ "pallas-codec", "pallas-network", "pretty_assertions", - "proptest", "reqwest 0.13.2", "rstest", "sentry", @@ -634,7 +642,7 @@ dependencies = [ "tokio", "tokio-tungstenite", "tokio-util", - "toml 1.0.2+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", "tower", "tower-http", "tracing", @@ -674,12 +682,13 @@ dependencies = [ "bytes", "cardano-serialization-lib", "clap", - "getrandom 0.3.2", + "getrandom 0.3.4", "hex", "machine-uid", "nix", "pallas-network", "pretty_assertions", + "proptest", "reqwest 0.13.2", "rstest", "serde", @@ -792,6 +801,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "blockfrost-platform-tx-evaluator" +version = "1.0.0-rc.2" +dependencies = [ + "base64", + "blockfrost-platform-common", + "blockfrost-platform-node", + "blockfrost-platform-testgen", + "chrono", + "hex", + "pallas-addresses", + "pallas-codec", + "pallas-crypto", + "pallas-network", + "pallas-primitives", + "pallas-traverse", + "pallas-validate", + "serde", + "serde_json", + "tokio", + "tracing", + "uuid", +] + [[package]] name = "blockfrost-sdk-bridge" version = "1.0.0-rc.2" @@ -820,11 +853,23 @@ dependencies = [ "uuid", ] +[[package]] +name = "blst" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byteorder" @@ -840,9 +885,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "bytestring" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" +checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289" dependencies = [ "bytes", ] @@ -869,7 +914,7 @@ dependencies = [ "cryptoxide", "digest 0.9.0", "ed25519-bip32", - "getrandom 0.2.15", + "getrandom 0.2.17", "hashlink", "hex", "itertools 0.10.5", @@ -898,9 +943,9 @@ checksum = "089a0261d1bc59e54e8e11860031efd88593f0e61b921172c474f1f38c2f2d3c" [[package]] name = "cc" -version = "1.2.53" +version = "1.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ "find-msvc-tools", "jobserver", @@ -919,9 +964,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -931,17 +976,29 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-link 0.1.1", + "windows-link 0.2.1", +] + +[[package]] +name = "chumsky" +version = "1.0.0-alpha.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7b80276986f86789dc56ca6542d53bba9cda3c66091ebbe7bd96fc1bdf20f1f" +dependencies = [ + "hashbrown 0.14.5", + "regex-automata 0.3.9", + "serde", + "stacker", + "unicode-ident", ] [[package]] @@ -967,9 +1024,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.59" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5caf74d17c3aec5495110c34cc3f78644bfa89af6c8993ed4de2790e49b6499" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" dependencies = [ "clap_builder", "clap_derive", @@ -977,9 +1034,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.59" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370daa45065b80218950227371916a1633217ae42b2715b2287b606dcd618e24" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -989,21 +1046,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "clear_on_drop" @@ -1025,9 +1082,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "colored" @@ -1059,9 +1116,9 @@ checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "convert_case" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] @@ -1076,6 +1133,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1091,11 +1158,20 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" -version = "3.2.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] @@ -1177,7 +1253,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "crossterm_winapi", "derive_more", "document-features", @@ -1200,15 +1276,15 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -1226,8 +1302,18 @@ version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] @@ -1241,7 +1327,20 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.100", + "syn 2.0.117", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", ] [[package]] @@ -1250,9 +1349,20 @@ version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ - "darling_core", + "darling_core 0.21.3", "quote", - "syn 2.0.100", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.117", ] [[package]] @@ -1271,9 +1381,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "deadpool" @@ -1345,9 +1455,9 @@ dependencies = [ [[package]] name = "deflate64" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" +checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" [[package]] name = "der" @@ -1361,9 +1471,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", "serde_core", @@ -1371,33 +1481,34 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.100", + "rustc_version", + "syn 2.0.117", "unicode-xid", ] [[package]] name = "diesel" -version = "2.3.6" +version = "2.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b6c2fc184a6fb6ebcf5f9a5e3bbfa84d8fd268cdfcce4ed508979a6259494d" +checksum = "f4ae09a41a4b89f94ec1e053623da8340d996bc32c6517d325a9daad9b239358" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "byteorder", "chrono", "diesel_derives", @@ -1416,7 +1527,7 @@ dependencies = [ "dsl_auto_type", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -1436,7 +1547,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c" dependencies = [ - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -1486,6 +1597,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.0", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1494,20 +1615,20 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "doc-comment" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] @@ -1530,19 +1651,19 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd122633e4bef06db27737f21d3738fb89c8f6d5360d6d9d7635dda142a7757e" dependencies = [ - "darling", + "darling 0.21.3", "either", "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ed25519-bip32" @@ -1585,37 +1706,36 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "filetime" -version = "0.2.25" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.59.0", ] [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "findshlibs" @@ -1631,13 +1751,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ - "crc32fast", - "libz-rs-sys", "miniz_oxide", + "zlib-rs", ] [[package]] @@ -1690,9 +1809,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1705,9 +1824,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1715,15 +1834,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1732,32 +1851,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-timer" @@ -1767,9 +1886,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -1779,7 +1898,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1804,41 +1922,41 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "r-efi 5.3.0", + "wasip2", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 6.0.0", "wasip2", "wasip3", "wasm-bindgen", @@ -1846,15 +1964,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "governor" @@ -1867,13 +1985,13 @@ dependencies = [ "futures-sink", "futures-timer", "futures-util", - "getrandom 0.3.2", + "getrandom 0.3.4", "hashbrown 0.16.1", "nonzero_ext", "parking_lot", "portable-atomic", "quanta", - "rand 0.9.0", + "rand 0.9.2", "smallvec", "spinning_top", "web-time", @@ -1881,17 +1999,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.3.1", - "indexmap 2.13.0", + "http 1.4.0", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -1900,12 +2018,13 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -1921,6 +2040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", + "allocator-api2", ] [[package]] @@ -1943,6 +2063,12 @@ dependencies = [ "foldhash 0.2.0", ] +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + [[package]] name = "hashlink" version = "0.9.1" @@ -1978,9 +2104,12 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.1.2" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec", +] [[package]] name = "hmac" @@ -1993,13 +2122,13 @@ dependencies = [ [[package]] name = "hostname" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" +checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" dependencies = [ "cfg-if", "libc", - "windows-link 0.1.1", + "windows-link 0.2.1", ] [[package]] @@ -2015,12 +2144,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -2031,7 +2159,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -2042,7 +2170,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body", "pin-project-lite", ] @@ -2061,22 +2189,21 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", "h2", - "http 1.3.1", + "http 1.4.0", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -2084,14 +2211,15 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.9" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", + "http 1.4.0", "hyper", "hyper-util", "rustls", + "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", @@ -2115,16 +2243,15 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64", "bytes", "futures-channel", - "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body", "hyper", "ipnet", @@ -2136,14 +2263,14 @@ dependencies = [ "tokio", "tower-service", "tracing", - "windows-registry 0.5.2", + "windows-registry", ] [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2151,7 +2278,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core 0.62.2", ] [[package]] @@ -2164,123 +2291,98 @@ dependencies = [ ] [[package]] -name = "icu_collections" -version = "1.5.0" +name = "ibig" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "d1fcc7f316b2c079dde77564a1360639c1a956a23fa96122732e416cb10717bb" dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", + "cfg-if", + "num-traits", + "static_assertions", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_collections" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", - "litemap", - "tinystr", - "writeable", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", "zerovec", ] [[package]] -name = "icu_locid_transform" -version = "1.5.0" +name = "icu_locale_core" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", + "litemap", "tinystr", + "writeable", "zerovec", ] -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ - "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", + "icu_locale_core", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "id-arena" version = "2.3.0" @@ -2306,9 +2408,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -2333,12 +2435,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_core", ] @@ -2354,11 +2456,11 @@ dependencies = [ [[package]] name = "inquire" -version = "0.9.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2628910d0114e9139056161d8644a2026be7b117f8498943f9437748b04c9e0a" +checksum = "6654738b8024300cf062d04a1c13c10c8e2cea598ec1c47dc9b6641159429756" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "crossterm", "dyn-clone", "fuzzy-matcher", @@ -2368,15 +2470,15 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" dependencies = [ "memchr", "serde", @@ -2384,9 +2486,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -2406,11 +2508,20 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jemalloc" @@ -2423,20 +2534,22 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -2467,25 +2580,25 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" [[package]] name = "libc" -version = "0.2.185" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets", + "windows-link 0.2.1", ] [[package]] name = "libproc" -version = "0.14.10" +version = "0.14.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78a09b56be5adbcad5aa1197371688dc6bb249a26da3bca2011ee2fb987ebfb" +checksum = "a54ad7278b8bc5301d5ffd2a94251c004feb971feba96c971ea4063645990757" dependencies = [ "bindgen", "errno", @@ -2494,41 +2607,33 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "libc", - "redox_syscall", -] - -[[package]] -name = "libz-rs-sys" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c10501e7805cee23da17c7790e59df2870c0d4043ec6d03f67d31e2b53e77415" -dependencies = [ - "zlib-rs", + "plain", + "redox_syscall 0.7.4", ] [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "litrs" -version = "0.4.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "loca" @@ -2539,17 +2644,6 @@ dependencies = [ "ptr", ] -[[package]] -name = "local-channel" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" -dependencies = [ - "futures-core", - "futures-sink", - "local-waker", -] - [[package]] name = "local-waker" version = "0.1.4" @@ -2593,7 +2687,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d7217d573cdb141d6da43113b098172e057d39915d79c4bdedbc3aacd46bd96" dependencies = [ "libc", - "windows-registry 0.6.1", + "windows-registry", "windows-sys 0.61.2", ] @@ -2603,7 +2697,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata", + "regex-automata 0.4.14", ] [[package]] @@ -2614,9 +2708,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.7.4" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "metrics" @@ -2635,7 +2729,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3589659543c04c7dc5526ec858591015b87cd8746583b51b48ef4353f99dbcda" dependencies = [ "base64", - "indexmap 2.13.0", + "indexmap 2.14.0", "metrics", "metrics-util", "quanta", @@ -2669,7 +2763,7 @@ dependencies = [ "hashbrown 0.16.1", "metrics", "quanta", - "rand 0.9.0", + "rand 0.9.2", "rand_xoshiro", "sketches-ddsketch", ] @@ -2713,12 +2807,32 @@ dependencies = [ [[package]] name = "minicbor" -version = "0.26.4" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0452a60c1863c1f50b5f77cd295e8d2786849f35883f0b9e18e7e6e1b5691b0" +dependencies = [ + "minicbor-derive 0.15.3", +] + +[[package]] +name = "minicbor" +version = "0.26.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acb9d59e79ad66121ab441a0d1950890906a41e01ae14145ecf8401aa8894f50" +checksum = "8a309f581ade7597820083bc275075c4c6986e57e53f8d26f88507cfefc8c987" dependencies = [ "half", - "minicbor-derive", + "minicbor-derive 0.16.2", +] + +[[package]] +name = "minicbor-derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -2729,7 +2843,7 @@ checksum = "a9882ef5c56df184b8ffc107fc6c61e33ee3a654b021961d790a78571bb9d67a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -2740,11 +2854,12 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -2755,15 +2870,15 @@ checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.61.2", ] [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", @@ -2782,7 +2897,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", @@ -2812,9 +2927,9 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] name = "ntapi" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" dependencies = [ "winapi", ] @@ -2854,11 +2969,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2896,9 +3011,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-derive" @@ -2908,7 +3023,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -2955,45 +3070,201 @@ dependencies = [ name = "num_cpus" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.0", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "hermit-abi", - "libc", + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", ] [[package]] -name = "objc2-core-foundation" +name = "objc2-ui-kit" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", ] [[package]] -name = "objc2-io-kit" +name = "objc2-user-notifications" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" dependencies = [ - "libc", - "objc2-core-foundation", + "objc2", + "objc2-foundation", ] [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "opaque-debug" @@ -3003,11 +3274,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "foreign-types", "libc", @@ -3024,20 +3295,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.107" +version = "0.9.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" dependencies = [ "cc", "libc", @@ -3053,19 +3324,24 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "os_info" -version = "3.10.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a604e53c24761286860eba4e2c8b23a0161526476b1de520139d69cdb85a6b5" +checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" dependencies = [ + "android_system_properties", "log", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", "serde", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "pallas-addresses" version = "1.0.0-alpha.5" -source = "git+https://github.com/txpipe/pallas.git?rev=ed422bc0d5a09f77617d4882dd4c71089a8bcdb2#ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" +source = "git+https://github.com/blockfrost/pallas.git?rev=cfa0c633640f5989e44f50ed27efc38992f9ed0d#cfa0c633640f5989e44f50ed27efc38992f9ed0d" dependencies = [ "base58", "bech32 0.9.1", @@ -3080,10 +3356,10 @@ dependencies = [ [[package]] name = "pallas-codec" version = "1.0.0-alpha.5" -source = "git+https://github.com/txpipe/pallas.git?rev=ed422bc0d5a09f77617d4882dd4c71089a8bcdb2#ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" +source = "git+https://github.com/blockfrost/pallas.git?rev=cfa0c633640f5989e44f50ed27efc38992f9ed0d#cfa0c633640f5989e44f50ed27efc38992f9ed0d" dependencies = [ "hex", - "minicbor", + "minicbor 0.26.5", "serde", "thiserror 1.0.69", ] @@ -3091,12 +3367,12 @@ dependencies = [ [[package]] name = "pallas-crypto" version = "1.0.0-alpha.5" -source = "git+https://github.com/txpipe/pallas.git?rev=ed422bc0d5a09f77617d4882dd4c71089a8bcdb2#ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" +source = "git+https://github.com/blockfrost/pallas.git?rev=cfa0c633640f5989e44f50ed27efc38992f9ed0d#cfa0c633640f5989e44f50ed27efc38992f9ed0d" dependencies = [ "cryptoxide", "hex", "pallas-codec", - "rand_core 0.9.3", + "rand_core 0.9.5", "serde", "thiserror 1.0.69", ] @@ -3104,7 +3380,7 @@ dependencies = [ [[package]] name = "pallas-hardano" version = "1.0.0-alpha.5" -source = "git+https://github.com/txpipe/pallas.git?rev=ed422bc0d5a09f77617d4882dd4c71089a8bcdb2#ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" +source = "git+https://github.com/blockfrost/pallas.git?rev=cfa0c633640f5989e44f50ed27efc38992f9ed0d#cfa0c633640f5989e44f50ed27efc38992f9ed0d" dependencies = [ "binary-layout", "hex", @@ -3124,7 +3400,7 @@ dependencies = [ [[package]] name = "pallas-network" version = "1.0.0-alpha.5" -source = "git+https://github.com/txpipe/pallas.git?rev=ed422bc0d5a09f77617d4882dd4c71089a8bcdb2#ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" +source = "git+https://github.com/blockfrost/pallas.git?rev=cfa0c633640f5989e44f50ed27efc38992f9ed0d#cfa0c633640f5989e44f50ed27efc38992f9ed0d" dependencies = [ "byteorder", "hex", @@ -3132,7 +3408,7 @@ dependencies = [ "pallas-codec", "pallas-crypto", "rand 0.8.5", - "socket2 0.5.9", + "socket2 0.5.10", "thiserror 1.0.69", "tokio", "tracing", @@ -3141,7 +3417,7 @@ dependencies = [ [[package]] name = "pallas-primitives" version = "1.0.0-alpha.5" -source = "git+https://github.com/txpipe/pallas.git?rev=ed422bc0d5a09f77617d4882dd4c71089a8bcdb2#ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" +source = "git+https://github.com/blockfrost/pallas.git?rev=cfa0c633640f5989e44f50ed27efc38992f9ed0d#cfa0c633640f5989e44f50ed27efc38992f9ed0d" dependencies = [ "hex", "pallas-codec", @@ -3153,7 +3429,7 @@ dependencies = [ [[package]] name = "pallas-traverse" version = "1.0.0-alpha.5" -source = "git+https://github.com/txpipe/pallas.git?rev=ed422bc0d5a09f77617d4882dd4c71089a8bcdb2#ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" +source = "git+https://github.com/blockfrost/pallas.git?rev=cfa0c633640f5989e44f50ed27efc38992f9ed0d#cfa0c633640f5989e44f50ed27efc38992f9ed0d" dependencies = [ "hex", "itertools 0.13.0", @@ -3166,6 +3442,43 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "pallas-uplc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d17ae8308fa96979da3b2314d0b28e85403448751880582d4f2beafa7d68bb6c" +dependencies = [ + "blst", + "bumpalo", + "chumsky", + "cryptoxide", + "ibig", + "minicbor 0.25.1", + "num-traits", + "once_cell", + "secp256k1", + "thiserror 1.0.69", +] + +[[package]] +name = "pallas-validate" +version = "1.0.0-alpha.5" +source = "git+https://github.com/blockfrost/pallas.git?rev=cfa0c633640f5989e44f50ed27efc38992f9ed0d#cfa0c633640f5989e44f50ed27efc38992f9ed0d" +dependencies = [ + "chrono", + "hex", + "itertools 0.14.0", + "pallas-addresses", + "pallas-codec", + "pallas-crypto", + "pallas-primitives", + "pallas-traverse", + "pallas-uplc", + "serde", + "thiserror 1.0.69", + "tracing", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -3184,7 +3497,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", "windows-link 0.2.1", ] @@ -3222,15 +3535,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkg-config" @@ -3238,11 +3545,26 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "potential_utf" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] [[package]] name = "powerfmt" @@ -3262,7 +3584,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.24", + "zerocopy", ] [[package]] @@ -3288,28 +3610,28 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.34" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -3320,7 +3642,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "procfs-core", "rustix", ] @@ -3331,29 +3653,39 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "hex", ] [[package]] name = "proptest" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.10.0", + "bitflags 2.11.0", "num-traits", - "rand 0.9.0", + "rand 0.9.2", "rand_chacha 0.9.0", "rand_xorshift", - "regex-syntax", + "regex-syntax 0.8.10", "rusty-fork", "tempfile", "unarray", ] +[[package]] +name = "psm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8" +dependencies = [ + "ar_archive_writer", + "cc", +] + [[package]] name = "ptr" version = "0.2.3" @@ -3362,15 +3694,15 @@ checksum = "76208293e44a44a702aa22f5cf40e2e55f4a6cd19953ddadb0b3f68e337d87ce" [[package]] name = "quanta" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" dependencies = [ "crossbeam-utils", "libc", "once_cell", "raw-cpuid", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "web-sys", "winapi", ] @@ -3383,18 +3715,24 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.40" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" @@ -3409,13 +3747,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", - "zerocopy 0.8.24", + "rand_core 0.9.5", ] [[package]] @@ -3435,7 +3772,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -3459,16 +3796,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", ] [[package]] @@ -3492,7 +3829,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -3501,16 +3838,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] name = "raw-cpuid" -version = "11.5.0" +version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -3524,20 +3861,29 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_syscall" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "redox_users" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.17", "libredox", "thiserror 2.0.18", ] @@ -3559,43 +3905,60 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.14", + "regex-syntax 0.8.10", +] + +[[package]] +name = "regex-automata" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-syntax 0.7.5", ] [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.10", ] [[package]] name = "regex-lite" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "regex-syntax" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "relative-path" @@ -3614,7 +3977,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body", "http-body-util", "hyper", @@ -3654,7 +4017,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 1.3.1", + "http 1.4.0", "http-body", "http-body-util", "hyper", @@ -3692,7 +4055,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -3732,21 +4095,21 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.100", + "syn 2.0.117", "unicode-ident", ] [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" -version = "1.1.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc_version" @@ -3759,11 +4122,11 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys", @@ -3772,9 +4135,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.39" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "once_cell", "rustls-pki-types", @@ -3785,9 +4148,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "zeroize", ] @@ -3811,9 +4174,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -3823,17 +4186,17 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3881,7 +4244,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -3890,14 +4253,34 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + [[package]] name = "security-framework" -version = "2.11.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.10.0", - "core-foundation", + "bitflags 2.11.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -3905,9 +4288,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -3915,9 +4298,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "sentry" @@ -3984,7 +4367,7 @@ version = "0.46.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0b1e7ca40f965db239da279bf278d87b7407469b98835f27f0c8e59ed189b06" dependencies = [ - "rand 0.9.0", + "rand 0.9.2", "sentry-types", "serde", "serde_json", @@ -4017,7 +4400,7 @@ version = "0.46.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b07eefe04486316c57aba08ab53dd44753c25102d1d3fe05775cc93a13262d9" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "sentry-backtrace", "sentry-core", "tracing-core", @@ -4032,7 +4415,7 @@ checksum = "567711f01f86a842057e1fc17779eba33a336004227e1a1e7e6cc2599e22e259" dependencies = [ "debugid", "hex", - "rand 0.9.0", + "rand 0.9.2", "serde", "serde_json", "thiserror 2.0.18", @@ -4079,7 +4462,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -4090,7 +4473,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -4108,12 +4491,13 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" dependencies = [ "itoa", "serde", + "serde_core", ] [[package]] @@ -4124,14 +4508,14 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -4150,15 +4534,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.14.0", "schemars 0.9.0", "schemars 1.2.1", "serde_core", @@ -4169,14 +4553,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" dependencies = [ - "darling", + "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -4186,7 +4570,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -4198,7 +4582,7 @@ checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.9.0", "opaque-debug", ] @@ -4210,7 +4594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -4225,9 +4609,9 @@ dependencies = [ [[package]] name = "shellexpand" -version = "3.1.1" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" +checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8" dependencies = [ "dirs", ] @@ -4240,9 +4624,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" dependencies = [ "libc", "signal-hook-registry", @@ -4261,33 +4645,31 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "sketches-ddsketch" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e9a774a6c28142ac54bb25d25562e6bcf957493a184f15ad4eebccb23e410a" +checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -4297,9 +4679,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -4326,9 +4708,28 @@ 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 = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stacker" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" @@ -4355,9 +4756,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -4375,13 +4776,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -4395,17 +4796,17 @@ dependencies = [ "ntapi", "objc2-core-foundation", "objc2-io-kit", - "windows 0.61.1", + "windows 0.61.3", ] [[package]] name = "system-configuration" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.10.0", - "core-foundation", + "bitflags 2.11.0", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -4438,12 +4839,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.24.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.4.2", "once_cell", "rustix", "windows-sys 0.61.2", @@ -4475,7 +4876,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -4486,17 +4887,25 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", ] [[package]] @@ -4533,9 +4942,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -4543,9 +4952,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -4581,7 +4990,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -4620,9 +5029,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -4650,30 +5059,24 @@ dependencies = [ "serde_spanned", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", - "winnow", + "winnow 0.7.15", ] [[package]] name = "toml" -version = "1.0.2+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1dfefef6a142e93f346b64c160934eb13b5594b84ab378133ac6815cb2bd57f" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "serde_core", "serde_spanned", - "toml_datetime 1.0.0+spec-1.1.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 1.0.1", ] -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" - [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -4685,38 +5088,39 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.0.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ - "indexmap 2.13.0", - "toml_datetime 0.6.11", - "winnow", + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.1", ] [[package]] name = "toml_parser" -version = "1.0.9+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.1", ] [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tower" @@ -4741,10 +5145,10 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body", "iri-string", "pin-project-lite", @@ -4785,7 +5189,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -4822,14 +5226,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex-automata", + "regex-automata 0.4.14", "sharded-slab", "smallvec", "thread_local", @@ -4852,11 +5256,11 @@ checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", - "http 1.3.1", + "http 1.4.0", "httparse", "log", "native-tls", - "rand 0.9.0", + "rand 0.9.2", "sha1", "thiserror 2.0.18", "utf-8", @@ -4881,15 +5285,15 @@ dependencies = [ [[package]] name = "typed-path" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3015e6ce46d5ad8751e4a772543a30c7511468070e98e64e20165f8f81155b64" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "uname" @@ -4908,30 +5312,30 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicase" -version = "2.8.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-width" @@ -4975,7 +5379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" dependencies = [ "base64", - "http 1.3.1", + "http 1.4.0", "httparse", "log", ] @@ -4999,12 +5403,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8-zero" version = "0.8.1" @@ -5025,11 +5423,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.21.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ - "getrandom 0.4.1", + "getrandom 0.4.2", "js-sys", "serde_core", "wasm-bindgen", @@ -5073,18 +5471,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" -dependencies = [ - "wit-bindgen-rt", -] +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" @@ -5106,9 +5495,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -5119,21 +5508,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" dependencies = [ - "cfg-if", "js-sys", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5141,22 +5528,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -5178,7 +5565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap 2.14.0", "wasm-encoder", "wasmparser", ] @@ -5189,17 +5576,17 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "hashbrown 0.15.5", - "indexmap 2.13.0", + "indexmap 2.14.0", "semver", ] [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" dependencies = [ "js-sys", "wasm-bindgen", @@ -5217,9 +5604,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.1" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86138b15b2b7d561bc4469e77027b8dd005a43dc502e9031d1f5afc8ce1f280e" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" dependencies = [ "rustls-pki-types", ] @@ -5248,14 +5635,14 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.61.1" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections 0.2.0", - "windows-core 0.61.0", - "windows-future 0.2.0", - "windows-link 0.1.1", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", "windows-numerics 0.2.0", ] @@ -5277,7 +5664,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", + "windows-core 0.61.2", ] [[package]] @@ -5291,13 +5678,13 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.1.1", + "windows-link 0.1.3", "windows-result 0.3.4", "windows-strings 0.4.2", ] @@ -5317,12 +5704,13 @@ dependencies = [ [[package]] name = "windows-future" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core 0.61.0", - "windows-link 0.1.1", + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading 0.1.0", ] [[package]] @@ -5333,7 +5721,7 @@ checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ "windows-core 0.62.2", "windows-link 0.2.1", - "windows-threading", + "windows-threading 0.2.1", ] [[package]] @@ -5344,7 +5732,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -5355,14 +5743,14 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-link" @@ -5376,8 +5764,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", - "windows-link 0.1.1", + "windows-core 0.61.2", + "windows-link 0.1.3", ] [[package]] @@ -5390,17 +5778,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-registry" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" -dependencies = [ - "windows-link 0.1.1", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - [[package]] name = "windows-registry" version = "0.6.1" @@ -5418,7 +5795,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link 0.1.1", + "windows-link 0.1.3", ] [[package]] @@ -5436,7 +5813,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link 0.1.1", + "windows-link 0.1.3", ] [[package]] @@ -5491,6 +5868,15 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-threading" version = "0.2.1" @@ -5550,9 +5936,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" dependencies = [ "memchr", ] @@ -5577,15 +5969,6 @@ dependencies = [ "wit-parser", ] -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.10.0", -] - [[package]] name = "wit-bindgen-rust" version = "0.51.0" @@ -5594,9 +5977,9 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck 0.5.0", - "indexmap 2.13.0", + "indexmap 2.14.0", "prettyplease", - "syn 2.0.100", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -5612,7 +5995,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -5624,8 +6007,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.10.0", - "indexmap 2.13.0", + "bitflags 2.11.0", + "indexmap 2.14.0", "log", "serde", "serde_derive", @@ -5644,7 +6027,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.0", + "indexmap 2.14.0", "log", "semver", "serde", @@ -5654,23 +6037,17 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[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", @@ -5684,11 +6061,10 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -5696,82 +6072,62 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" -dependencies = [ - "zerocopy-derive 0.8.24", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", "synstructure", ] [[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" dependencies = [ "zeroize_derive", ] @@ -5784,14 +6140,25 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", ] [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -5800,20 +6167,20 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "zip" -version = "8.1.0" +version = "8.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e499faf5c6b97a0d086f4a8733de6d47aee2252b8127962439d8d4311a73f72" +checksum = "dcab981e19633ebcf0b001ddd37dd802996098bc1864f90b7c5d970ce76c1d59" dependencies = [ "aes", "bzip2", @@ -5821,9 +6188,9 @@ dependencies = [ "crc32fast", "deflate64", "flate2", - "getrandom 0.4.1", + "getrandom 0.4.2", "hmac", - "indexmap 2.13.0", + "indexmap 2.14.0", "lzma-rust2", "memchr", "pbkdf2", @@ -5838,15 +6205,15 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.5.5" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" [[package]] name = "zmij" -version = "1.0.16" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zopfli" @@ -5880,9 +6247,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.15+zstd.1.5.7" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 47c02578..e709b421 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "crates/api_provider", "crates/node", "crates/data_node", + "crates/tx_evaluator", "crates/testgen", "crates/error_decoder", "crates/gateway", @@ -34,6 +35,7 @@ bf-build-utils = { path = "crates/build_utils", package = "blockfrost-platform-b bf-common = { path = "crates/common", package = "blockfrost-platform-common" } bf-data-node = { path = "crates/data_node", package = "blockfrost-platform-data-node" } bf-node = { path = "crates/node", package = "blockfrost-platform-node" } +bf-tx-evaluator = { path = "crates/tx_evaluator", package = "blockfrost-platform-tx-evaluator" } bf-testgen = { path = "crates/testgen", package = "blockfrost-platform-testgen" } bip39 = "2.2.2" blockfrost = { version = "1.2.3", default-features = false, features = [ @@ -67,13 +69,16 @@ nix = { version = "0.30", default-features = false, features = ["signal"] } ntest = "0.9.5" num_cpus = "1" # FIXME: use a proper Pallas release after they merge : -pallas-addresses = { git = "https://github.com/txpipe/pallas.git", rev = "ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" } -pallas-codec = { git = "https://github.com/txpipe/pallas.git", rev = "ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" } -pallas-crypto = { git = "https://github.com/txpipe/pallas.git", rev = "ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" } -pallas-hardano = { git = "https://github.com/txpipe/pallas.git", rev = "ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" } -pallas-network = { git = "https://github.com/txpipe/pallas.git", rev = "ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" } -pallas-primitives = { git = "https://github.com/txpipe/pallas.git", rev = "ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" } -pallas-traverse = { git = "https://github.com/txpipe/pallas.git", rev = "ed422bc0d5a09f77617d4882dd4c71089a8bcdb2" } +pallas-addresses = { git = "https://github.com/blockfrost/pallas.git", rev = "cfa0c633640f5989e44f50ed27efc38992f9ed0d" } +pallas-codec = { git = "https://github.com/blockfrost/pallas.git", rev = "cfa0c633640f5989e44f50ed27efc38992f9ed0d" } +pallas-crypto = { git = "https://github.com/blockfrost/pallas.git", rev = "cfa0c633640f5989e44f50ed27efc38992f9ed0d" } +pallas-hardano = { git = "https://github.com/blockfrost/pallas.git", rev = "cfa0c633640f5989e44f50ed27efc38992f9ed0d" } +pallas-network = { git = "https://github.com/blockfrost/pallas.git", rev = "cfa0c633640f5989e44f50ed27efc38992f9ed0d" } +pallas-primitives = { git = "https://github.com/blockfrost/pallas.git", rev = "cfa0c633640f5989e44f50ed27efc38992f9ed0d" } +pallas-traverse = { git = "https://github.com/blockfrost/pallas.git", rev = "cfa0c633640f5989e44f50ed27efc38992f9ed0d" } +pallas-validate = { git = "https://github.com/blockfrost/pallas.git", rev = "cfa0c633640f5989e44f50ed27efc38992f9ed0d", features = [ + "phase2", +] } pretty_assertions = "1.4.1" proptest = "1.10.0" reqwest = { version = "0.13.2", default-features = false, features = [ diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 823949de..66789ac1 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -35,6 +35,7 @@ tracing-journald.workspace = true [dev-dependencies] pretty_assertions.workspace = true +proptest.workspace = true rstest.workspace = true tokio.workspace = true diff --git a/crates/common/src/chain_config.rs b/crates/common/src/chain_config.rs new file mode 100644 index 00000000..c0eec5f1 --- /dev/null +++ b/crates/common/src/chain_config.rs @@ -0,0 +1,97 @@ +use crate::helpers::system_start_to_epoch_millis; +use pallas_network::miniprotocols::localstate::queries_v16::{CurrentProtocolParam, GenesisConfig}; +use serde::Serialize; + +/// Cached chain configuration queried from the Cardano node at startup and +/// refreshed at epoch boundaries by `ChainConfigWatch`. +pub struct ChainConfigCache { + /// Shelley genesis configuration + pub genesis_config: GenesisConfig, + /// Current protocol parameters + pub protocol_params: CurrentProtocolParam, + /// Slot timing derived from genesis + pub slot_config: SlotConfig, + /// Current Cardano era index (see [`Self::CONWAY_ERA`]) + pub era: u16, +} + +impl ChainConfigCache { + /// Conway era index used by Ouroboros + pub const CONWAY_ERA: u16 = 6; + + pub fn new( + genesis_config: GenesisConfig, + protocol_params: CurrentProtocolParam, + ) -> Result { + let slot_config = SlotConfig::by_network_magic(&genesis_config); + + Ok(Self { + genesis_config, + protocol_params, + slot_config, + era: Self::CONWAY_ERA, + }) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SlotConfig { + /// Duration of a single slot in milliseconds + pub slot_length: u64, + /// Absolute slot number at the start of the Shelley era + pub zero_slot: u64, + /// Unix timestamp in milliseconds corresponding to `zero_slot` + pub zero_time: u64, + /// Number of slots per epoch + pub epoch_length: u64, +} + +impl SlotConfig { + pub fn mainnet() -> Self { + Self { + slot_length: 1000, + zero_slot: 4492800, + zero_time: 1596059091000, + epoch_length: 432000, + } + } + + pub fn preprod() -> Self { + Self { + slot_length: 1000, + zero_slot: 86400, + zero_time: 1655683200000, + epoch_length: 432000, + } + } + + pub fn preview() -> Self { + Self { + slot_length: 1000, + zero_slot: 0, + zero_time: 1666656000000, + epoch_length: 86400, + } + } + + pub fn by_network_magic(genesis_config: &GenesisConfig) -> Self { + match genesis_config.network_magic { + 764824073 => Self::mainnet(), + 1 => Self::preprod(), + 2 => Self::preview(), + _ => Self::from_genesis_config(genesis_config), + } + } + + /// Derive slot config from genesis for custom/unknown networks. + /// Assumes no Byron era (zero_slot = 0, zero_time = system_start). + fn from_genesis_config(genesis_config: &GenesisConfig) -> Self { + Self { + slot_length: genesis_config.slot_length as u64 * 1000, + zero_slot: 0, + zero_time: system_start_to_epoch_millis(&genesis_config.system_start), + epoch_length: genesis_config.epoch_length as u64, + } + } +} diff --git a/crates/common/src/errors.rs b/crates/common/src/errors.rs index d2a062d1..671f23eb 100644 --- a/crates/common/src/errors.rs +++ b/crates/common/src/errors.rs @@ -70,6 +70,12 @@ impl From for BlockfrostError { } } +impl From for BlockfrostError { + fn from(err: serde_json::Error) -> Self { + Self::internal_server_error(format!("JSON error: {err}")) + } +} + impl From for BlockfrostError { fn from(err: AppError) -> Self { match err { @@ -101,12 +107,6 @@ impl From for BlockfrostError { } } -impl From for BlockfrostError { - fn from(e: serde_json::Error) -> Self { - BlockfrostError::internal_server_error(format!("JSON error: {e}")) - } -} - impl From for BlockfrostError { fn from(e: url::ParseError) -> Self { BlockfrostError::internal_server_error(format!("URL error: {e}",)) @@ -220,6 +220,15 @@ impl BlockfrostError { } } + /// Error for 503 Service Unavailable + pub fn service_unavailable(message: String) -> Self { + Self { + error: "Service Unavailable".to_string(), + message, + status_code: 503, + } + } + /// This error is converted in middleware to internal_server_error_user pub fn internal_server_error(error: String) -> Self { Self { @@ -261,6 +270,7 @@ impl IntoResponse for BlockfrostError { 404 => StatusCode::NOT_FOUND, 405 => StatusCode::METHOD_NOT_ALLOWED, 500 => StatusCode::INTERNAL_SERVER_ERROR, + 503 => StatusCode::SERVICE_UNAVAILABLE, _ => StatusCode::INTERNAL_SERVER_ERROR, }; diff --git a/crates/common/src/helpers.rs b/crates/common/src/helpers.rs new file mode 100644 index 00000000..027f209d --- /dev/null +++ b/crates/common/src/helpers.rs @@ -0,0 +1,116 @@ +use crate::errors::BlockfrostError; +use pallas_network::miniprotocols::localstate::queries_v16::{BigInt, SystemStart}; + +/// This function allows us to take both hex-encoded and raw bytes. It has +/// to be a heuristic: if there are input bytes that are not `[0-9a-f]`, +/// then it must be a binary string. Otherwise, we assume it’s hex encoded. +/// +/// **Note**: there is a small probability that the user gave us a binary +/// string that only _looked_ like a hex-encoded one, but it’s rare enough +/// to ignore it. +pub fn binary_or_hex_heuristic(xs: &[u8]) -> Vec { + let even_length = xs.len().is_multiple_of(2); + let contains_non_hex = xs.iter().any(|&x| !x.is_ascii_hexdigit()); + + if !even_length || contains_non_hex { + xs.to_vec() + } else { + hex::decode(xs).unwrap_or_else(|_| unreachable!()) + } +} + +pub fn convert_bigint(bigint: &BigInt) -> Result { + match bigint { + BigInt::Int(big) => Ok(i128::from(*big)), + _ => Err(BlockfrostError::internal_server_error( + "Invalid/unsupported BigInt format".to_string(), + )), + } +} + +/// Convert a `SystemStart` to Unix epoch milliseconds. +/// +/// Date arithmetic is done manually to avoid adding a `chrono` dependency to `common`. +/// +/// # Panics +/// Panics if `SystemStart` contains an unsupported `BigInt` variant. +/// This should only be called during startup with data from the Cardano node. +pub fn system_start_to_epoch_millis(system_start: &SystemStart) -> u64 { + let year = i64::try_from( + convert_bigint(&system_start.year).expect("Failed to convert SystemStart year"), + ) + .expect("SystemStart year out of i64 range"); + let day_of_year = system_start.day_of_year; + let picos = i64::try_from( + convert_bigint(&system_start.picoseconds_of_day) + .expect("Failed to convert SystemStart picoseconds"), + ) + .expect("SystemStart picoseconds out of i64 range"); + + let epoch_days = days_from_year(year) + day_of_year - 1; + (epoch_days as u64) * 86_400_000 + (picos / 1_000_000_000) as u64 +} + +/// Days from Unix epoch (1970-01-01) to January 1 of the given year. +fn days_from_year(year: i64) -> i64 { + let y = year - 1; + 365 * (year - 1970) + (y / 4 - 492) - (y / 100 - 19) + (y / 400 - 4) +} + +#[cfg(test)] +mod tests { + use super::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn proptest_binary_or_hex_heuristic( + binary in prop::collection::vec(any::(), 0..=128) + .prop_filter("exclude values made up only of hex digits", |xs| { + xs.iter().any(|&x| !x.is_ascii_hexdigit()) + }) + ) { + let hex_string = hex::encode(&binary); + assert_eq!( + binary_or_hex_heuristic(hex_string.as_bytes()), + binary_or_hex_heuristic(&binary) + ) + } + } + + #[test] + fn test_days_from_year_known_epochs() { + // 1970-01-01 is day 0 + assert_eq!(days_from_year(1970), 0); + // 2000-01-01 = 10957 days after epoch + assert_eq!(days_from_year(2000), 10957); + // 2017-01-01 = 17167 days after epoch (Cardano mainnet era) + assert_eq!(days_from_year(2017), 17167); + } + + #[test] + fn test_system_start_to_epoch_millis_mainnet() { + // Cardano mainnet SystemStart: 2017-09-23T21:44:51Z + // Unix timestamp: 1506203091000 ms + let system_start = SystemStart { + year: BigInt::Int(2017.into()), + day_of_year: 266, // September 23 = day 266 + picoseconds_of_day: BigInt::Int( + 78_291_000_000_000_000i64.into(), // 21:44:51 = 78291s = 78291e12 picos + ), + }; + assert_eq!(system_start_to_epoch_millis(&system_start), 1506203091000); + } + + #[test] + fn test_system_start_to_epoch_millis_preview() { + // Cardano preview SystemStart: 2022-11-01T00:00:00Z + // Unix timestamp: 1667260800000 ms + let system_start = SystemStart { + year: BigInt::Int(2022.into()), + day_of_year: 305, // November 1 = day 305 + picoseconds_of_day: BigInt::Int(0.into()), + }; + assert_eq!(system_start_to_epoch_millis(&system_start), 1667260800000); + } +} diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 14c9f2a1..98f183d1 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -1,6 +1,8 @@ pub mod cardano_keys; +pub mod chain_config; pub mod errors; pub mod find_libexec; +pub mod helpers; pub mod hydra; pub mod json_client; pub mod pagination; diff --git a/crates/integration_tests/src/gateway/mod.rs b/crates/integration_tests/src/gateway/mod.rs index e36dca4c..c8af8b60 100644 --- a/crates/integration_tests/src/gateway/mod.rs +++ b/crates/integration_tests/src/gateway/mod.rs @@ -244,7 +244,7 @@ pub async fn setup() -> (TestGateway, Client, String, ApiPrefix) { let gw = TestGateway::start().await; let gateway_url = format!("http://{}", gw.addr); - let (app, _, _, icebreakers_api, api_prefix) = + let (app, _, _, icebreakers_api, api_prefix, _) = crate::platform::build_app_non_solitary(Some(gateway_url)) .await .expect("Failed to build the application"); diff --git a/crates/integration_tests/src/platform/mod.rs b/crates/integration_tests/src/platform/mod.rs index d5170eb9..c5c9f0c0 100644 --- a/crates/integration_tests/src/platform/mod.rs +++ b/crates/integration_tests/src/platform/mod.rs @@ -4,7 +4,7 @@ pub mod tx_builder; use axum::Router; use bf_common::types::{LogLevel, Network}; -use bf_node::pool::NodePool; +use bf_node::{chain_config_watch::ChainConfigWatch, pool::NodePool}; use blockfrost_platform::config::{Config, DataNodeConfig, IcebreakersConfig, Mode}; use blockfrost_platform::{ AppError, health_monitor, @@ -13,6 +13,14 @@ use blockfrost_platform::{ }; use std::{env, sync::Arc, time::Duration}; +pub async fn initialize_app() -> Router { + crate::initialize_logging(); + let (app, _, _, _, _, mut config_watch) = + build_app().await.expect("Failed to build the application"); + config_watch.wait_ready().await; + app +} + pub fn test_config(icebreakers_config: Option) -> Arc { dotenvy::dotenv().ok(); @@ -45,6 +53,7 @@ pub async fn build_app() -> Result< health_monitor::HealthMonitor, Option>, ApiPrefix, + ChainConfigWatch, ), AppError, > { @@ -62,6 +71,7 @@ pub async fn build_app_non_solitary( health_monitor::HealthMonitor, Option>, ApiPrefix, + ChainConfigWatch, ), AppError, > { @@ -116,6 +126,7 @@ pub async fn build_app_with_data_node( health_monitor::HealthMonitor, Option>, ApiPrefix, + ChainConfigWatch, ), AppError, > { diff --git a/crates/integration_tests/tests/api/utils.rs b/crates/integration_tests/tests/api/utils.rs new file mode 100644 index 00000000..ddc35f71 --- /dev/null +++ b/crates/integration_tests/tests/api/utils.rs @@ -0,0 +1,6 @@ +pub mod txs { + pub mod evaluate { + pub mod root; + pub mod utxos; + } +} diff --git a/crates/integration_tests/tests/api/utils/txs/evaluate/root.rs b/crates/integration_tests/tests/api/utils/txs/evaluate/root.rs new file mode 100644 index 00000000..224ab11e --- /dev/null +++ b/crates/integration_tests/tests/api/utils/txs/evaluate/root.rs @@ -0,0 +1,286 @@ +mod tests { + use axum::body::to_bytes; + use serde_json::json; + use tower::ServiceExt; + + use integration_tests::platform::initialize_app; + + use axum::{body::Body, http::Request}; + use reqwest::Method; + + #[tokio::test] + async fn test_success_no_version() { + let tx_hex = "84A300818258204E9A66B7E310F004893EEF615E11F8AE6C3328CF2BFDB32F6E40063636D42D7C00018182581D70C40F9129C2684046EB02325B96CA2899A6FA6478C1DDE9B5C53206A51A00D59F800200A10581840000D8799F4D48656C6C6F2C20576F726C6421FF820000F5F6"; + // init our app + let app = initialize_app().await; + + // prepare the request + let request = Request::builder() + .method(Method::POST) + .uri("/utils/txs/evaluate") + .header("Content-Type", "application/cbor") + .body(Body::from(tx_hex)) + .unwrap(); + + // send the request and get the response + let response = app + .oneshot(request) + .await + .expect("Request to /utils/txs/evaluate failed"); + assert!( + response.status().is_success(), + "Response was not successful" + ); + + // Convert the response body to bytes + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .expect("Failed to read response body"); + + let body_json: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); + + assert_eq!(body_json["type"], "jsonwsp/response"); + assert_eq!(body_json["version"], "1.0"); + assert_eq!(body_json["servicename"], "ogmios"); + assert_eq!(body_json["methodname"], "EvaluateTx"); + assert!(body_json["reflection"]["id"].is_string()); + assert_eq!( + body_json["result"], + json!({"EvaluationResult": {"spend:0": {"memory": 15694, "steps": 3776164}}}) + ); + } + + #[tokio::test] + async fn test_success_v5() { + let tx_hex = "84A300818258204E9A66B7E310F004893EEF615E11F8AE6C3328CF2BFDB32F6E40063636D42D7C00018182581D70C40F9129C2684046EB02325B96CA2899A6FA6478C1DDE9B5C53206A51A00D59F800200A10581840000D8799F4D48656C6C6F2C20576F726C6421FF820000F5F6"; + // init our app + let app = initialize_app().await; + + // prepare the request + let request = Request::builder() + .method(Method::POST) + .uri("/utils/txs/evaluate?version=5") + .header("Content-Type", "application/cbor") + .body(Body::from(tx_hex)) + .unwrap(); + + // send the request and get the response + let response = app + .oneshot(request) + .await + .expect("Request to /utils/txs/evaluate failed"); + assert!( + response.status().is_success(), + "Response was not successful" + ); + + // Convert the response body to bytes + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .expect("Failed to read response body"); + + let body_json: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); + + assert_eq!(body_json["type"], "jsonwsp/response"); + assert_eq!(body_json["servicename"], "ogmios"); + assert_eq!(body_json["methodname"], "EvaluateTx"); + assert!(body_json["reflection"]["id"].is_string()); + assert_eq!( + body_json["result"], + json!({"EvaluationResult": {"spend:0": {"memory": 15694, "steps": 3776164}}}) + ); + } + + #[tokio::test] + #[ignore = "pallas-validate does not generate same results with the external evaluator (ledger)"] + async fn test_success_v5_native() { + let tx_hex = "84A300818258204E9A66B7E310F004893EEF615E11F8AE6C3328CF2BFDB32F6E40063636D42D7C00018182581D70C40F9129C2684046EB02325B96CA2899A6FA6478C1DDE9B5C53206A51A00D59F800200A10581840000D8799F4D48656C6C6F2C20576F726C6421FF820000F5F6"; + // init our app + let app = initialize_app().await; + + // prepare the request + let request = Request::builder() + .method(Method::POST) + .uri("/utils/txs/evaluate?version=5&evaluator=native") + .header("Content-Type", "application/cbor") + .body(Body::from(tx_hex)) + .unwrap(); + + // send the request and get the response + let response = app + .oneshot(request) + .await + .expect("Request to /utils/txs/evaluate failed"); + assert!( + response.status().is_success(), + "Response was not successful" + ); + + // Convert the response body to bytes + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .expect("Failed to read response body"); + + let body_json: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); + + assert_eq!(body_json["type"], "jsonwsp/response"); + assert_eq!(body_json["servicename"], "ogmios"); + assert_eq!(body_json["methodname"], "EvaluateTx"); + assert!(body_json["reflection"]["id"].is_string()); + assert_eq!( + body_json["result"], + json!({"EvaluationResult": {"spend:0": {"memory": 15694, "steps": 3776164}}}) + ); + } + #[tokio::test] + async fn test_success_v6() { + let tx_hex = "84A300818258204E9A66B7E310F004893EEF615E11F8AE6C3328CF2BFDB32F6E40063636D42D7C00018182581D70C40F9129C2684046EB02325B96CA2899A6FA6478C1DDE9B5C53206A51A00D59F800200A10581840000D8799F4D48656C6C6F2C20576F726C6421FF820000F5F6"; + // init our app + let app = initialize_app().await; + + // prepare the request + let request = Request::builder() + .method(Method::POST) + .uri("/utils/txs/evaluate?version=6") + .header("Content-Type", "application/cbor") + .body(Body::from(tx_hex)) + .unwrap(); + + // send the request and get the response + let response = app + .oneshot(request) + .await + .expect("Request to /utils/txs/evaluate failed"); + assert!( + response.status().is_success(), + "Response was not successful" + ); + + // Convert the response body to bytes + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .expect("Failed to read response body"); + + let body_json: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); + + assert_eq!(body_json["jsonrpc"], "2.0"); + assert_eq!(body_json["method"], "evaluateTransaction"); + assert!(body_json["id"].is_string()); + assert_eq!( + body_json["result"], + json!([{"budget": {"cpu": 3776164, "memory": 15694}, "validator": {"purpose": "spend", "index": 0}}]) + ); + } + + /// Unknown version numbers are silently treated as v5 + #[tokio::test] + async fn test_unknown_version_falls_back_to_v5() { + let tx_hex = "84A300818258204E9A66B7E310F004893EEF615E11F8AE6C3328CF2BFDB32F6E40063636D42D7C00018182581D70C40F9129C2684046EB02325B96CA2899A6FA6478C1DDE9B5C53206A51A00D59F800200A10581840000D8799F4D48656C6C6F2C20576F726C6421FF820000F5F6"; + let app = initialize_app().await; + + let request = Request::builder() + .method(Method::POST) + .uri("/utils/txs/evaluate?version=53") + .header("Content-Type", "application/cbor") + .body(Body::from(tx_hex)) + .unwrap(); + + let response = app + .oneshot(request) + .await + .expect("Request to /utils/txs/evaluate failed"); + + assert!( + response.status().is_success(), + "Response was not successful" + ); + + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .expect("Failed to read response body"); + + let body_json: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); + + assert_eq!( + body_json.get("type").unwrap().as_str().unwrap(), + "jsonwsp/response" + ); + assert_eq!( + body_json.get("methodname").unwrap().as_str().unwrap(), + "EvaluateTx" + ); + } + + /// Pre-Alonzo (Mary era) transaction returns IncompatibleEra in v5 format + #[tokio::test] + async fn test_fail_incompatible_era_v5() { + let tx_hex = "83a300818258200ac82ea5bc0967a17d4a60e2474b01df72440673429ff89b2802d3bd2a38ec3e01018282583900e2fbc47df26fcd065c074c451e792599ea8fc159f76163ca4c2b520b58adbef896164ee7456ccb4eaa965a87a602b0e3b2825d7b4ee789b01a000f4240825839003c77cd7f3c07b3b0ba72044848592d2e5687569ad25b93a926392f5e83892080b40900e146e1c68f12ef6811773bd8740196cd211f3211de1af9b0595d021a0002c5bda10081825820da818bbf3a082945884681d062147ca7dc3111d87fab415268749124a3ed1d31584059ca300a7d38abf454482a57281acdbbaab740b868978131f36117a224e6ba2be5248da0205296d7a8211506d6430a2873c201831e326e5db68ac9e1403e520ef6"; + let app = initialize_app().await; + + let request = Request::builder() + .method(Method::POST) + .uri("/utils/txs/evaluate?version=5") + .header("Content-Type", "application/cbor") + .body(Body::from(tx_hex)) + .unwrap(); + + let response = app + .oneshot(request) + .await + .expect("Request to /utils/txs/evaluate failed"); + assert!( + response.status().is_success(), + "Response was not successful" + ); + + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .expect("Failed to read response body"); + + let body_json: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); + + assert_eq!( + body_json.get("type").unwrap().as_str().unwrap(), + "jsonwsp/response" + ); + assert_eq!( + body_json["result"]["EvaluationFailure"]["IncompatibleEra"] + .as_str() + .unwrap(), + "Mary" + ); + } + + /// Pre-Alonzo (Mary era) transaction returns IncompatibleEra in v6 format (code 3000) + #[tokio::test] + async fn test_fail_incompatible_era_v6() { + let tx_hex = "83a300818258200ac82ea5bc0967a17d4a60e2474b01df72440673429ff89b2802d3bd2a38ec3e01018282583900e2fbc47df26fcd065c074c451e792599ea8fc159f76163ca4c2b520b58adbef896164ee7456ccb4eaa965a87a602b0e3b2825d7b4ee789b01a000f4240825839003c77cd7f3c07b3b0ba72044848592d2e5687569ad25b93a926392f5e83892080b40900e146e1c68f12ef6811773bd8740196cd211f3211de1af9b0595d021a0002c5bda10081825820da818bbf3a082945884681d062147ca7dc3111d87fab415268749124a3ed1d31584059ca300a7d38abf454482a57281acdbbaab740b868978131f36117a224e6ba2be5248da0205296d7a8211506d6430a2873c201831e326e5db68ac9e1403e520ef6"; + let app = initialize_app().await; + + let request = Request::builder() + .method(Method::POST) + .uri("/utils/txs/evaluate?version=6") + .header("Content-Type", "application/cbor") + .body(Body::from(tx_hex)) + .unwrap(); + + let response = app + .oneshot(request) + .await + .expect("Request to /utils/txs/evaluate failed"); + assert!( + response.status().is_success(), + "Response was not successful" + ); + + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .expect("Failed to read response body"); + + let body_json: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); + + let error = body_json.get("error").unwrap(); + assert_eq!(error.get("code").unwrap().as_i64().unwrap(), 3000); + assert_eq!(error["data"]["incompatibleEra"].as_str().unwrap(), "mary"); + } +} diff --git a/crates/integration_tests/tests/api/utils/txs/evaluate/utxos.rs b/crates/integration_tests/tests/api/utils/txs/evaluate/utxos.rs new file mode 100644 index 00000000..a3756df9 --- /dev/null +++ b/crates/integration_tests/tests/api/utils/txs/evaluate/utxos.rs @@ -0,0 +1,140 @@ +mod tests { + use serde_json::json; + use tower::ServiceExt; + + use integration_tests::platform::initialize_app; + + use axum::{ + body::{Body, to_bytes}, + http::Request, + }; + use reqwest::Method; + + /// This test is identical to the Blockfrost test: + /// https://github.com/blockfrost/blockfrost-tests/blob/dc33312126e9d7c49836d7605ab72224a337bc91/src/fixtures/preview/utils/txs-evaluate-utxos.ts#L5 + /// https://github.com/blockfrost/blockfrost-tests/blob/dc33312126e9d7c49836d7605ab72224a337bc91/src/fixtures/preview/utils/txs-evaluate-utxos.ts#L22 + #[tokio::test] + async fn success_cbor_only() { + // init our app + let app = initialize_app().await; + + let input = json!({ + "cbor": "84A300818258204E9A66B7E310F004893EEF615E11F8AE6C3328CF2BFDB32F6E40063636D42D7C00018182581D70C40F9129C2684046EB02325B96CA2899A6FA6478C1DDE9B5C53206A51A00D59F800200A10581840000D8799F4D48656C6C6F2C20576F726C6421FF820000F5F6", + }); + + // prepare the request + let request = Request::builder() + .method(Method::POST) + .uri("/utils/txs/evaluate/utxos") + .header("Content-Type", "application/json") + .body(Body::from(input.to_string())) + .unwrap(); + + // send the request and get the response + let response = app + .oneshot(request) + .await + .expect("Request to /utils/txs/evaluate failed"); + + assert!(response.status().is_success(), "Response should success"); + + // Convert the response body to bytes + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .expect("Failed to read response body"); + + let body_json: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); + + assert_eq!(body_json["type"], "jsonwsp/response"); + assert_eq!(body_json["servicename"], "ogmios"); + assert_eq!(body_json["methodname"], "EvaluateTx"); + assert!(body_json["reflection"]["id"].is_string()); + assert_eq!( + body_json["result"], + json!({"EvaluationResult": {"spend:0": {"memory": 15694, "steps": 3776164}}}) + ); + } + + /// Verifies that the mirror field sent by the client is NOT echoed back. + #[tokio::test] + async fn success_mirror_not_echoed() { + let app = initialize_app().await; + + let input = json!({ + "cbor": "84A300818258204E9A66B7E310F004893EEF615E11F8AE6C3328CF2BFDB32F6E40063636D42D7C00018182581D70C40F9129C2684046EB02325B96CA2899A6FA6478C1DDE9B5C53206A51A00D59F800200A10581840000D8799F4D48656C6C6F2C20576F726C6421FF820000F5F6", + "mirror": {"id": "test-request-123"} + }); + + let request = Request::builder() + .method(Method::POST) + .uri("/utils/txs/evaluate/utxos") + .header("Content-Type", "application/json") + .body(Body::from(input.to_string())) + .unwrap(); + + let response = app + .oneshot(request) + .await + .expect("Request to /utils/txs/evaluate/utxos failed"); + + assert!(response.status().is_success()); + + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .expect("Failed to read response body"); + + let body_json: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); + + assert_eq!(body_json["type"], "jsonwsp/response"); + assert!(body_json["reflection"]["id"].is_string()); + assert_ne!(body_json["reflection"]["id"], "test-request-123"); + } + + /// This test is identical to the Blockfrost test: + /// https://github.com/blockfrost/blockfrost-tests/blob/dc33312126e9d7c49836d7605ab72224a337bc91/src/fixtures/preview/utils/txs-evaluate-utxos.ts#L43 + #[tokio::test] + #[ignore = "not implemented yet"] + async fn fail_missing_input() { + // init our app + let app = initialize_app().await; + + let input = json!({ + "cbor": "".to_string() + + "84A30081825820FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + + "FFFFFFFFFFFFFFFF7C00018182581D70C40F9129C2684046EB02325B96CA" + + "2899A6FA6478C1DDE9B5C53206A51A00D59F800200A10581840000D8799F" + + "4D48656C6C6F2C20576F726C6421FF820000F5F6", + "additionalUtxoSet": [ + + ] + }); + + // prepare the request + let request = Request::builder() + .method(Method::POST) + .uri("/utils/txs/evaluate/utxos") + .header("Content-Type", "application/json") + .body(Body::from(input.to_string())) + .unwrap(); + + // send the request and get the response + let response = app + .oneshot(request) + .await + .expect("Request to /utils/txs/evaluate failed"); + assert!(response.status().is_success(), "Response should success"); + + // Convert the response body to bytes + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .expect("Failed to read response body"); + + // Convert the bytes to a string and print + let body_str = String::from_utf8_lossy(&body_bytes); + + assert_eq!( + body_str, + "{\"error\":\"Bad Request\",\"message\":\"Error evaluating transaction: resolved Input not found\",\"status_code\":400}" + ); + } +} diff --git a/crates/integration_tests/tests/data/supported_endpoints.json b/crates/integration_tests/tests/data/supported_endpoints.json index 02798d1a..36fbe0da 100644 --- a/crates/integration_tests/tests/data/supported_endpoints.json +++ b/crates/integration_tests/tests/data/supported_endpoints.json @@ -37,5 +37,7 @@ "/epochs/latest/parameters", "/genesis", "/addresses/{address}/transactions", - "/addresses/{address}/utxos" + "/addresses/{address}/utxos", + "/utils/txs/evaluate", + "/utils/txs/evaluate/utxos" ] diff --git a/crates/integration_tests/tests/e2e_websocket.rs b/crates/integration_tests/tests/e2e_websocket.rs index 1a2550ca..6e11ab41 100644 --- a/crates/integration_tests/tests/e2e_websocket.rs +++ b/crates/integration_tests/tests/e2e_websocket.rs @@ -116,7 +116,7 @@ async fn test_ws_invalid_credentials_rejected() { }; let config = test_config(Some(icebreakers_config)); - let (app, _, _, icebreakers_api, api_prefix) = + let (app, _, _, icebreakers_api, api_prefix, _) = build(config).await.expect("Failed to build app"); let icebreakers_api = icebreakers_api.expect("icebreakers_api should be Some"); diff --git a/crates/integration_tests/tests/eval_test.rs b/crates/integration_tests/tests/eval_test.rs new file mode 100644 index 00000000..30d231b7 --- /dev/null +++ b/crates/integration_tests/tests/eval_test.rs @@ -0,0 +1,3 @@ +pub mod api { + pub mod utils; +} diff --git a/crates/integration_tests/tests/platform_data_node.rs b/crates/integration_tests/tests/platform_data_node.rs index 34cd6958..7a6e82d8 100644 --- a/crates/integration_tests/tests/platform_data_node.rs +++ b/crates/integration_tests/tests/platform_data_node.rs @@ -34,7 +34,7 @@ async fn test_data_node_health_monitoring() { initialize_logging(); let mock = MockDataNode::healthy().await; - let (app, _, _, _, _) = build_app_with_data_node(mock.url) + let (app, _, _, _, _, _) = build_app_with_data_node(mock.url) .await .expect("Failed to build the application"); @@ -59,7 +59,7 @@ async fn test_data_node_unhealthy_status() { initialize_logging(); let mock = MockDataNode::unhealthy().await; - let (app, _, _, _, _) = build_app_with_data_node(mock.url) + let (app, _, _, _, _, _) = build_app_with_data_node(mock.url) .await .expect("Failed to build the application"); @@ -81,7 +81,7 @@ async fn test_data_node_unreachable() { initialize_logging(); let mock = MockDataNode::unreachable(); - let (app, _, _, _, _) = build_app_with_data_node(mock.url) + let (app, _, _, _, _, _) = build_app_with_data_node(mock.url) .await .expect("Failed to build the application"); diff --git a/crates/integration_tests/tests/platform_metrics.rs b/crates/integration_tests/tests/platform_metrics.rs index 339a342f..ee81c82c 100644 --- a/crates/integration_tests/tests/platform_metrics.rs +++ b/crates/integration_tests/tests/platform_metrics.rs @@ -12,7 +12,7 @@ use tower::ServiceExt; async fn test_route_metrics() { initialize_logging(); - let (app, _, _, _, _) = build_app().await.expect("Failed to build the application"); + let (app, _, _, _, _, _) = build_app().await.expect("Failed to build the application"); // Test without trailing slash let response = app diff --git a/crates/integration_tests/tests/platform_root.rs b/crates/integration_tests/tests/platform_root.rs index d0fe198a..4dfcc0ba 100644 --- a/crates/integration_tests/tests/platform_root.rs +++ b/crates/integration_tests/tests/platform_root.rs @@ -14,7 +14,7 @@ use tower::ServiceExt; async fn test_route_root() { initialize_logging(); - let (app, _, _, _, _) = build_app().await.expect("Failed to build the application"); + let (app, _, _, _, _, _) = build_app().await.expect("Failed to build the application"); let response = app .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) diff --git a/crates/integration_tests/tests/platform_submit.rs b/crates/integration_tests/tests/platform_submit.rs index 8401e95b..785265a8 100644 --- a/crates/integration_tests/tests/platform_submit.rs +++ b/crates/integration_tests/tests/platform_submit.rs @@ -15,7 +15,7 @@ use tower::ServiceExt; #[ntest::timeout(120_000)] async fn test_route_submit_cbor_error() { initialize_logging(); - let (app, _, _, _, _) = build_app().await.expect("Failed to build the application"); + let (app, _, _, _, _, _) = build_app().await.expect("Failed to build the application"); let tx = "AAAAAA"; @@ -48,7 +48,7 @@ async fn test_route_submit_cbor_error() { #[ntest::timeout(120_000)] async fn test_route_submit_error() { initialize_logging(); - let (app, _, _, _, _) = build_app().await.expect("Failed to build the application"); + let (app, _, _, _, _, _) = build_app().await.expect("Failed to build the application"); let tx = "84a300d90102818258205176274bef11d575edd6aa72392aaf993a07f736e70239c1fb22d4b1426b22bc01018282583900ddf1eb9ce2a1561e8f156991486b97873fb6969190cbc99ddcb3816621dcb03574152623414ed354d2d8f50e310f3f2e7d167cb20e5754271a003d09008258390099a5cb0fa8f19aba38cacf8a243d632149129f882df3a8e67f6bd512bcb0cde66a545e9fbc7ca4492f39bca1f4f265cc1503b4f7d6ff205c1b000000024f127a7c021a0002a2ada100d90102818258208b83e59abc9d7a66a77be5e0825525546a595174f8b929f164fcf5052d7aab7b5840709c64556c946abf267edd90b8027343d065193ef816529d8fa7aa2243f1fd2ec27036a677974199e2264cb582d01925134b9a20997d5a734da298df957eb002f5f6"; @@ -92,7 +92,7 @@ async fn test_route_submit_error() { #[ntest::timeout(120_000)] async fn test_route_submit_agent_dequeu() { initialize_logging(); - let (app, _, _, _, _) = build_app().await.expect("Failed to build the application"); + let (app, _, _, _, _, _) = build_app().await.expect("Failed to build the application"); let tx = "84a800848258204c16d304e6d531c59afd87a9199b7bb4175bc131b3d6746917901046b662963c00825820893c3f630c0b2db16d041c388aa0d58746ccbbc44133b2d7a3127a72c79722f1018258200998adb591c872a241776e39fe855e04b2d7c361008e94c582f59b6b6ccc452c028258208380ce7240ba59187f6450911f74a70cf3d2749228badb2e7cd10fb6499355f503018482581d61e15900a9a62a8fb01f936a25bf54af209c7ed1248c4e5abd05ec4e76821a0023ba63a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b5900a300581d71cba5c6770fe7b30ebc1fa32f01938c150513211360ded23ac76e36b301821a006336d5a3581c239075b83c03c2333eacd0b0beac6b8314f11ce3dc0c047012b0cad4a144706f6f6c01581c3547b4325e495d529619335603ababde10025dceafa9ed34b1fb6611a158208b284793d3bd4967244a2ddd68410d56d06d36ac8d201429b937096a2e8234bc1b7ffffffffffade6b581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b59195e99028201d818583ad8799fd8799f4040ffd8799f581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c23545484f534b59ff1a006336d5195e99ff825839016d06090559d8ed2988aa5b2fff265d668cf552f4f62278c0128f816c0a48432e080280d0d9b15edb65563995f97ce236035afea568e660d1821a00118f32a1581c2f8b2d1f384485896f38406173fa11df2a4ce53b4b0886138b76597aa1476261746368657201825839016d06090559d8ed2988aa5b2fff265d668cf552f4f62278c0128f816c0a48432e080280d0d9b15edb65563995f97ce236035afea568e660d11a06d9f713021a000ab9e00b582027f17979d848d6472896266dd8bf39f7251ca23798713464bc407bf637286c230d81825820cf5de9189b958f8ad64c1f1837c2fa4711d073494598467a1c1a59589393eae20310825839016d06090559d8ed2988aa5b2fff265d668cf552f4f62278c0128f816c0a48432e080280d0d9b15edb65563995f97ce236035afea568e660d11a08666c75111a001016d01282825820bf93dc59c10c19c35210c2414779d7391ca19128cc7b13794ea85af5ff835f59008258201c37df764f8261edce8678b197767668a91d544b2b203fb5d0cf9acc10366e7600a200818258200eabfa083d7969681d2fc8e825a5f79e1c40f03aeac46ecd94bf5c5790db1bc058409a029ddd3cdde65598bb712c640ea63eeebfee526ce49bd0983b4d1fdca858481ddf931bf0354552cc0a7d3365e2f03fdb457c0466cea8b371b645f9b6d0c2010582840001d8799fd8799f011a006336d5195e991b7ffffffffffade6bd8799f1a000539e7ff01ffff821a000b46e41a0a7f3ca4840003d87d80821a002dccfe1a28868be8f5f6"; @@ -127,7 +127,7 @@ async fn test_route_submit_agent_dequeu() { #[ntest::timeout(120_000)] async fn test_route_submit_success() { initialize_logging(); - let (app, _, _, _, _) = build_app().await.expect("Failed to build the application"); + let (app, _, _, _, _, _) = build_app().await.expect("Failed to build the application"); let blockfrost_client = get_blockfrost_client(); let tx = build_tx(&blockfrost_client).await.unwrap(); diff --git a/crates/node/src/cbor/validation.rs b/crates/node/src/cbor/validation.rs index c388890a..4fcbde4b 100644 --- a/crates/node/src/cbor/validation.rs +++ b/crates/node/src/cbor/validation.rs @@ -1,7 +1,7 @@ use bf_common::errors::BlockfrostError; use pallas_hardano::display::haskell_error::as_cbor_decode_failure; use pallas_primitives::{alonzo::Value, babbage::GenTransactionOutput, conway::Tx}; -use tracing::warn; +use tracing::info; /// Checks if the given transaction is a valid CBOR-encoded transaction, by trying to decode it. /// This function is used to validate the transaction before submitting it to the node. @@ -11,20 +11,26 @@ pub(crate) fn validate_tx_cbor(tx: &[u8]) -> Result<(), BlockfrostError> { match pallas_codec::minicbor::decode::(tx) { Ok(decoded) => { if _check_multiasset_zero(decoded) { - Err(BlockfrostError::custom_400( - as_cbor_decode_failure("MultiAsset cannot contain zeros".to_string(), 0) - .unwrap_or_else(|e| format!("Failed to format decode error: {e}")), - )) + let msg = as_cbor_decode_failure("MultiAsset cannot contain zeros".to_string(), 0) + .unwrap_or_else(|e| { + // pallas hardano failure, never expected to happen + tracing::warn!("Failed to format CBOR decode error: {e}"); + "transaction contains MultiAsset with zeros".to_string() + }); + Err(BlockfrostError::custom_400(msg)) } else { Ok(()) } }, Err(e) => { - warn!("Invalid TX CBOR: {:?}, CBOR: {}", e, hex::encode(tx)); - Err(BlockfrostError::custom_400( - as_cbor_decode_failure(e.to_string(), e.position().unwrap_or(0) as u64) - .unwrap_or_else(|e| format!("Failed to format decode error: {e}")), - )) + info!("Invalid TX CBOR: {:?}, CBOR: {}", e, hex::encode(tx)); + let msg = as_cbor_decode_failure(e.to_string(), e.position().unwrap_or(0) as u64) + .unwrap_or_else(|fmt_err| { + // pallas hardano failure, never expected to happen + tracing::warn!("Failed to format CBOR decode error: {fmt_err}"); + "transaction CBOR decode failure".to_string() + }); + Err(BlockfrostError::custom_400(msg)) }, } } diff --git a/crates/node/src/chain_config.rs b/crates/node/src/chain_config.rs new file mode 100644 index 00000000..aae8dfd0 --- /dev/null +++ b/crates/node/src/chain_config.rs @@ -0,0 +1,55 @@ +use bf_common::{ + chain_config::{ChainConfigCache, SlotConfig}, + errors::AppError, +}; + +use crate::pool::NodePool; + +/// inits the cache only if the chain tip is in the latest epoch +pub async fn init_caches(node_pool: NodePool) -> Result { + let mut node = node_pool.get().await?; + let (genesis_config, protocol_params, tip_slot) = + node.genesis_config_and_pp().await.map_err(|e| { + AppError::Server(format!( + "Could not fetch genesis and protocol parameters. Is the Cardano node running? {e}" + )) + })?; + + let config = + ChainConfigCache::new(genesis_config, protocol_params).map_err(AppError::Server)?; + + // Node's tip must be within the last epoch + let expected_slot = current_slot(&config.slot_config); + if expected_slot.saturating_sub(tip_slot) > config.slot_config.epoch_length { + return Err(AppError::Server(format!( + "Node tip slot ({tip_slot}) is more than one epoch behind expected slot ({expected_slot})" + ))); + } + + Ok(config) +} + +fn current_slot(slot_config: &SlotConfig) -> u64 { + let now_ms = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64; + let elapsed_ms = now_ms.saturating_sub(slot_config.zero_time); + slot_config.zero_slot + elapsed_ms / slot_config.slot_length +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_current_slot_future_zero_time_returns_zero_slot() { + let config = SlotConfig { + slot_length: 1000, + zero_slot: 100, + zero_time: u64::MAX, + epoch_length: 432000, + }; + assert_eq!(current_slot(&config), 100); + } +} diff --git a/crates/node/src/chain_config_watch.rs b/crates/node/src/chain_config_watch.rs new file mode 100644 index 00000000..0341ee05 --- /dev/null +++ b/crates/node/src/chain_config_watch.rs @@ -0,0 +1,233 @@ +use std::sync::Arc; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +use bf_common::chain_config::ChainConfigCache; +use bf_common::errors::BlockfrostError; +use tokio::sync::watch; +use tokio::time; + +use crate::chain_config::init_caches; +use crate::pool::NodePool; + +const RETRY_INTERVAL: Duration = Duration::from_secs(60); +/// Buffer after computed epoch boundary before re-querying protocol params +const EPOCH_BOUNDARY_BUFFER: Duration = Duration::from_secs(30); + +/// Watches the Cardano node for sync readiness and epoch-boundary protocol +/// parameter changes, publishing [`ChainConfigCache`] updates via a +/// `tokio::sync::watch` channel. +#[derive(Clone)] +pub struct ChainConfigWatch { + rx: watch::Receiver>>, +} + +impl ChainConfigWatch { + /// Spawn the background monitor and return immediately + /// + /// The watch value starts as `None` and is set to `Some(…)` once the node + /// is synced (tip within one epoch of expected slot) + pub fn spawn(node_pool: NodePool) -> Self { + let (tx, rx) = watch::channel(None); + + tokio::spawn(async move { + monitor_loop(node_pool, tx).await; + }); + + Self { rx } + } + + /// Returns the current config or a 503 if the node is not yet synced + pub fn get(&self) -> Result, BlockfrostError> { + self.rx.borrow().clone().ok_or_else(|| { + BlockfrostError::service_unavailable( + "Chain configuration is not yet available. The Cardano node may still be syncing." + .to_string(), + ) + }) + } + + /// Wait until the first config is available (node synced and init complete) + pub async fn wait_ready(&mut self) { + while self.rx.borrow().is_none() { + match self.rx.changed().await { + Ok(_) => {}, + Err(err) => { + tracing::error!( + "ChainConfigWatch: watch channel closed before configuration became available: {err}" + ); + break; + }, + } + } + } +} + +/// The main background loop: init config, then watch for epoch changes. +async fn monitor_loop(node_pool: NodePool, tx: watch::Sender>>) { + // Phase 1: first successful init, retried until chain tip is in the latest epoch + let config = match init_until_success(&node_pool, &tx).await { + Some(c) => c, + None => return, + }; + let mut slot_config = config.slot_config.clone(); + if tx.send(Some(Arc::new(config))).is_err() { + return; + } + tracing::info!("ChainConfigWatch: chain configuration loaded"); + + // Phase 2: watch for epoch changes. + loop { + let sleep_dur = duration_until_next_epoch(&slot_config); + tokio::select! { + () = time::sleep(sleep_dur) => {}, + () = tx.closed() => { + tracing::info!("ChainConfigWatch: stopping monitor"); + return; + }, + } + + let new_config = loop { + match init_caches(node_pool.clone()).await { + Ok(c) => break c, + Err(e) => { + tracing::warn!( + "ChainConfigWatch: failed to refresh chain config: {e}. Retrying in {}s.", + RETRY_INTERVAL.as_secs() + ); + tokio::select! { + () = time::sleep(RETRY_INTERVAL) => {}, + () = tx.closed() => { + tracing::info!("ChainConfigWatch: stopping monitor"); + return; + }, + } + }, + } + }; + + slot_config = new_config.slot_config.clone(); + if tx.send(Some(Arc::new(new_config))).is_err() { + break; + } + } +} + +/// Retry `init_caches` until it succeeds +/// Returns `None` if all receivers were dropped (shutdown) +async fn init_until_success( + node_pool: &NodePool, + tx: &watch::Sender>>, +) -> Option { + loop { + match init_caches(node_pool.clone()).await { + Ok(config) => return Some(config), + Err(e) => { + tracing::debug!( + "ChainConfigWatch: failed to load chain config from node: {e}. Retrying in {}s.", + RETRY_INTERVAL.as_secs() + ); + tokio::select! { + () = time::sleep(RETRY_INTERVAL) => {}, + () = tx.closed() => return None, + } + }, + } + } +} + +/// Compute how long to sleep until the next epoch boundary (plus buffer). +fn duration_until_next_epoch(slot_config: &bf_common::chain_config::SlotConfig) -> Duration { + if slot_config.slot_length == 0 || slot_config.epoch_length == 0 { + return RETRY_INTERVAL; + } + + let now_ms = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64; + + // Current slot (may be approximate but close enough for scheduling). + let elapsed_ms = now_ms.saturating_sub(slot_config.zero_time); + let current_slot = slot_config.zero_slot + elapsed_ms / slot_config.slot_length; + + // Slots into the current epoch. + let slot_in_epoch = (current_slot - slot_config.zero_slot) % slot_config.epoch_length; + let slots_until_next = slot_config.epoch_length - slot_in_epoch; + + let ms_until_next = slots_until_next * slot_config.slot_length; + Duration::from_millis(ms_until_next) + EPOCH_BOUNDARY_BUFFER +} + +#[cfg(test)] +mod tests { + use super::*; + use bf_common::chain_config::SlotConfig; + + #[test] + fn test_zero_slot_length_returns_retry_interval() { + let config = SlotConfig { + slot_length: 0, + zero_slot: 0, + zero_time: 0, + epoch_length: 432000, + }; + assert_eq!(duration_until_next_epoch(&config), RETRY_INTERVAL); + } + + #[test] + fn test_zero_epoch_length_returns_retry_interval() { + let config = SlotConfig { + slot_length: 1000, + zero_slot: 0, + zero_time: 0, + epoch_length: 0, + }; + assert_eq!(duration_until_next_epoch(&config), RETRY_INTERVAL); + } + + #[test] + fn test_result_includes_buffer() { + let result = duration_until_next_epoch(&SlotConfig::preview()); + assert!(result >= EPOCH_BOUNDARY_BUFFER); + } + + #[test] + fn test_result_at_most_one_epoch() { + let config = SlotConfig::preview(); + let max = + Duration::from_millis(config.epoch_length * config.slot_length) + EPOCH_BOUNDARY_BUFFER; + let result = duration_until_next_epoch(&config); + assert!(result <= max, "result {result:?} exceeds max {max:?}"); + } + + #[test] + fn test_exact_epoch_boundary_waits_full_epoch() { + let now_ms = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + let config = SlotConfig { + slot_length: 1000, + zero_slot: 0, + zero_time: now_ms, // current time → current_slot = 0 → slot_in_epoch = 0 + epoch_length: 100, + }; + let result = duration_until_next_epoch(&config); + let full_epoch = Duration::from_millis(config.epoch_length * config.slot_length); + // Should be approximately full_epoch + buffer, not just buffer + assert!( + result > full_epoch, + "At epoch boundary, expected > {full_epoch:?}, got {result:?}" + ); + } + + #[test] + fn test_mainnet_config() { + let config = SlotConfig::mainnet(); + let max = + Duration::from_millis(config.epoch_length * config.slot_length) + EPOCH_BOUNDARY_BUFFER; + let result = duration_until_next_epoch(&config); + assert!(result >= EPOCH_BOUNDARY_BUFFER); + assert!(result <= max); + } +} diff --git a/crates/node/src/ledger.rs b/crates/node/src/ledger.rs new file mode 100644 index 00000000..6469026b --- /dev/null +++ b/crates/node/src/ledger.rs @@ -0,0 +1,29 @@ +use pallas_network::miniprotocols::localstate::{ + self, + queries_v16::{CurrentProtocolParam, GenesisConfig}, +}; + +use super::connection::NodeClient; + +use bf_common::errors::BlockfrostError; + +impl NodeClient { + pub async fn genesis_config_and_pp( + &mut self, + ) -> Result<(GenesisConfig, CurrentProtocolParam, u64), BlockfrostError> { + self.with_statequery(|generic_client: &mut localstate::GenericClient| { + Box::pin(async { + let era = localstate::queries_v16::get_current_era(generic_client).await?; + let genesis = + localstate::queries_v16::get_genesis_config(generic_client, era).await?; + let params = + localstate::queries_v16::get_current_pparams(generic_client, era).await?; + let tip_slot = localstate::queries_v16::get_chain_point(generic_client) + .await? + .slot_or_default(); + Ok((genesis, params, tip_slot)) + }) + }) + .await + } +} diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 77f47919..cafe7cb5 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -1,7 +1,11 @@ pub mod cbor; +pub mod chain_config; +pub mod chain_config_watch; pub mod connection; +pub mod ledger; pub mod monitoring; pub mod pool; pub mod pool_manager; pub mod sync_progress; pub mod transactions; +pub mod utxo; diff --git a/crates/node/src/transactions.rs b/crates/node/src/transactions.rs index f19b6da5..6bb846a8 100644 --- a/crates/node/src/transactions.rs +++ b/crates/node/src/transactions.rs @@ -7,7 +7,7 @@ use pallas_network::miniprotocols::{ localstate, localtxsubmission::{EraTx, Response}, }; -use tracing::{error, info, warn}; +use tracing::{error, info}; impl NodeClient { /// Submits a transaction to the connected Cardano node. @@ -44,9 +44,15 @@ impl NodeClient { Ok(txid) }, Ok(Response::Rejected(reason)) => { - let haskell_display = as_node_submit_error(reason) - .unwrap_or_else(|e| format!("Failed to format submit error: {e}")); - warn!( + let haskell_display = as_node_submit_error(reason).unwrap_or_else(|e| { + tracing::warn!( + connection_id = self.connection_id, + "TxSubmitFail: failed to parse rejection reason: {e}, CBOR: {}", + hex::encode(&tx) + ); + "transaction rejected with unknown reason".to_string() + }); + info!( connection_id = self.connection_id, "TxSubmitFail: {}, CBOR: {}", haskell_display, diff --git a/crates/node/src/utxo.rs b/crates/node/src/utxo.rs new file mode 100644 index 00000000..ed1216c8 --- /dev/null +++ b/crates/node/src/utxo.rs @@ -0,0 +1,23 @@ +use pallas_network::miniprotocols::localstate::{ + self, + queries_v16::{TxIns, UTxOByTxin}, +}; + +use super::connection::NodeClient; + +use bf_common::errors::BlockfrostError; + +impl NodeClient { + pub async fn get_utxos_for_txins(&mut self, ins: TxIns) -> Result { + self.with_statequery(|generic_client: &mut localstate::GenericClient| { + Box::pin(async { + let era = localstate::queries_v16::get_current_era(generic_client).await?; + + let utxos: UTxOByTxin = + localstate::queries_v16::get_utxo_by_txin(generic_client, era, ins).await?; + Ok(utxos) + }) + }) + .await + } +} diff --git a/crates/platform/Cargo.toml b/crates/platform/Cargo.toml index f800eee4..9ef7f9ad 100644 --- a/crates/platform/Cargo.toml +++ b/crates/platform/Cargo.toml @@ -13,6 +13,7 @@ bf-common.workspace = true bf-node.workspace = true bf-data-node.workspace = true bf-api-provider.workspace = true +bf-tx-evaluator.workspace = true anyhow.workspace = true axum.workspace = true @@ -52,7 +53,6 @@ uuid.workspace = true [dev-dependencies] bf-api-provider.workspace = true pretty_assertions.workspace = true -proptest.workspace = true rstest.workspace = true [target.'cfg(unix)'.dependencies] diff --git a/crates/platform/src/api/tx/submit.rs b/crates/platform/src/api/tx/submit.rs index 7ddab170..d02ece78 100644 --- a/crates/platform/src/api/tx/submit.rs +++ b/crates/platform/src/api/tx/submit.rs @@ -1,6 +1,6 @@ use crate::validation::validate_content_type; use axum::{Extension, Json, http::HeaderMap, response::IntoResponse}; -use bf_common::errors::BlockfrostError; +use bf_common::{errors::BlockfrostError, helpers::binary_or_hex_heuristic}; use bf_node::pool::NodePool; use metrics::counter; @@ -41,42 +41,3 @@ pub async fn route( Ok((response_headers, Json(response_body))) } - -/// This function allows us to take both hex-encoded and raw bytes. It has -/// to be a heuristic: if there are input bytes that are not `[0-9a-f]`, -/// then it must be a binary string. Otherwise, we assume it’s hex encoded. -/// -/// **Note**: there is a small probability that the user gave us a binary -/// string that only _looked_ like a hex-encoded one, but it’s rare enough -/// to ignore it. -pub fn binary_or_hex_heuristic(xs: &[u8]) -> Vec { - let even_length = xs.len().is_multiple_of(2); - - if !even_length || xs.iter().any(|&x| !x.is_ascii_hexdigit()) { - xs.to_vec() - } else { - hex::decode(xs).unwrap_or_else(|_| unreachable!()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use proptest::prelude::*; - - proptest! { - #[test] - fn proptest_binary_or_hex_heuristic( - binary in prop::collection::vec(any::(), 0..=128) - .prop_filter("exclude values made up only of hex digits", |xs| { - xs.iter().any(|&x| !x.is_ascii_hexdigit()) - }) - ) { - let hex_string = hex::encode(&binary); - assert_eq!( - binary_or_hex_heuristic(hex_string.as_bytes()), - binary_or_hex_heuristic(&binary) - ) - } - } -} diff --git a/crates/platform/src/api/utils/txs/evaluate.rs b/crates/platform/src/api/utils/txs/evaluate.rs index b1ee3085..ad1bc5f6 100644 --- a/crates/platform/src/api/utils/txs/evaluate.rs +++ b/crates/platform/src/api/utils/txs/evaluate.rs @@ -1,2 +1,3 @@ +mod model; pub mod root; pub mod utxos; diff --git a/crates/platform/src/api/utils/txs/evaluate/model.rs b/crates/platform/src/api/utils/txs/evaluate/model.rs new file mode 100644 index 00000000..84a40a13 --- /dev/null +++ b/crates/platform/src/api/utils/txs/evaluate/model.rs @@ -0,0 +1,12 @@ +use serde::Deserialize; + +fn default_version() -> u8 { + 5 +} + +#[derive(Deserialize)] +pub struct EvaluateQuery { + // The default version is 5, which represents Ogmios v5. + #[serde(default = "default_version")] + pub version: u8, +} diff --git a/crates/platform/src/api/utils/txs/evaluate/root.rs b/crates/platform/src/api/utils/txs/evaluate/root.rs index ba8d9950..36757769 100644 --- a/crates/platform/src/api/utils/txs/evaluate/root.rs +++ b/crates/platform/src/api/utils/txs/evaluate/root.rs @@ -1,6 +1,30 @@ -use crate::{BlockfrostError, api::ApiResult}; -use serde_json::Value; +use crate::validation::validate_content_type; +use crate::{BlockfrostError, api::utils::txs::evaluate::model::EvaluateQuery}; +use axum::{Extension, Json, extract::Query, response::IntoResponse}; +use bf_node::pool::NodePool; +use bf_tx_evaluator::external::ExternalEvaluator; +use hyper::HeaderMap; -pub async fn route() -> ApiResult { - Err(BlockfrostError::not_found()) +pub async fn route( + Extension(node): Extension, + Extension(evaluator): Extension, + Query(query): Query, + headers: HeaderMap, + body: axum::body::Bytes, +) -> Result { + validate_content_type(&headers, &["application/cbor"])?; + + match query.version { + 6 => Ok(Json( + evaluator + .evaluate_tx_payload_v6(node, body.as_ref(), None) + .await?, + )), + // Everything else is treated as v5 + _ => Ok(Json( + evaluator + .evaluate_tx_payload_v5(node, body.as_ref(), None) + .await?, + )), + } } diff --git a/crates/platform/src/api/utils/txs/evaluate/utxos.rs b/crates/platform/src/api/utils/txs/evaluate/utxos.rs index ba8d9950..a114a014 100644 --- a/crates/platform/src/api/utils/txs/evaluate/utxos.rs +++ b/crates/platform/src/api/utils/txs/evaluate/utxos.rs @@ -1,6 +1,41 @@ -use crate::{BlockfrostError, api::ApiResult}; -use serde_json::Value; +use crate::BlockfrostError; +use axum::{ + Extension, Json, + extract::{self}, + response::IntoResponse, +}; +use bf_node::pool::NodePool; +use bf_tx_evaluator::{external::ExternalEvaluator, model::api::TxEvaluationRequest}; -pub async fn route() -> ApiResult { - Err(BlockfrostError::not_found()) +pub async fn route( + Extension(node): Extension, + Extension(evaluator): Extension, + extract::Json(tx_request): extract::Json, +) -> Result { + // query.version is ignored on purpose + match tx_request { + TxEvaluationRequest::V6(request) => Ok(Json( + evaluator + .evaluate_tx_payload_v6( + node, + request.transaction.cbor.as_bytes(), + request.additional_utxo, + ) + .await?, + )), + TxEvaluationRequest::V5Cbor(request) => Ok(Json( + evaluator + .evaluate_tx_payload_v5(node, request.cbor.as_bytes(), request.additional_utxo_set) + .await?, + )), + TxEvaluationRequest::V5Evaluate(request) => Ok(Json( + evaluator + .evaluate_tx_payload_v5( + node, + request.evaluate.as_bytes(), + request.additional_utxo_set, + ) + .await?, + )), + } } diff --git a/crates/platform/src/main.rs b/crates/platform/src/main.rs index 934ad5e7..47439f39 100644 --- a/crates/platform/src/main.rs +++ b/crates/platform/src/main.rs @@ -35,7 +35,7 @@ async fn main() -> Result<(), AppError> { env!("GIT_REVISION") ); - let (app, _, health_monitor, icebreakers_api, api_prefix) = + let (app, _, health_monitor, icebreakers_api, api_prefix, _chain_config_watch) = build(config.clone().into()).await?; let address = std::net::SocketAddr::new(config.server_address, config.server_port); diff --git a/crates/platform/src/server.rs b/crates/platform/src/server.rs index 637e2e3a..d8389a82 100644 --- a/crates/platform/src/server.rs +++ b/crates/platform/src/server.rs @@ -8,10 +8,11 @@ use crate::{ icebreakers::api::IcebreakersAPI, middlewares::errors::error_middleware, }; -use axum::{Extension, Router, middleware::from_fn}; +use axum::{Extension, Router, extract::DefaultBodyLimit, middleware::from_fn}; use bf_common::errors::{AppError, BlockfrostError}; use bf_data_node::client::DataNode; -use bf_node::pool::NodePool; +use bf_node::{chain_config_watch::ChainConfigWatch, pool::NodePool}; +use bf_tx_evaluator::external::ExternalEvaluator; use metrics::{setup_metrics_recorder, spawn_process_collector}; use routes::{hidden::get_hidden_api_routes, nest_routes, regular::get_regular_api_routes}; use state::{ApiPrefix, AppState}; @@ -31,6 +32,7 @@ pub async fn build( health_monitor::HealthMonitor, Option>, ApiPrefix, + ChainConfigWatch, ), AppError, > { @@ -72,6 +74,12 @@ pub async fn build( // Set up optional Icebreakers API (solitary option in CLI) let icebreakers_api = IcebreakersAPI::new(&config, api_prefix.clone()).await?; + // Spawn the chain config watcher + let chain_config_watch = ChainConfigWatch::spawn(node_conn_pool.clone()); + + // Initialize the Haskell-based tx evaluator + let tx_evaluator = ExternalEvaluator::new(chain_config_watch.clone()); + // API routes that are always under / (and also under the UUID prefix, if we use it) let regular_api_routes = get_regular_api_routes(!config.no_metrics); let hidden_api_routes = get_hidden_api_routes(!config.no_metrics); @@ -94,6 +102,7 @@ pub async fn build( .with_state(app_state.clone()) .layer(Extension(health_monitor.clone())) .layer(Extension(node_conn_pool.clone())) + .layer(Extension(tx_evaluator.clone())) .layer(from_fn(error_middleware)) .fallback(BlockfrostError::not_found()); @@ -107,7 +116,8 @@ pub async fn build( let inner = NormalizePathLayer::trim_trailing_slash().layer(inner); let app = Router::new() .fallback_service(inner) - .layer(ConcurrencyLimitLayer::new(config.server_concurrency_limit)); + .layer(ConcurrencyLimitLayer::new(config.server_concurrency_limit)) + .layer(DefaultBodyLimit::max(10 * 1024 * 1024)); // 10 MB Ok(( app, @@ -115,5 +125,6 @@ pub async fn build( health_monitor, icebreakers_api, api_prefix, + chain_config_watch, )) } diff --git a/crates/platform/src/server/routes/hidden.rs b/crates/platform/src/server/routes/hidden.rs index f0aae598..a0dfe7c0 100644 --- a/crates/platform/src/server/routes/hidden.rs +++ b/crates/platform/src/server/routes/hidden.rs @@ -1,6 +1,7 @@ +use crate::api::utils; use crate::api::{ accounts, addresses, assets, blocks, epochs, governance, health, ledger, metadata, network, - pools, scripts, tx, txs, utils, + pools, scripts, tx, txs, }; use crate::middlewares::metrics::track_http_metrics; use crate::server::state::AppState; @@ -135,9 +136,9 @@ pub fn get_hidden_api_routes(enable_metrics: bool) -> Router { .route("/txs/{hash}/required_signers", get(txs::hash::required_signers::route)) .route("/txs/{hash}/cbor", get(txs::hash::cbor::route)) - // utils - .route("/utils/tx/evaluate", post(utils::txs::evaluate::root::route)) - .route("/utils/tx/evaluate/utxos", post(utils::txs::evaluate::utxos::route)); + // tx evaluate + .route("/utils/txs/evaluate",post(utils::txs::evaluate::root::route)) + .route("/utils/txs/evaluate/utxos",post(utils::txs::evaluate::utxos::route)); if enable_metrics { router = router.route_layer(from_fn(track_http_metrics)); diff --git a/crates/testgen/src/testgen.rs b/crates/testgen/src/testgen.rs index 925766d2..502d63a5 100644 --- a/crates/testgen/src/testgen.rs +++ b/crates/testgen/src/testgen.rs @@ -30,12 +30,14 @@ struct TestgenRequest { #[non_exhaustive] pub enum Variant { DeserializeStream, + EvaluateStream, } impl Variant { fn as_arg(self) -> &'static str { match self { Self::DeserializeStream => "deserialize-stream", + Self::EvaluateStream => "evaluate-stream", } } } diff --git a/crates/tx_evaluator/Cargo.toml b/crates/tx_evaluator/Cargo.toml new file mode 100644 index 00000000..9aec3910 --- /dev/null +++ b/crates/tx_evaluator/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "blockfrost-platform-tx-evaluator" +version.workspace = true +license.workspace = true +edition.workspace = true + +[dependencies] +bf-common.workspace = true +bf-testgen.workspace = true +bf-node.workspace = true +serde.workspace = true +hex.workspace = true +base64.workspace = true +serde_json.workspace = true +pallas-network.workspace = true +pallas-crypto.workspace = true +pallas-traverse.workspace = true +pallas-codec.workspace = true +pallas-primitives.workspace = true +pallas-addresses.workspace = true +pallas-validate.workspace = true +chrono.workspace = true +uuid.workspace = true +tracing.workspace = true +tokio.workspace = true + +[features] +tarpaulin = [] diff --git a/crates/tx_evaluator/README.md b/crates/tx_evaluator/README.md new file mode 100644 index 00000000..96f0a31f --- /dev/null +++ b/crates/tx_evaluator/README.md @@ -0,0 +1,151 @@ +# TX evaluator + +This package contains native and external tx evaluator. + +- Native is based on `pallas-validate`. +- External is using `ledger` codebase via `testgen` Haskell binary. + +`native` produces different evaluation results when compared to `external` at the moment. Knowing that `external` depends on the ledger codebase, we can consider `external` results accurate. + +## Versions + +Both `native` and `external` evaluator produces version 5 & 6 results. These versions are there for compatibility with [Blockfrost](https://docs.blockfrost.io/#tag/cardano--utilities/POST/utils/txs/evaluate). Blockfrost implementation is a proxy to Ogmios, so version 5 & 6 is Ogmios versions. + +### Version 5 Data Structure + +#### V5 Full Input for API + +```javascript +{ + "type": "jsonwsp/request", + "version": "1.0", + "servicename": "ogmios", + "methodname": "EvaluateTx", + "args": { + "evaluate": "string", + "additionalUtxoSet": [ + [ + { + "txId": "stringstringstringstringstringstringstringstringstringstringstri", + "index": 4294967295 + }, + { + "address": "addr_test1qz66ue36465w2qq40005h2hadad6pnjht8mu6sgplsfj74qdjnshguewlx4ww0eet26y2pal4xpav5prcydf28cvxtjqx46x7f", + "value": { + "coins": 2, + "assets": { + "3542acb3a64d80c29302260d62c3b87a742ad14abf855ebc6733081e": 42, + "b5ae663aaea8e500157bdf4baafd6f5ba0ce5759f7cd4101fc132f54.706174617465": 1337 + } + }, + "datumHash": null, + "datum": null, + "script": null + } + ] + ] + }, + "mirror": null +} +``` + +#### V5 Full Output for API + +```javascript +{ + "type": "jsonwsp/response", + "version": "1.0", + "servicename": "ogmios", + "methodname": "EvaluateTx", + "result": { + "EvaluationResult": { + "spend:1": { + "memory": 5236222, + "steps": 1212353 + }, + "mint:0": { + "memory": 5000, + "steps": 42 + } + } + }, + "reflection": { + "id": "3db92aa6-5765-46a8-a9a4-2e8a94ab2a1d" + } +} +``` + +### Version 6 Data Structure + +#### V6 Full Input for API + +```javascript +{ + "jsonrpc": "2.0", + "method": "evaluateTransaction", + "params": { + "transaction": { + "cbor": "string" + }, + "additionalUtxo": [ + { + "transaction": { + "id": "stringstringstringstringstringstringstringstringstringstringstri" + }, + "index": 4294967295, + "address": "addr1q9d34spgg2kdy47n82e7x9pdd6vql6d2engxmpj20jmhuc2047yqd4xnh7u6u5jp4t0q3fkxzckph4tgnzvamlu7k5psuahzcp", + "value": { + "ada": { + "lovelace": 0 + }, + "property1": { + "property1": 0, + "property2": 0 + }, + "property2": { + "property1": 0, + "property2": 0 + } + }, + "datumHash": "c248757d390181c517a5beadc9c3fe64bf821d3e889a963fc717003ec248757d", + "datum": "string", + "script": { + "language": "native", + "json": { + "clause": "signature", + "from": "3c07030e36bfff7cd2f004356ef320f3fe3c07030e7cd2f004356437" + }, + "cbor": "string" + } + } + ] + }, + "id": null +} +``` + +#### V6 Full Output for API + +```javascript +{ + "jsonrpc": "2.0", + "method": "evaluateTransaction", + "result": [ + { + "validator": "spend:1", + "budget": { + "memory": 5236222, + "cpu": 1212353 + } + }, + { + "validator": "mint:0", + "budget": { + "memory": 5000, + "cpu": 42 + } + } + ], + "id": "3db92aa6-5765-46a8-a9a4-2e8a94ab2a1d" +} +``` diff --git a/crates/tx_evaluator/src/external.rs b/crates/tx_evaluator/src/external.rs new file mode 100644 index 00000000..55986984 --- /dev/null +++ b/crates/tx_evaluator/src/external.rs @@ -0,0 +1,425 @@ +use std::collections::HashMap; +use std::str::FromStr; +use std::sync::Arc; + +use bf_node::chain_config_watch::ChainConfigWatch; +use bf_node::pool::NodePool; +use pallas_codec::{ + minicbor::to_vec, + utils::{AnyUInt, CborWrap}, +}; + +use pallas_network::miniprotocols::{ + localstate::queries_v16::{ + DatumOption, PostAlonsoTransactionOutput, TransactionOutput, UTxO, Value as NetworkValue, + }, + localtxsubmission::primitives::ScriptRef, +}; +use pallas_primitives::Bytes; +use pallas_primitives::KeyValuePairs; +use pallas_traverse::MultiEraTx; +use serde::Serialize; +use tokio::sync::Mutex; + +use bf_common::{ + chain_config::{ChainConfigCache, SlotConfig}, + errors::{AppError, BlockfrostError}, +}; +use bf_testgen::testgen::{Testgen, TestgenResponse, Variant}; + +use crate::{ + helper::{generate_reflection_v5, generate_reflection_v6, resolve_tx_body}, + model::api::{ + AdditionalUtxoSet, AdditionalUtxoV6, OgmiosError, OutputReferenceV6, TransactionIdV6, + }, + native::{ + convert_to_datum_option_network, convert_to_network_value, convert_to_network_value_v6, + create_address, extract_inputs, + }, + ogmios5_response::invalid_request_v5, + wrapper::{ + wrap_as_incompatible_era_v5, wrap_as_incompatible_era_v6, wrap_eval_output_v5, + wrap_eval_output_v6, wrap_ogmios_error_v6, + }, +}; + +/// Evaluates transactions using the external testgen-hs Haskell binary +#[derive(Clone)] +pub struct ExternalEvaluator { + /// Source of chain configuration + config_watch: ChainConfigWatch, + /// Shared mutable state holding the current testgen-hs process + state: Arc>, +} + +struct EvaluatorState { + /// Running testgen-hs process, if initialized + testgen: Option, + /// The config that was used to init the current testgen process + last_config: Option>, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct InitPayload { + system_start: String, + protocol_params: String, + slot_config: SlotConfig, + era: u16, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct EvalPayload { + tx: String, + utxos: String, +} + +/// Either a testgen-hs response +/// or a domain-level Ogmios compatible error detected before evaluation +pub enum EvalOutput { + Testgen(TestgenResponse), + Error(OgmiosError), +} + +/// Evaluates the given tx with utxos using the external testgen exe, which is a Haskell binary. +impl ExternalEvaluator { + pub fn new(config_watch: ChainConfigWatch) -> Self { + Self { + config_watch, + state: Arc::new(Mutex::new(EvaluatorState { + testgen: None, + last_config: None, + })), + } + } + + /// Get or init testgen, re-spawning if config changed. Returns 503 if + /// chain config is not yet available + async fn ensure_testgen(&self) -> Result { + let current_config = self.config_watch.get()?; + + let mut state = self.state.lock().await; + + // Config unchanged, testgen already running + if let (Some(last), Some(testgen)) = (&state.last_config, &state.testgen) + && Arc::ptr_eq(last, ¤t_config) + { + return Ok(testgen.clone()); + } + + tracing::info!("ExternalEvaluator: spawning testgen-hs"); + + let testgen = spawn_and_init_testgen(¤t_config).await.map_err(|e| { + tracing::error!("ExternalEvaluator: failed to initialize testgen-hs: {e}"); + BlockfrostError::internal_server_error(format!( + "Failed to initialize ExternalEvaluator: {e}" + )) + })?; + + state.testgen = Some(testgen.clone()); + state.last_config = Some(current_config); + tracing::info!("ExternalEvaluator: testgen-hs initialized successfully"); + Ok(testgen) + } + + pub async fn evaluate_binary_tx( + &self, + node_pool: NodePool, + tx_cbor_binary: &[u8], + additional_utxos: Vec<(UTxO, TransactionOutput)>, + ) -> Result { + let testgen = self.ensure_testgen().await?; + let node = node_pool.get(); + + let multi_era_tx = match MultiEraTx::decode(tx_cbor_binary) { + Ok(tx) => tx, + Err(err) => { + let msg = format!( + "Invalid request: Deserialisation failure while decoding serialised transaction. CBOR failed with error: {}", + err + ); + return Ok(EvalOutput::Testgen(TestgenResponse::Err( + serde_json::Value::String(msg), + ))); + }, + }; + + // No redeemers = no scripts to evaluate. Return empty result + if multi_era_tx.redeemers().is_empty() { + return Ok(EvalOutput::Testgen(TestgenResponse::Ok(serde_json::json!( + [] + )))); + } + + let txins = extract_inputs(&multi_era_tx)?; + + let utxos_from_node = node.await?.get_utxos_for_txins(txins).await?; + + // Merge UTxOs from node (on-chain) and user-provided additional UTxOs. + // + // Conflict detection (follows Ogmios's OverlappingAdditionalUtxo approach): + // A TxIn deterministically identifies a unique TxOut on-chain. If a user + // provides a TxOut for a TxIn that already exists on-chain with a *different* + // value, the user's data is wrong — we reject it with a 400 error. User always loses + // against the node and mempool data(if we had it). + // See: https://github.com/CardanoSolutions/ogmios/blob/bdb1bad58506e9ac470796c5e1406cde49aebc1a/server/src/Ogmios/App/Protocol/TxSubmission.hs#L628-L649 + // + // Deduplication within additional_utxos: if the user sends the same TxIn twice, + // the last entry wins — matching Haskell's Data.Map.fromList semantics. + // + // Mempool gap: we do not have mempool support as agreed before (pallas has a txmonitor + // miniprotocol but it is not wired up in our node connection layer). Ogmios + // merges mempool UTxOs (via LocalTxMonitor) with user additional UTxOs before + // validating against network UTxOs. We only have network + user-provided. + type TxInKey = (pallas_crypto::hash::Hash<32>, u64); + let txin_key = |utxo: &UTxO| -> TxInKey { (utxo.transaction_id, u64::from(&utxo.index)) }; + + let node_utxo_map: HashMap = utxos_from_node + .to_vec() + .into_iter() + .map(|(txin, txout)| (txin_key(&txin), (txin, txout))) + .collect(); + + // Check for conflicts against on-chain UTxOs and deduplicate additional_utxos. + let mut overlapping: Vec = Vec::new(); + let mut additional_deduped: HashMap = HashMap::new(); + for (txin, txout) in additional_utxos { + let key = txin_key(&txin); + if let Some((_, node_txout)) = node_utxo_map.get(&key) { + if node_txout != &txout { + overlapping.push(OutputReferenceV6 { + transaction: TransactionIdV6 { + id: key.0.to_string(), + }, + index: key.1, + }); + } + // Same TxIn with identical TxOut: node already has it, skip. + } else { + // Not on-chain. Last entry wins for duplicate TxIns within additional_utxos. + additional_deduped.insert(key, (txin, txout)); + } + } + + if !overlapping.is_empty() { + return Ok(EvalOutput::Error(OgmiosError::overlapping_utxo( + overlapping, + ))); + } + + let utxos = KeyValuePairs::from_iter( + node_utxo_map + .into_values() + .chain(additional_deduped.into_values()), + ); + + let utxos_cbor = hex::encode(to_vec(&utxos).map_err(|err| { + BlockfrostError::internal_server_error(format!( + "ExternalEvaluator: Failed to serialize UTxOs: {err}" + )) + })?); + + // TODO(testgen-hs): CBOR with empty guarded fields (e.g. empty required_signers + // key 14 = 0x80) is rejected by testgen-hs's Conway decoder (fieldGuarded). + // Ogmios avoids this via Babbage fallback. Adding Babbage decode to testgen-hs + // would fix this. Until then, affected test fixtures should expect a fault response. + let payload = EvalPayload { + tx: hex::encode(tx_cbor_binary), + utxos: utxos_cbor, + }; + + let json = serde_json::to_string(&payload).map_err(|err| { + BlockfrostError::internal_server_error(format!( + "ExternalEvaluator: Failed to serialize payload: {err}" + )) + })?; + + let response = testgen.send(json).await.map_err(|err| { + BlockfrostError::internal_server_error(format!( + "ExternalEvaluator: Failed to send payload: {err}" + )) + })?; + Ok(EvalOutput::Testgen(response)) + } + + fn build_transaction_output( + txin: UTxO, + address: Bytes, + script_ref: Option>, + amount: NetworkValue, + inline_datum: Option, + ) -> (UTxO, TransactionOutput) { + let txout = TransactionOutput::Current(PostAlonsoTransactionOutput { + address, + script_ref, + amount, + inline_datum, + }); + (txin, txout) + } + + /// Decodes raw payload (hex/base64/binary) then evaluates. Used by application/cbor endpoint. + pub async fn evaluate_tx_payload_v5( + &self, + node_pool: NodePool, + tx_body: &[u8], + additional_utxos: Option, + ) -> Result { + let tx_cbor_binary = match resolve_tx_body(tx_body) { + Ok(decoded) => decoded, + Err(_) => return Ok(invalid_request_v5(&generate_reflection_v5())), + }; + self.evaluate_binary_tx_v5(node_pool, &tx_cbor_binary, additional_utxos) + .await + } + + /// Decodes raw payload (hex/base64/binary) then evaluates. Used by application/cbor endpoint. + pub async fn evaluate_tx_payload_v6( + &self, + node_pool: NodePool, + tx_body: &[u8], + additional_utxos: Option>, + ) -> Result { + let tx_cbor_binary = match resolve_tx_body(tx_body) { + Ok(decoded) => decoded, + Err(msg) => { + let oe = OgmiosError::invalid_request(msg); + return Ok(wrap_ogmios_error_v6(&oe, &generate_reflection_v6())); + }, + }; + self.evaluate_binary_tx_v6(node_pool, &tx_cbor_binary, additional_utxos) + .await + } + + /// Evaluates already-decoded CBOR binary. Used by JSON endpoints (utxos.rs). + pub async fn evaluate_binary_tx_v5( + &self, + node_pool: NodePool, + tx_cbor_binary: &[u8], + additional_utxos: Option, + ) -> Result { + // Pre-Alonzo tx (3-element CBOR array) cannot be evaluated. + if tx_cbor_binary.first() == Some(&0x83) { + return Ok(wrap_as_incompatible_era_v5("Mary".to_string())); + } + + let user_utxos = additional_utxos + .unwrap_or_default() + .iter() + .map( + |(utxo, tout)| -> Result<(UTxO, TransactionOutput), BlockfrostError> { + let txin = UTxO { + transaction_id: pallas_crypto::hash::Hash::<32>::from_str(&utxo.tx_id) + .map_err(|e| { + BlockfrostError::custom_400(format!( + "invalid tx_id '{}': {e}", + utxo.tx_id + )) + })?, + index: AnyUInt::U64(utxo.index), + }; + Ok(Self::build_transaction_output( + txin, + create_address(&tout.address)?, + tout.script + .as_ref() + .map(|s| ScriptRef::try_from(s.clone()).map(CborWrap)) + .transpose()?, + convert_to_network_value(&tout.value)?, + convert_to_datum_option_network(&tout.datum, &tout.datum_hash)?, + )) + }, + ) + .collect::, _>>()?; + + Ok(wrap_eval_output_v5( + self.evaluate_binary_tx(node_pool, tx_cbor_binary, user_utxos) + .await?, + )) + } + + /// Evaluates already-decoded CBOR binary. Used by JSON endpoints (utxos.rs). + pub async fn evaluate_binary_tx_v6( + &self, + node_pool: NodePool, + tx_cbor_binary: &[u8], + additional_utxos: Option>, + ) -> Result { + // Pre-Alonzo tx (3-element CBOR array) cannot be evaluated. + if tx_cbor_binary.first() == Some(&0x83) { + return Ok(wrap_as_incompatible_era_v6("mary".to_string())); + } + + let user_utxos = additional_utxos + .unwrap_or_default() + .iter() + .map( + |utxo| -> Result<(UTxO, TransactionOutput), BlockfrostError> { + let txin = UTxO { + transaction_id: pallas_crypto::hash::Hash::<32>::from_str( + utxo.transaction_id(), + ) + .map_err(|e| { + BlockfrostError::custom_400(format!( + "invalid transaction id '{}': {e}", + utxo.transaction_id() + )) + })?, + index: AnyUInt::U64(utxo.output_index()), + }; + Ok(Self::build_transaction_output( + txin, + create_address(&utxo.address)?, + utxo.script + .as_ref() + .map(|s| ScriptRef::try_from(s).map(CborWrap)) + .transpose()?, + convert_to_network_value_v6(&utxo.value)?, + convert_to_datum_option_network(&utxo.datum, &utxo.datum_hash)?, + )) + }, + ) + .collect::, _>>()?; + + Ok(wrap_eval_output_v6( + self.evaluate_binary_tx(node_pool, tx_cbor_binary, user_utxos) + .await?, + )) + } +} + +/// Spawn a fresh testgen-hs process and send the init payload +/// TODO(testgen-hs): Currently testgen only accepts init message as the first message. +/// Support receiving init message anytime so we won't need to kill&spawn +async fn spawn_and_init_testgen(config: &ChainConfigCache) -> Result { + let system_start = to_vec(&config.genesis_config.system_start).map_err(|err| { + AppError::Server(format!( + "ExternalEvaluator: failed to serialize genesis config: {err}" + )) + })?; + + let protocol_params = to_vec(&config.protocol_params).map_err(|err| { + AppError::Server(format!( + "ExternalEvaluator: failed to serialize protocol params: {err}" + )) + })?; + + let init_payload = InitPayload { + system_start: hex::encode(system_start), + protocol_params: hex::encode(protocol_params), + slot_config: config.slot_config.clone(), + era: config.era, + }; + + let payload = serde_json::to_string(&init_payload).map_err(|err| { + AppError::Server(format!( + "ExternalEvaluator: failed to serialize initial payload: {err}" + )) + })?; + + let testgen = Testgen::spawn_with_init(Variant::EvaluateStream, payload) + .map_err(|err| AppError::Server(format!("Failed to spawn ExternalEvaluator: {err}")))?; + + Ok(testgen) +} diff --git a/crates/tx_evaluator/src/helper.rs b/crates/tx_evaluator/src/helper.rs new file mode 100644 index 00000000..13cf5a07 --- /dev/null +++ b/crates/tx_evaluator/src/helper.rs @@ -0,0 +1,93 @@ +use base64::{Engine as _, engine::general_purpose}; +use serde_json::json; + +pub fn generate_reflection_v5() -> serde_json::Value { + json!({"id": uuid::Uuid::new_v4().to_string()}) +} +pub fn generate_reflection_v6() -> serde_json::Value { + serde_json::Value::String(uuid::Uuid::new_v4().to_string()) +} + +/// Decodes a CBOR payload from an `application/cbor` endpoint body. +/// Matches Blockfrost API behavior: +/// - Non-ASCII bytes (or empty) → treat as raw binary CBOR +/// - ASCII text → try hex decode, then base64 decode +/// - If neither works → error +pub fn resolve_tx_body(body: &[u8]) -> Result, String> { + let is_text = !body.is_empty() && body.iter().all(|b| b.is_ascii()); + if !is_text { + return Ok(body.to_vec()); + } + + // 1. Hex decode (even length, all hex chars) + let even_length = body.len().is_multiple_of(2); + let all_hex = body.iter().all(|b| b.is_ascii_hexdigit()); + if even_length + && all_hex + && let Ok(decoded) = hex::decode(body) + { + return Ok(decoded); + } + + // 2. Base64 decode + if let Ok(decoded) = general_purpose::STANDARD.decode(body) + && !decoded.is_empty() + { + return Ok(decoded); + } + + Err("Invalid request: failed to decode payload from base64 or base16.".to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + + // Prod test cases verified via curl against cardano-preview.blockfrost.io + + #[test] + fn raw_binary_cbor() { + let cbor = hex::decode("84A300818258204E9A66B7").unwrap(); + assert_eq!(resolve_tx_body(&cbor).unwrap(), cbor); + } + + #[test] + fn hex_text_even_length() { + let hex = b"84A300818258204E9A66B7"; + let expected = hex::decode("84A300818258204E9A66B7").unwrap(); + assert_eq!(resolve_tx_body(hex).unwrap(), expected); + } + + #[test] + fn hex_text_odd_length_fails() { + let odd_hex = b"84a300818258204acdf8c67"; + assert!(resolve_tx_body(odd_hex).is_err()); + } + + #[test] + fn base64_text() { + let cbor = hex::decode("84A300818258204E9A66B7").unwrap(); + let b64 = general_purpose::STANDARD.encode(&cbor); + assert_eq!(resolve_tx_body(b64.as_bytes()).unwrap(), cbor); + } + + #[test] + fn garbage_text_fails() { + assert!(resolve_tx_body(b"hello world").is_err()); + } + + #[test] + fn empty_body_returns_empty() { + assert_eq!(resolve_tx_body(b"").unwrap(), Vec::::new()); + } + + #[test] + fn raw_0x80_binary() { + assert_eq!(resolve_tx_body(&[0x80]).unwrap(), vec![0x80]); + } + + #[test] + fn hex_text_80_decodes() { + assert_eq!(resolve_tx_body(b"80").unwrap(), vec![0x80]); + } +} diff --git a/crates/tx_evaluator/src/lib.rs b/crates/tx_evaluator/src/lib.rs new file mode 100644 index 00000000..7c3006bd --- /dev/null +++ b/crates/tx_evaluator/src/lib.rs @@ -0,0 +1,6 @@ +pub mod external; +mod helper; +pub mod model; +pub mod native; +pub mod ogmios5_response; +pub mod wrapper; diff --git a/crates/tx_evaluator/src/model/api.rs b/crates/tx_evaluator/src/model/api.rs new file mode 100644 index 00000000..afca3d5d --- /dev/null +++ b/crates/tx_evaluator/src/model/api.rs @@ -0,0 +1,1169 @@ +use core::fmt; +use std::collections::HashMap; +use std::str::FromStr; + +use bf_common::errors::BlockfrostError; +use pallas_primitives::{Bytes, ExUnits, KeepRaw, conway::RedeemerTag}; +use pallas_validate::phase2::EvalReport; +use pallas_validate::phase2::tx::TxEvalResult; +use serde::de; +use serde::{Deserialize, Serialize, ser::SerializeMap}; + +fn decode_script_hex(s: &str) -> Result { + hex::decode(s) + .map(Bytes::from) + .map_err(|e| BlockfrostError::custom_400(format!("invalid hex-encoded script CBOR: {e}"))) +} + +fn decode_pubkey_hash(st: &str) -> Result<[u8; 28], BlockfrostError> { + let mut bytes = [0u8; 28]; + hex::decode_to_slice(st, &mut bytes).map_err(|e| { + BlockfrostError::custom_400(format!("invalid hex-encoded pubkey hash: {e}")) + })?; + Ok(bytes) +} + +// JSON request +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum TxEvaluationRequest { + V6(TxEvaluationRequestV6), + V5Cbor(TxEvaluationRequestV5Cbor), + V5Evaluate(TxEvaluationRequestV5Evaluate), +} + +#[derive(Debug, Deserialize)] +pub struct TxEvaluationRequestV5Cbor { + pub cbor: String, // @todo can be base16 or base64 CBOR + #[serde(rename = "additionalUtxoSet")] + pub additional_utxo_set: Option, + pub mirror: Option, +} + +#[derive(Debug, Deserialize)] +pub struct TxEvaluationRequestV5Evaluate { + pub evaluate: String, + #[serde(rename = "additionalUtxoSet")] + pub additional_utxo_set: Option, + pub mirror: Option, +} +// JSON request + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TxEvaluationRequestV6 { + pub transaction: TransactionCborV6, + pub additional_utxo: Option>, + #[serde(default)] + pub mirror: Option, +} +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AdditionalUtxoV6 { + #[serde(flatten)] + pub output_reference: OutputReferenceV6, + pub address: String, + pub value: ValueV6, + #[serde(rename = "datumHash")] + pub datum_hash: Option, + pub datum: Option, + pub script: Option, +} + +impl AdditionalUtxoV6 { + pub fn transaction_id(&self) -> &str { + &self.output_reference.transaction.id + } + + pub fn output_index(&self) -> u64 { + self.output_reference.index + } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ValueV6 { + pub ada: LovelaceV6, + #[serde(flatten)] + pub assets: HashMap>, +} + +/// If language is native, the json structure can be one of cbor or json. +/// If language is plutus, only cbor field is expected. +/// +/// ```json +/// { +/// "script": { +/// "language": "native", +/// "json": { +/// "clause": "signature", +/// "from": "3c07030e36bfff7cd2f004356ef320f3fe3c07030e7cd2f004356437" +/// }, +/// "cbor": "string" +/// } +/// } +/// ``` +#[derive(Debug, Deserialize)] +#[serde(tag = "language", rename_all = "camelCase")] +pub enum ScriptV6 { + #[serde(rename = "plutus:v1")] + PlutusV1 { cbor: String }, + #[serde(rename = "plutus:v2")] + PlutusV2 { cbor: String }, + #[serde(rename = "plutus:v3")] + PlutusV3 { cbor: String }, + Native { + json: Option, + cbor: Option, + }, +} + +#[derive(Deserialize, Debug, Clone)] +#[serde(tag = "clause", rename_all = "camelCase")] +pub enum ScriptNativeV6 { + Signature { + from: String, + }, + Any { + from: Vec, + }, + All { + from: Vec, + }, + Some { + at_least: u32, + from: Vec, + }, + Before { + slot: u64, + }, + After { + slot: u64, + }, +} + +#[derive(Deserialize, Debug)] +pub struct TxIn { + #[serde(rename = "txId")] + pub tx_id: String, + pub index: u64, +} + +#[derive(Debug, Deserialize)] +pub struct TransactionCborV6 { + pub cbor: String, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct TransactionIdV6 { + pub id: String, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct OutputReferenceV6 { + pub transaction: TransactionIdV6, + pub index: u64, +} + +#[derive(Debug, Deserialize)] +pub struct LovelaceV6 { + pub lovelace: u64, +} + +#[derive(Deserialize, Debug)] +pub struct TxOut { + pub address: String, + pub value: Value, + #[serde(rename = "datumHash")] + pub datum_hash: Option, + pub datum: Option, + pub script: Option