diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce9903b5..eb4cc791 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -154,7 +154,35 @@ jobs: env: RISC0_DEV_MODE: "1" RUST_LOG: "info" - run: cargo nextest run -p integration_tests -- --skip tps_test + run: cargo nextest run -p integration_tests -- --skip tps_test --skip indexer + + integration-tests-indexer: + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v5 + with: + ref: ${{ github.head_ref }} + + - uses: ./.github/actions/install-system-deps + + - uses: ./.github/actions/install-risc0 + + - uses: ./.github/actions/install-logos-blockchain-circuits + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install active toolchain + run: rustup install + + - name: Install nextest + run: cargo install --locked cargo-nextest + + - name: Run tests + env: + RISC0_DEV_MODE: "1" + RUST_LOG: "info" + run: cargo nextest run -p integration_tests indexer -- --skip tps_test valid-proof-test: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 62bf5f9c..914a4dd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,7 @@ dependencies = [ "actix-macros", "actix-rt", "actix_derive", - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "crossbeam-channel", "futures-core", @@ -33,7 +33,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", @@ -61,19 +61,19 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.11.2" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7926860314cbe2fb5d1f13731e387ab43bd32bca224e82e6e2db85de0a3dba49" +checksum = "f860ee6746d0c5b682147b2f7f8ef036d4f92fe518251a3a35ffa3650eafdf0e" dependencies = [ "actix-codec", "actix-rt", "actix-service", "actix-utils", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "bytestring", - "derive_more 2.1.0", + "derive_more 2.1.1", "encoding_rs", "foldhash", "futures-core", @@ -102,14 +102,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[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", @@ -215,7 +215,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -226,7 +226,7 @@ checksum = "b6ac1e58cded18cb28ddc17143c4dea5345b3ad575e14f32f66e4054a56eb271" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -312,7 +312,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "once_cell", "version_check", ] @@ -429,16 +429,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1384d3fe1eecb464229fcf6eebb72306591c56bf27b373561489458a7c73027d" dependencies = [ "futures", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "wasm-bindgen-futures", ] [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" dependencies = [ "backtrace", ] @@ -454,9 +454,9 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ded5f9a03ac8f24d1b8a25101ee812cd32cdc8c50a4c50237de2c4915850e73" +checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5" dependencies = [ "rustversion", ] @@ -541,7 +541,7 @@ checksum = "e7e89fe77d1f0f4fe5b96dfc940923d88d17b6a773808124f21e764dfb063c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -639,7 +639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -665,7 +665,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -811,7 +811,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -892,6 +892,40 @@ dependencies = [ "xattr", ] +[[package]] +name = "astro-float" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96034cc871c05bb65ad7fb77e6a8bebf45d8b055ed0311769e2f83a1d373c1ec" +dependencies = [ + "astro-float-macro", + "astro-float-num", +] + +[[package]] +name = "astro-float-macro" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05cfe0f6df5a74fb25b9e713470ad77e091f35f798730673c8772f26ed438963" +dependencies = [ + "astro-float-num", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "astro-float-num" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86887daca11d02e0b04f37a9cb81888aae881397fb48ff66494e356aea97554a" +dependencies = [ + "itertools 0.10.5", + "lazy_static", + "rand 0.8.5", + "serde", +] + [[package]] name = "async-lock" version = "3.4.2" @@ -928,7 +962,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -939,7 +973,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -968,7 +1002,7 @@ dependencies = [ "manyhow", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -984,7 +1018,7 @@ dependencies = [ "proc-macro2", "quote", "quote-use", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -1021,10 +1055,9 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -1058,7 +1091,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-tungstenite", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -1082,7 +1115,6 @@ dependencies = [ "sync_wrapper", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -1167,9 +1199,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.1" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bedrock_client" @@ -1178,8 +1210,10 @@ dependencies = [ "anyhow", "common", "futures", + "humantime-serde", "log", "logos-blockchain-chain-broadcast-service", + "logos-blockchain-chain-service", "logos-blockchain-common-http-client", "logos-blockchain-core", "reqwest", @@ -1202,7 +1236,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -1211,7 +1245,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -1255,9 +1289,9 @@ 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 = "bitvec" @@ -1303,7 +1337,7 @@ checksum = "227aa051deec8d16bd9c34605e7aaf153f240e35483dd42f6f78903847934738" dependencies = [ "async-stream", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.11.0", "bollard-buildkit-proto", "bollard-stubs", "bytes", @@ -1329,7 +1363,7 @@ dependencies = [ "serde_derive", "serde_json", "serde_urlencoded", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", "tokio", "tokio-stream", @@ -1379,7 +1413,7 @@ dependencies = [ "maybe-async", "reqwest", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -1402,7 +1436,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -1416,15 +1450,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5c6f81257d10a0f602a294ae4182251151ff97dbb504ef9afcdda4a64b24d9b4" [[package]] name = "bytemuck" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" dependencies = [ "bytemuck_derive", ] @@ -1437,7 +1471,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -1455,6 +1489,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bytesize" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" +dependencies = [ + "serde_core", +] + [[package]] name = "bytestring" version = "1.5.0" @@ -1503,7 +1546,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -1514,22 +1557,22 @@ checksum = "befbfd072a8e81c02f8c507aefce431fe5e7d051f83d48a23ffc9b9fe5a11799" dependencies = [ "clap", "heck", - "indexmap 2.12.1", + "indexmap 2.13.0", "log", "proc-macro2", "quote", "serde", "serde_json", - "syn 2.0.111", + "syn 2.0.116", "tempfile", - "toml 0.9.11+spec-1.1.0", + "toml 0.9.12+spec-1.1.0", ] [[package]] name = "cc" -version = "1.2.49" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "jobserver", @@ -1572,7 +1615,7 @@ checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -1588,9 +1631,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ "iana-time-zone", "js-sys", @@ -1623,9 +1666,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.53" +version = "4.5.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "c5caf74d17c3aec5495110c34cc3f78644bfa89af6c8993ed4de2790e49b6499" dependencies = [ "clap_builder", "clap_derive", @@ -1633,9 +1676,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.5.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "370daa45065b80218950227371916a1633217ae42b2715b2287b606dcd618e24" dependencies = [ "anstream", "anstyle", @@ -1645,21 +1688,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] name = "clap_lex" -version = "0.7.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "cobs" @@ -1667,7 +1710,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -1678,7 +1721,7 @@ checksum = "a9dbbdc4b4d349732bc6690de10a9de952bd39ba6a065c586e26600b6b0b91f5" dependencies = [ "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -1710,6 +1753,7 @@ dependencies = [ "anyhow", "base64 0.22.1", "borsh", + "bytesize", "hex", "log", "logos-blockchain-common-http-client", @@ -1720,7 +1764,7 @@ dependencies = [ "serde_json", "serde_with", "sha2", - "thiserror 2.0.17", + "thiserror 2.0.18", "url", ] @@ -1742,7 +1786,7 @@ dependencies = [ "convert_case 0.6.0", "pathdiff", "serde_core", - "toml 0.9.11+spec-1.1.0", + "toml 0.9.12+spec-1.1.0", "winnow", ] @@ -1805,9 +1849,9 @@ checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" [[package]] name = "const-str" -version = "0.7.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0664d2867b4a32697dfe655557f5c3b187e9b605b38612a748e5ec99811d160" +checksum = "18f12cc9948ed9604230cdddc7c86e270f9401ccbe3c2e98a4378c5e7632212f" [[package]] name = "const_format" @@ -1868,6 +1912,24 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case_extras" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589c70f0faf8aa9d17787557d5eae854d7755cac50f5c3d12c81d3d57661cebb" +dependencies = [ + "convert_case 0.11.0", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -2044,7 +2106,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2078,7 +2140,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2092,7 +2154,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2103,7 +2165,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2114,20 +2176,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.111", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", + "syn 2.0.116", ] [[package]] @@ -2146,15 +2195,15 @@ 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 = "data-encoding-macro" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" +checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -2162,12 +2211,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" +checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" dependencies = [ "data-encoding", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2183,9 +2232,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4" dependencies = [ "powerfmt", "serde_core", @@ -2210,7 +2259,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2221,7 +2270,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2242,7 +2291,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2252,7 +2301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2265,29 +2314,29 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] name = "derive_more" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case 0.10.0", "proc-macro2", "quote", "rustc_version", - "syn 2.0.111", + "syn 2.0.116", "unicode-xid", ] @@ -2341,7 +2390,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2351,7 +2400,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edb75a85449fd9c34d9fb3376c6208ec4115d2ca43b965175a52d71349ecab8" dependencies = [ "derive_builder", - "indexmap 2.12.1", + "indexmap 2.13.0", "serde", "serde_yaml", ] @@ -2465,7 +2514,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2555,7 +2604,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2575,7 +2624,7 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2768,9 +2817,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "flate2" @@ -2821,7 +2870,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -2845,16 +2894,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "forwarded-header-value" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" -dependencies = [ - "nonempty", - "thiserror 1.0.69", -] - [[package]] name = "funty" version = "2.0.0" @@ -2863,9 +2902,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[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", @@ -2878,9 +2917,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", @@ -2888,50 +2927,49 @@ 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", "futures-util", - "num_cpus", ] [[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.111", + "syn 2.0.116", ] [[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" @@ -2945,9 +2983,9 @@ dependencies = [ [[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", @@ -2957,7 +2995,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -2967,7 +3004,7 @@ version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bf845b08f7c2ef3b5ad19f80779d43ae20d278652b91bb80adda65baf2d8ed6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "log", "managed", @@ -3009,9 +3046,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -3034,6 +3071,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasip3", + "wasm-bindgen", +] + [[package]] name = "ghash" version = "0.5.1" @@ -3051,7 +3103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "fallible-iterator", - "indexmap 2.12.1", + "indexmap 2.13.0", "stable_deref_trait", ] @@ -3113,26 +3165,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "governor" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" -dependencies = [ - "cfg-if", - "dashmap 5.5.3", - "futures", - "futures-timer", - "no-std-compat", - "nonzero_ext", - "parking_lot", - "portable-atomic", - "quanta", - "rand 0.8.5", - "smallvec", - "spinning_top", -] - [[package]] name = "group" version = "0.13.0" @@ -3162,7 +3194,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.12.1", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -3181,7 +3213,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.4.0", - "indexmap 2.12.1", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -3319,9 +3351,9 @@ dependencies = [ [[package]] name = "hmac-sha512" -version = "1.1.7" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89e8d20b3799fa526152a5301a771eaaad80857f83e01b23216ceaafb2d9280" +checksum = "019ece39bbefc17f13f677a690328cb978dbf6790e141a3c24e66372cb38588b" [[package]] name = "home" @@ -3409,6 +3441,16 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hydration_context" version = "0.3.0" @@ -3512,14 +3554,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", "http 1.4.0", "http-body", @@ -3528,7 +3569,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.6.2", "system-configuration", "tokio", "tower-service", @@ -3553,9 +3594,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -3656,6 +3697,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -3699,10 +3746,14 @@ dependencies = [ "borsh", "common", "futures", + "humantime-serde", "log", "logos-blockchain-core", + "nssa", + "nssa_core", "serde", "serde_json", + "storage", "tokio", "url", ] @@ -3738,7 +3789,7 @@ dependencies = [ "hex", "nssa", "nssa_core", - "schemars 1.2.0", + "schemars 1.2.1", "serde", "serde_with", ] @@ -3749,7 +3800,7 @@ version = "0.1.0" dependencies = [ "indexer_service_protocol", "jsonrpsee", - "schemars 1.2.0", + "schemars 1.2.1", "serde_json", ] @@ -3766,9 +3817,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -3778,9 +3829,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.18.3" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88" +checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" dependencies = [ "console", "portable-atomic", @@ -3806,11 +3857,13 @@ dependencies = [ "anyhow", "base64 0.22.1", "borsh", + "bytesize", "common", "env_logger", "futures", "hex", "indexer_service", + "indexer_service_rpc", "key_protocol", "log", "nssa", @@ -3850,9 +3903,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -3913,9 +3966,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jf-crhf" @@ -3976,9 +4029,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -4019,7 +4072,7 @@ dependencies = [ "rustls-pki-types", "rustls-platform-verifier", "soketto", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tokio-rustls", "tokio-util", @@ -4047,10 +4100,10 @@ dependencies = [ "rustc-hash", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "wasm-bindgen-futures", ] @@ -4072,9 +4125,9 @@ dependencies = [ "rustls-platform-verifier", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", - "tower 0.5.2", + "tower", "url", ] @@ -4088,7 +4141,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -4110,11 +4163,11 @@ dependencies = [ "serde", "serde_json", "soketto", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tokio-stream", "tokio-util", - "tower 0.5.2", + "tower", "tracing", ] @@ -4127,7 +4180,7 @@ dependencies = [ "http 1.4.0", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -4139,7 +4192,7 @@ dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", - "tower 0.5.2", + "tower", ] [[package]] @@ -4152,7 +4205,7 @@ dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", - "tower 0.5.2", + "tower", "url", ] @@ -4199,7 +4252,7 @@ dependencies = [ "secp256k1", "serde", "sha2", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -4210,9 +4263,9 @@ checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" [[package]] name = "lazy-regex" -version = "3.4.2" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "191898e17ddee19e60bccb3945aa02339e81edd4a8c50e21fd4d48cdecda7b29" +checksum = "6bae91019476d3ec7147de9aa291cadb6d870abf2f3015d2da73a90325ac1496" dependencies = [ "lazy-regex-proc_macros", "once_cell", @@ -4221,14 +4274,14 @@ dependencies = [ [[package]] name = "lazy-regex-proc_macros" -version = "3.4.2" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35dc8b0da83d1a9507e12122c80dea71a9c7c613014347392483a83ea593e04" +checksum = "4de9c1e1439d8b7b3061b2d209809f447ca33241733d9a3c01eabf2dc8d94358" dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -4241,17 +4294,23 @@ dependencies = [ ] [[package]] -name = "leptos" -version = "0.8.15" +name = "leb128fmt" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9569fc37575a5d64c0512145af7630bf651007237ef67a8a77328199d315bb" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "leptos" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43ad8042559850ee5253293b8460a75513f7d542021b9303083d5c236bcdd16f" dependencies = [ "any_spawner", "base64 0.22.1", "cfg-if", "either_of", "futures", - "getrandom 0.3.4", + "getrandom 0.4.1", "hydration_context", "leptos_config", "leptos_dom", @@ -4272,10 +4331,10 @@ dependencies = [ "server_fn", "slotmap", "tachys", - "thiserror 2.0.17", + "thiserror 2.0.18", "throw_error", - "typed-builder 0.23.2", - "typed-builder-macro 0.23.2", + "typed-builder", + "typed-builder-macro", "wasm-bindgen", "wasm-bindgen-futures", "wasm_split_helpers", @@ -4284,13 +4343,12 @@ dependencies = [ [[package]] name = "leptos_axum" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0caa95760f87f3067e05025140becefdbdfd36cbc2adac4519f06e1f1edf4af" +checksum = "196de3f5cde6a4c4cd254bb16dc6abd2efbf46cc3ae1b6c7da0731f77b4bdf61" dependencies = [ "any_spawner", "axum 0.8.8", - "dashmap 6.1.0", "futures", "hydration_context", "leptos", @@ -4298,32 +4356,32 @@ dependencies = [ "leptos_macro", "leptos_meta", "leptos_router", - "parking_lot", + "or_poisoned", "server_fn", "tachys", "tokio", - "tower 0.5.2", + "tower", "tower-http", ] [[package]] name = "leptos_config" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071fc40aeb9fcab885965bad1887990477253ad51f926cd19068f45a44c59e89" +checksum = "19a2ac32008dda0d657f2147cc33336f4e743e091597db10f7a99d668e92a46d" dependencies = [ "config", "regex", "serde", - "thiserror 2.0.17", - "typed-builder 0.21.2", + "thiserror 2.0.18", + "typed-builder", ] [[package]] name = "leptos_dom" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f4330c88694c5575e0bfe4eecf81b045d14e76a4f8b00d5fd2a63f8779f895" +checksum = "35742e9ed8f8aaf9e549b454c68a7ac0992536e06856365639b111f72ab07884" dependencies = [ "js-sys", "or_poisoned", @@ -4336,27 +4394,27 @@ dependencies = [ [[package]] name = "leptos_hot_reload" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d61ec3e1ff8aaee8c5151688550c0363f85bc37845450764c31ff7584a33f38" +checksum = "9d2a0f220c8a5ef3c51199dfb9cdd702bc0eb80d52fbe70c7890adfaaae8a4b1" dependencies = [ "anyhow", "camino", - "indexmap 2.12.1", - "parking_lot", + "indexmap 2.13.0", + "or_poisoned", "proc-macro2", "quote", "rstml", "serde", - "syn 2.0.111", + "syn 2.0.116", "walkdir", ] [[package]] name = "leptos_integration_utils" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13cccc9305df53757bae61bf15641bfa6a667b5f78456ace4879dfe0591ae0e8" +checksum = "c097f89cd9aa606297672f56fa5bdda09f01609a9f4eefaccdbb5ab5afea4279" dependencies = [ "futures", "hydration_context", @@ -4369,13 +4427,14 @@ dependencies = [ [[package]] name = "leptos_macro" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86ffd2e9cf3e264e9b3e16bdb086cefa26bd0fa7bc6a26b0cc5f6c1fd3178ed" +checksum = "712325a77f1d050bf2897061ccaf2b075930aab36954980d658f04452686c474" dependencies = [ "attribute-derive", "cfg-if", - "convert_case 0.10.0", + "convert_case 0.11.0", + "convert_case_extras", "html-escape", "itertools 0.14.0", "leptos_hot_reload", @@ -4386,18 +4445,18 @@ dependencies = [ "rstml", "rustc_version", "server_fn_macro", - "syn 2.0.111", + "syn 2.0.116", "uuid", ] [[package]] name = "leptos_meta" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d489e38d3f541e9e43ecc2e3a815527840345a2afca629b3e23fcc1dd254578" +checksum = "6c3efe657b4c55ed2e078922786ffe20acfb71767c3dd913767b09a35c75c890" dependencies = [ "futures", - "indexmap 2.12.1", + "indexmap 2.13.0", "leptos", "or_poisoned", "send_wrapper 0.6.0", @@ -4407,9 +4466,9 @@ dependencies = [ [[package]] name = "leptos_router" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e573711f2fb9ab5d655ec38115220d359eaaf1dcb93cc0ea624543b6dba959" +checksum = "35058d4096407b8369843b5b5d227588dfd57ecc9e9bda0567523f084dce69e8" dependencies = [ "any_spawner", "either_of", @@ -4424,7 +4483,7 @@ dependencies = [ "rustc_version", "send_wrapper 0.6.0", "tachys", - "thiserror 2.0.17", + "thiserror 2.0.18", "url", "wasm-bindgen", "web-sys", @@ -4439,14 +4498,14 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] name = "leptos_server" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf1045af93050bf3388d1c138426393fc131f6d9e46a65519da884c033ed730" +checksum = "da974775c5ccbb6bd64be7f53f75e8321542e28f21563a416574dbe4d5447eae" dependencies = [ "any_spawner", "base64 0.22.1", @@ -4464,9 +4523,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.178" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libloading" @@ -4480,9 +4539,9 @@ dependencies = [ [[package]] name = "liblzma" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c36d08cad03a3fbe2c4e7bb3a9e84c57e4ee4135ed0b065cade3d98480c648" +checksum = "b6033b77c21d1f56deeae8014eb9fbe7bdf1765185a6c508b5ca82eeaed7f899" dependencies = [ "liblzma-sys", ] @@ -4500,9 +4559,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libp2p-identity" @@ -4514,19 +4573,19 @@ dependencies = [ "hkdf", "multihash", "sha2", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "libredox" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "libc", - "redox_syscall 0.6.0", + "redox_syscall 0.7.1", ] [[package]] @@ -4565,12 +4624,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "linear-map" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" - [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -4618,7 +4671,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "logos-blockchain-blend-crypto" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "blake2", "logos-blockchain-groth16", @@ -4632,7 +4685,7 @@ dependencies = [ [[package]] name = "logos-blockchain-blend-message" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "blake2", "derivative", @@ -4654,13 +4707,15 @@ dependencies = [ [[package]] name = "logos-blockchain-blend-proofs" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "ed25519-dalek", "generic-array 1.3.5", "logos-blockchain-blend-crypto", "logos-blockchain-groth16", + "logos-blockchain-pol", "logos-blockchain-poq", + "logos-blockchain-utils", "num-bigint 0.4.6", "serde", "thiserror 1.0.69", @@ -4669,7 +4724,7 @@ dependencies = [ [[package]] name = "logos-blockchain-chain-broadcast-service" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "async-trait", "derivative", @@ -4685,7 +4740,7 @@ dependencies = [ [[package]] name = "logos-blockchain-chain-service" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "async-trait", "bytes", @@ -4715,7 +4770,7 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-prover" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "logos-blockchain-circuits-utils", "tempfile", @@ -4724,7 +4779,7 @@ dependencies = [ [[package]] name = "logos-blockchain-circuits-utils" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "dirs", ] @@ -4732,13 +4787,16 @@ dependencies = [ [[package]] name = "logos-blockchain-common-http-client" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "futures", + "hex", "logos-blockchain-chain-broadcast-service", "logos-blockchain-chain-service", "logos-blockchain-core", + "logos-blockchain-groth16", "logos-blockchain-http-api-common", + "logos-blockchain-key-management-system-keys", "reqwest", "serde", "serde_json", @@ -4749,7 +4807,7 @@ dependencies = [ [[package]] name = "logos-blockchain-core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "ark-ff 0.4.2", "bincode", @@ -4757,15 +4815,16 @@ dependencies = [ "bytes", "const-hex", "futures", - "generic-array 1.3.5", "hex", "logos-blockchain-blend-proofs", "logos-blockchain-cryptarchia-engine", "logos-blockchain-groth16", "logos-blockchain-key-management-system-keys", + "logos-blockchain-poc", "logos-blockchain-pol", "logos-blockchain-poseidon2", "logos-blockchain-utils", + "logos-blockchain-utxotree", "multiaddr", "nom 8.0.0", "num-bigint 0.4.6", @@ -4778,9 +4837,10 @@ dependencies = [ [[package]] name = "logos-blockchain-cryptarchia-engine" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "cfg_eval", + "logos-blockchain-pol", "logos-blockchain-utils", "serde", "serde_with", @@ -4793,7 +4853,7 @@ dependencies = [ [[package]] name = "logos-blockchain-cryptarchia-sync" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "bytes", "futures", @@ -4810,7 +4870,7 @@ dependencies = [ [[package]] name = "logos-blockchain-groth16" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", @@ -4822,34 +4882,33 @@ dependencies = [ "num-bigint 0.4.6", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "logos-blockchain-http-api-common" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "axum 0.7.9", - "governor", "logos-blockchain-core", "logos-blockchain-key-management-system-keys", "serde", "serde_json", "serde_with", - "tower_governor", + "tracing", ] [[package]] name = "logos-blockchain-key-management-system-keys" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "async-trait", "bytes", "ed25519-dalek", "generic-array 1.3.5", - "logos-blockchain-blend-proofs", + "hex", "logos-blockchain-groth16", "logos-blockchain-key-management-system-macros", "logos-blockchain-poseidon2", @@ -4859,7 +4918,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "subtle", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "x25519-dalek", @@ -4869,24 +4928,41 @@ dependencies = [ [[package]] name = "logos-blockchain-key-management-system-macros" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", +] + +[[package]] +name = "logos-blockchain-key-management-system-operators" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" +dependencies = [ + "async-trait", + "logos-blockchain-blend-proofs", + "logos-blockchain-core", + "logos-blockchain-groth16", + "logos-blockchain-key-management-system-keys", + "logos-blockchain-poseidon2", + "logos-blockchain-utxotree", + "tokio", + "tracing", ] [[package]] name = "logos-blockchain-key-management-system-service" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "async-trait", "log", "logos-blockchain-key-management-system-keys", + "logos-blockchain-key-management-system-operators", "overwatch", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", ] @@ -4894,7 +4970,7 @@ dependencies = [ [[package]] name = "logos-blockchain-ledger" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "derivative", "logos-blockchain-blend-crypto", @@ -4904,7 +4980,7 @@ dependencies = [ "logos-blockchain-cryptarchia-engine", "logos-blockchain-groth16", "logos-blockchain-key-management-system-keys", - "logos-blockchain-mmr", + "logos-blockchain-pol", "logos-blockchain-utils", "logos-blockchain-utxotree", "num-bigint 0.4.6", @@ -4915,22 +4991,10 @@ dependencies = [ "tracing", ] -[[package]] -name = "logos-blockchain-mmr" -version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" -dependencies = [ - "ark-ff 0.4.2", - "logos-blockchain-groth16", - "logos-blockchain-poseidon2", - "rpds", - "serde", -] - [[package]] name = "logos-blockchain-network-service" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "async-trait", "futures", @@ -4944,25 +5008,44 @@ dependencies = [ ] [[package]] -name = "logos-blockchain-pol" +name = "logos-blockchain-poc" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", "logos-blockchain-groth16", "logos-blockchain-witness-generator", "num-bigint 0.4.6", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "logos-blockchain-pol" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" +dependencies = [ + "astro-float", + "logos-blockchain-circuits-prover", + "logos-blockchain-circuits-utils", + "logos-blockchain-groth16", + "logos-blockchain-utils", + "logos-blockchain-witness-generator", + "num-bigint 0.4.6", "num-traits", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", + "tracing", ] [[package]] name = "logos-blockchain-poq" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", @@ -4972,13 +5055,14 @@ dependencies = [ "num-bigint 0.4.6", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", + "tracing", ] [[package]] name = "logos-blockchain-poseidon2" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "ark-bn254 0.4.0", "ark-ff 0.4.2", @@ -4989,7 +5073,7 @@ dependencies = [ [[package]] name = "logos-blockchain-services-utils" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "async-trait", "futures", @@ -5004,7 +5088,7 @@ dependencies = [ [[package]] name = "logos-blockchain-storage-service" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "async-trait", "bytes", @@ -5021,7 +5105,7 @@ dependencies = [ [[package]] name = "logos-blockchain-time-service" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "async-trait", "futures", @@ -5029,7 +5113,7 @@ dependencies = [ "logos-blockchain-cryptarchia-engine", "overwatch", "sntpc", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", "tokio", "tokio-stream", @@ -5039,7 +5123,7 @@ dependencies = [ [[package]] name = "logos-blockchain-utils" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "async-trait", "blake2", @@ -5056,10 +5140,9 @@ dependencies = [ [[package]] name = "logos-blockchain-utxotree" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "ark-ff 0.4.2", - "logos-blockchain-core", "logos-blockchain-groth16", "logos-blockchain-poseidon2", "num-bigint 0.4.6", @@ -5071,7 +5154,7 @@ dependencies = [ [[package]] name = "logos-blockchain-witness-generator" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "tempfile", ] @@ -5079,7 +5162,7 @@ dependencies = [ [[package]] name = "logos-blockchain-zksign" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git#451df112f8574aea2840d04fffb7e16e76d24f42" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#81192877116cbc3eedf6688b85fab6dd0e448290" dependencies = [ "logos-blockchain-circuits-prover", "logos-blockchain-circuits-utils", @@ -5089,7 +5172,8 @@ dependencies = [ "num-bigint 0.4.6", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", + "tracing", ] [[package]] @@ -5180,7 +5264,7 @@ dependencies = [ "manyhow-macros", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -5196,13 +5280,13 @@ dependencies = [ [[package]] name = "match-lookup" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" +checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.116", ] [[package]] @@ -5235,20 +5319,20 @@ checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memmap2" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" dependencies = [ "libc", ] @@ -5278,7 +5362,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "block", "core-graphics-types", "foreign-types 0.5.0", @@ -5390,17 +5474,17 @@ dependencies = [ [[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", "openssl", - "openssl-probe 0.1.6", + "openssl-probe", "openssl-sys", "schannel", - "security-framework 2.11.1", + "security-framework", "security-framework-sys", "tempfile", ] @@ -5445,12 +5529,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "no-std-compat" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" - [[package]] name = "no_std_strings" version = "0.1.3" @@ -5476,18 +5554,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "nonempty" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" - -[[package]] -name = "nonzero_ext" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" - [[package]] name = "nssa" version = "0.1.0" @@ -5508,7 +5574,7 @@ dependencies = [ "sha2", "test-case", "test_program_methods", - "thiserror 2.0.17", + "thiserror 2.0.18", "token_core", ] @@ -5519,13 +5585,14 @@ dependencies = [ "base58", "borsh", "bytemuck", + "bytesize", "chacha20", "k256", "risc0-zkvm", "serde", "serde_json", "serde_with", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -5602,7 +5669,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -5646,16 +5713,6 @@ dependencies = [ "libm", ] -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_enum" version = "0.7.5" @@ -5674,7 +5731,7 @@ checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -5722,7 +5779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed0423ff9973dea4d6bd075934fdda86ebb8c05bdf9d6b0507067d4a1226371d" dependencies = [ "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -5749,7 +5806,7 @@ version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -5766,15 +5823,9 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - [[package]] name = "openssl-probe" version = "0.2.1" @@ -5801,7 +5852,7 @@ checksum = "969ccca8ffc4fb105bd131a228107d5c9dd89d9d627edf3295cbe979156f9712" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -5824,7 +5875,7 @@ dependencies = [ "async-trait", "futures", "overwatch-derive", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tokio-stream", "tokio-util", @@ -5840,7 +5891,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -5894,7 +5945,7 @@ dependencies = [ "regex", "regex-syntax", "structmeta", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -5941,7 +5992,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -6054,7 +6105,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -6085,7 +6136,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -6101,9 +6152,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -6116,7 +6167,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", "version_check", "yansi", ] @@ -6154,11 +6205,11 @@ dependencies = [ [[package]] name = "proptest" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -6196,7 +6247,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -6209,7 +6260,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -6235,21 +6286,6 @@ dependencies = [ "parking_lot", ] -[[package]] -name = "quanta" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" -dependencies = [ - "crossbeam-utils", - "libc", - "once_cell", - "raw-cpuid", - "wasi", - "web-sys", - "winapi", -] - [[package]] name = "quinn" version = "0.11.9" @@ -6263,8 +6299,8 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.1", - "thiserror 2.0.17", + "socket2 0.6.2", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -6285,7 +6321,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.17", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -6300,16 +6336,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.6.2", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -6333,7 +6369,7 @@ dependencies = [ "proc-macro-utils", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -6366,7 +6402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -6386,7 +6422,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]] @@ -6395,14 +6431,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "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.4", ] @@ -6413,16 +6449,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.9.3", -] - -[[package]] -name = "raw-cpuid" -version = "11.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" -dependencies = [ - "bitflags 2.10.0", + "rand_core 0.9.5", ] [[package]] @@ -6453,16 +6480,16 @@ dependencies = [ [[package]] name = "reactive_graph" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f0df355582937223ea403e52490201d65295bd6981383c69bfae5a1f8730c2" +checksum = "35774620b3da884a07341e9e36612e1509b1eb0553ef3bb76f1547dd1b797417" dependencies = [ "any_spawner", "async-lock", "futures", "guardian", "hydration_context", - "indexmap 2.12.1", + "indexmap 2.13.0", "or_poisoned", "paste", "pin-project-lite", @@ -6471,7 +6498,7 @@ dependencies = [ "send_wrapper 0.6.0", "serde", "slotmap", - "thiserror 2.0.17", + "thiserror 2.0.18", "web-sys", ] @@ -6481,7 +6508,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35372f05664a62a3dd389503371a15b8feb3396f99f6ec000de651fddb030942" dependencies = [ - "dashmap 6.1.0", + "dashmap", "guardian", "itertools 0.14.0", "or_poisoned", @@ -6502,7 +6529,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -6511,16 +6538,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "redox_syscall" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -6529,9 +6556,9 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -6551,14 +6578,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -6568,9 +6595,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -6579,21 +6606,21 @@ dependencies = [ [[package]] name = "regex-lite" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "reqwest" -version = "0.12.26" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", @@ -6626,13 +6653,13 @@ dependencies = [ "tokio-native-tls", "tokio-rustls", "tokio-util", - "tower 0.5.2", + "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.4.2", "web-sys", "webpki-roots", ] @@ -6655,7 +6682,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -6676,7 +6703,7 @@ dependencies = [ "anyhow", "borsh", "bytemuck", - "derive_more 2.1.0", + "derive_more 2.1.1", "elf", "lazy_static", "postcard", @@ -6756,7 +6783,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8eae53a7bf1c09828dfd46ed5c942cefbf4bef3c4400f6758001569a834c462" dependencies = [ "cc", - "derive_more 2.1.0", + "derive_more 2.1.1", "glob", "risc0-build-kernel", "risc0-core", @@ -6811,7 +6838,7 @@ dependencies = [ "bytemuck", "byteorder", "cfg-if", - "derive_more 2.1.0", + "derive_more 2.1.1", "enum-map", "gdbstub", "gdbstub_arch", @@ -6841,7 +6868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d677ec41e475534e18e58889ef0626dcdabf5e918804ef847da0c0bbf300b3" dependencies = [ "cc", - "derive_more 2.1.0", + "derive_more 2.1.1", "glob", "risc0-build-kernel", "risc0-core", @@ -6857,7 +6884,7 @@ dependencies = [ "bytemuck", "nvtx", "puffin", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -6928,7 +6955,7 @@ dependencies = [ "parking_lot", "paste", "rand 0.9.2", - "rand_core 0.9.3", + "rand_core 0.9.5", "rayon", "risc0-core", "risc0-sys", @@ -6952,7 +6979,7 @@ dependencies = [ "borsh", "bytemuck", "bytes", - "derive_more 2.1.0", + "derive_more 2.1.1", "elf", "enum-map", "gdbstub", @@ -6997,7 +7024,7 @@ checksum = "4db893788c416287e2e1a87e6b8f5302511a04a45329e699d6a32a16874fd24f" dependencies = [ "bytemuck", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "getrandom 0.3.4", "libm", "num_enum", @@ -7055,16 +7082,16 @@ dependencies = [ "quote", "rand 0.9.2", "syn 1.0.109", - "thiserror 2.0.17", + "thiserror 2.0.18", "tiny-keccak", "tokio", ] [[package]] name = "rsa" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ "const-oid", "digest", @@ -7090,9 +7117,9 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.111", + "syn 2.0.116", "syn_derive", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -7140,11 +7167,11 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys", @@ -7153,9 +7180,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.35" +version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "log", "once_cell", @@ -7172,17 +7199,17 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe 0.2.1", + "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.5.1", + "security-framework", ] [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "web-time", "zeroize", @@ -7203,10 +7230,10 @@ dependencies = [ "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki", - "security-framework 3.5.1", + "security-framework", "security-framework-sys", "webpki-root-certs 0.26.11", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7217,9 +7244,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ "ring", "rustls-pki-types", @@ -7243,9 +7270,9 @@ 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 = "rzup" @@ -7261,7 +7288,7 @@ dependencies = [ "sha2", "strum", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "toml 0.8.23", "yaml-rust2", ] @@ -7307,9 +7334,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -7320,14 +7347,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4908ad288c5035a8eb12cfdf0d49270def0a268ee162b75eeee0f85d155a7c45" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -7373,24 +7400,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "d17b898a6d6948c3a8ee4372c17cb384f90d2e6e912ef00895b14fd7ab54ec38" dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" -dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -7399,9 +7413,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "321c8673b092a9a42605034a9879d73cb79101ed5fd117bc9a597b89b4e9e61a" dependencies = [ "core-foundation-sys", "libc", @@ -7440,9 +7454,11 @@ dependencies = [ "base58", "bedrock_client", "borsh", + "bytesize", "chrono", "common", "futures", + "humantime-serde", "jsonrpsee", "log", "logos-blockchain-core", @@ -7468,7 +7484,9 @@ dependencies = [ "anyhow", "base58", "base64 0.22.1", + "bedrock_client", "borsh", + "bytesize", "common", "futures", "hex", @@ -7537,7 +7555,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -7548,20 +7566,20 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -7583,7 +7601,7 @@ checksum = "f3faaf9e727533a19351a43cc5a8de957372163c7d35cc48c90b75cdda13c352" dependencies = [ "percent-encoding", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -7594,7 +7612,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -7637,9 +7655,9 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.12.1", + "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.2.0", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -7655,7 +7673,7 @@ dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -7664,7 +7682,7 @@ version = "0.9.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0623d197252096520c6f2a5e1171ee436e5af99a5d7caa2891e55e61950e6d9" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.13.0", "itoa", "ryu", "serde", @@ -7683,16 +7701,15 @@ dependencies = [ [[package]] name = "server_fn" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353d02fa2886cd8dae0b8da0965289fa8f2ecc7df633d1ce965f62fdf9644d29" +checksum = "0fafe3a832a747ded8dc4827c538a2a064284f150c1c44c51ec56b58bd947dd7" dependencies = [ "axum 0.8.8", "base64 0.22.1", "bytes", - "const-str 0.7.1", + "const-str 1.1.0", "const_format", - "dashmap 6.1.0", "futures", "gloo-net", "http 1.4.0", @@ -7700,6 +7717,7 @@ dependencies = [ "hyper", "inventory", "js-sys", + "or_poisoned", "pin-project-lite", "rustc_version", "rustversion", @@ -7708,31 +7726,31 @@ dependencies = [ "serde_json", "serde_qs", "server_fn_macro_default", - "thiserror 2.0.17", + "thiserror 2.0.18", "throw_error", "tokio", - "tower 0.5.2", + "tower", "tower-layer", "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.5.0", "web-sys", "xxhash-rust", ] [[package]] name = "server_fn_macro" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "950b8cfc9ff5f39ca879c5a7c5e640de2695a199e18e424c3289d0964cabe642" +checksum = "14faf423aab09f8c3eb2d9785bb37f11a255cdf01857d3c6083eacc82269c191" dependencies = [ "const_format", - "convert_case 0.8.0", + "convert_case 0.11.0", "proc-macro2", "quote", "rustc_version", - "syn 2.0.111", + "syn 2.0.116", "xxhash-rust", ] @@ -7743,7 +7761,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63eb08f80db903d3c42f64e60ebb3875e0305be502bdc064ec0a0eab42207f00" dependencies = [ "server_fn_macro", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -7776,10 +7794,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.7" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -7801,9 +7820,9 @@ checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "slotmap" @@ -7852,9 +7871,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", "windows-sys 0.60.2", @@ -7885,15 +7904,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "spinning_top" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" -dependencies = [ - "lock_api", -] - [[package]] name = "spki" version = "0.7.3" @@ -7911,7 +7921,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" dependencies = [ "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -7934,7 +7944,8 @@ dependencies = [ "common", "nssa", "rocksdb", - "thiserror 2.0.17", + "tempfile", + "thiserror 2.0.18", ] [[package]] @@ -7952,7 +7963,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -7963,7 +7974,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -7984,7 +7995,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -8006,9 +8017,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.111" +version = "2.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" dependencies = [ "proc-macro2", "quote", @@ -8024,7 +8035,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -8044,25 +8055,25 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.11.0", "core-foundation 0.9.4", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -8070,9 +8081,9 @@ dependencies = [ [[package]] name = "tachys" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b2db11e455f7e84e2cc3e76f8a3f3843f7956096265d5ecff781eabe235077" +checksum = "14691ce610071757bd17bd8d572065192c9c93f9f169125390aaea345a4c56b9" dependencies = [ "any_spawner", "async-trait", @@ -8082,14 +8093,12 @@ dependencies = [ "erased", "futures", "html-escape", - "indexmap 2.12.1", + "indexmap 2.13.0", "itertools 0.14.0", "js-sys", - "linear-map", "next_tuple", "oco_ref", "or_poisoned", - "parking_lot", "paste", "reactive_graph", "reactive_stores", @@ -8110,12 +8119,12 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.23.0" +version = "3.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.1", "once_cell", "rustix", "windows-sys 0.61.2", @@ -8148,7 +8157,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -8159,7 +8168,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", "test-case-core", ] @@ -8203,7 +8212,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tokio-stream", "tokio-util", @@ -8222,11 +8231,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -8237,18 +8246,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -8344,9 +8353,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "bytes", "libc", @@ -8354,7 +8363,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.1", + "socket2 0.6.2", "tokio-macros", "windows-sys 0.61.2", ] @@ -8367,7 +8376,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -8453,11 +8462,11 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.11+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.13.0", "serde_core", "serde_spanned 1.0.4", "toml_datetime 0.7.5+spec-1.1.0", @@ -8490,7 +8499,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.13.0", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -8504,7 +8513,7 @@ version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.13.0", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "winnow", @@ -8512,9 +8521,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ "winnow", ] @@ -8533,9 +8542,9 @@ checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tonic" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a286e33f82f8a1ee2df63f4fa35c0becf4a85a0cb03091a15fd7bf0b402dc94a" +checksum = "7f32a6f80051a4111560201420c7885d0082ba9efe2ab61875c587bb6b18b9a0" dependencies = [ "async-trait", "axum 0.8.8", @@ -8550,11 +8559,11 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "socket2 0.6.1", + "socket2 0.6.2", "sync_wrapper", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -8562,9 +8571,9 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c55a2d6a14174563de34409c9f92ff981d006f56da9c6ecd40d9d4a31500b0" +checksum = "9f86539c0089bfd09b1f8c0ab0239d80392af74c21bc9e0f15e1b4aca4c1647f" dependencies = [ "bytes", "prost 0.14.3", @@ -8573,24 +8582,13 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", - "indexmap 2.12.1", + "indexmap 2.13.0", "pin-project-lite", "slab", "sync_wrapper", @@ -8607,7 +8605,7 @@ 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-core", "futures-util", @@ -8623,7 +8621,7 @@ dependencies = [ "pin-project-lite", "tokio", "tokio-util", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -8641,27 +8639,11 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" -[[package]] -name = "tower_governor" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3790eac6ad3fb8d9d96c2b040ae06e2517aa24b067545d1078b96ae72f7bb9a7" -dependencies = [ - "axum 0.7.9", - "forwarded-header-value", - "governor", - "http 1.4.0", - "pin-project", - "thiserror 1.0.69", - "tower 0.4.13", - "tracing", -] - [[package]] name = "tracing" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -8677,14 +8659,14 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] name = "tracing-core" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -8734,7 +8716,7 @@ dependencies = [ "log", "rand 0.9.2", "sha1", - "thiserror 2.0.17", + "thiserror 2.0.18", "utf-8", ] @@ -8754,33 +8736,13 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" -[[package]] -name = "typed-builder" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef81aec2ca29576f9f6ae8755108640d0a86dd3161b2e8bca6cfa554e98f77d" -dependencies = [ - "typed-builder-macro 0.21.2", -] - [[package]] name = "typed-builder" version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31aa81521b70f94402501d848ccc0ecaa8f93c8eb6999eb9747e72287757ffda" dependencies = [ - "typed-builder-macro 0.23.2", -] - -[[package]] -name = "typed-builder-macro" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecb9ecf7799210407c14a8cfdfe0173365780968dc57973ed082211958e0b18" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", + "typed-builder-macro", ] [[package]] @@ -8791,7 +8753,7 @@ checksum = "076a02dc54dd46795c2e9c8282ed40bcfb1e22747e955de9389a1de28190fb26" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -8827,7 +8789,7 @@ checksum = "27a7a9b72ba121f6f1f6c3632b85604cac41aedb5ddc70accbebb6cac83de846" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -8844,9 +8806,9 @@ checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-normalization" @@ -8911,9 +8873,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.1.4" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a" +checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc" dependencies = [ "base64 0.22.1", "log", @@ -8922,7 +8884,6 @@ dependencies = [ "rustls-pki-types", "ureq-proto", "utf-8", - "webpki-roots", ] [[package]] @@ -8939,14 +8900,15 @@ dependencies = [ [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -8981,11 +8943,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.1", "js-sys", "wasm-bindgen", ] @@ -9033,6 +8995,8 @@ dependencies = [ "env_logger", "futures", "hex", + "humantime", + "humantime-serde", "indicatif", "itertools 0.14.0", "key_protocol", @@ -9079,18 +9043,27 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -9101,11 +9074,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -9114,9 +9088,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9124,26 +9098,48 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.13.0", + "wasm-encoder", + "wasmparser", +] + [[package]] name = "wasm-streams" version = "0.4.2" @@ -9157,6 +9153,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasm_split_helpers" version = "0.2.0" @@ -9176,14 +9185,26 @@ dependencies = [ "base16", "quote", "sha2", - "syn 2.0.111", + "syn 2.0.116", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap 2.13.0", + "semver", ] [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -9205,23 +9226,23 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.5", + "webpki-root-certs 1.0.6", ] [[package]] name = "webpki-root-certs" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" dependencies = [ "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ "rustls-pki-types", ] @@ -9288,7 +9309,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -9299,7 +9320,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -9355,6 +9376,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.60.2" @@ -9570,9 +9600,91 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap 2.13.0", + "prettyplease", + "syn 2.0.116", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.116", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" @@ -9653,28 +9765,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -9694,7 +9806,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", "synstructure", ] @@ -9709,13 +9821,13 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -9748,7 +9860,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.116", ] [[package]] @@ -9762,12 +9874,18 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap 2.12.1", + "indexmap 2.13.0", "memchr", - "thiserror 2.0.17", + "thiserror 2.0.18", "zopfli", ] +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + [[package]] name = "zopfli" version = "0.8.3" diff --git a/Cargo.toml b/Cargo.toml index 5f2a7033..c868e0a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,6 @@ members = [ "indexer/service/protocol", "indexer/service/rpc", "explorer_service", - "programs/token/core", - "programs/token", "program_methods", "program_methods/guest", "test_program_methods", @@ -52,7 +50,7 @@ indexer_service = { path = "indexer/service" } indexer_service_protocol = { path = "indexer/service/protocol" } indexer_service_rpc = { path = "indexer/service/rpc" } wallet = { path = "wallet" } -wallet-ffi = { path = "wallet-ffi" } +wallet-ffi = { path = "wallet-ffi", default-features = false } token_core = { path = "programs/token/core" } token_program = { path = "programs/token" } amm_core = { path = "programs/amm/core" } @@ -89,6 +87,9 @@ thiserror = "2.0.12" sha2 = "0.10.8" hex = "0.4.3" bytemuck = "1.24.0" +bytesize = { version = "2.3.1", features = ["serde"] } +humantime-serde = "1.1" +humantime = "2.1" aes-gcm = "0.10.3" toml = "0.7.4" bincode = "1.3.3" @@ -113,6 +114,7 @@ logos-blockchain-common-http-client = { git = "https://github.com/logos-blockcha logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } logos-blockchain-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } logos-blockchain-chain-broadcast-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } +logos-blockchain-chain-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } rocksdb = { version = "0.24.0", default-features = false, features = [ "snappy", diff --git a/README.md b/README.md index 7bcfa25e..ee2d8097 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,12 @@ To our knowledge, this design is unique to LEZ. Other privacy-focused programmab 3. Transferring private to public (local / privacy-preserving execution) - Bob executes the token program `Transfer` function locally, sending to Charlie’s public account. - A ZKP of correct execution is generated. - - Bob’s private balance stays hidden. - - Charlie’s public account is updated on-chain. + - Bob’s private account and balance still remain hidden. + - Charlie's public account is modified with the new tokens added. +4. Transferring public to public (public execution): + - Alice submits a transaction to execute the token program `Transfer` function on-chain, specifying Charlie's public account as recipient. + - The execution is handled on-chain without ZKPs involved. + - Alice's and Charlie's accounts are modified according to the transaction. 4. Transfer from public to public (public execution) - Alice submits an on-chain transaction to run `Transfer`, sending to Charlie’s public account. @@ -139,12 +143,67 @@ The sequencer and logos blockchain node can be run locally: - `cargo build --all-features` - `./target/debug/logos-blockchain-node --deployment nodes/node/standalone-deployment-config.yaml nodes/node/standalone-node-config.yaml` - 2. On another terminal go to the `logos-blockchain/lssa` repo and run indexer service: + 2. Alternatively (WARNING: This node is outdated) go to ``logos-blockchain/lssa/` repo and run the node from docker: + - `cd bedrock` + - Change line 14 of `docker-compose.yml` from `"0:18080/tcp"` into `"8080:18080/tcp"` + - `docker compose up` + + 3. On another terminal go to the `logos-blockchain/lssa` repo and run indexer service: - `RUST_LOG=info cargo run -p indexer_service indexer/service/configs/indexer_config.json` - 3. On another terminal go to the `logos-blockchain/lssa` repo and run the sequencer: + 4. On another terminal go to the `logos-blockchain/lssa` repo and run the sequencer: - `RUST_LOG=info cargo run -p sequencer_runner sequencer_runner/configs/debug` +### Notes on cleanup + +After stopping services above you need to remove 3 folders to start cleanly: + 1. In the `logos-blockchain/logos-blockchain` folder `state` (not needed in case of docker setup) + 2. In the `lssa` folder `sequencer_runner/rocksdb` + 3. In the `lssa` file `sequencer_runner/bedrock_signing_key` + 4. In the `lssa` folder `indexer/service/rocksdb` + +### Normal mode (`just` commands) +We provide a `Justfile` for developer and user needs, you can run the whole setup with it. The only difference will be that logos-blockchain (bedrock) will be started from docker. + +#### 1'st Terminal + +```bash +just run-bedrock +``` + +#### 2'nd Terminal + +```bash +just run-indexer +``` + +#### 3'rd Terminal + +```bash +just run-sequencer +``` + +#### 4'th Terminal + +```bash +just run-explorer +``` + +#### 5'th Terminal + +You can run any command our wallet support by passing it as an argument for `just run-wallet`, for example: + +```bash +just run-wallet check-health +``` + +This will use a wallet binary built from this repo and not the one installed in your system if you have some. Also another wallet home directory will be used. This is done to not to mess up with your local wallet and to easily clean generated files (see next section). + +#### Shutdown + +1. Press `ctrl-c` in every terminal +2. Run `just clean` to clean runtime data + ### Standalone mode The sequencer can be run in standalone mode with: ```bash diff --git a/artifacts/program_methods/amm.bin b/artifacts/program_methods/amm.bin index 72bab162..45e28420 100644 Binary files a/artifacts/program_methods/amm.bin and b/artifacts/program_methods/amm.bin differ diff --git a/artifacts/program_methods/authenticated_transfer.bin b/artifacts/program_methods/authenticated_transfer.bin index 5fd291d7..5ff56cf5 100644 Binary files a/artifacts/program_methods/authenticated_transfer.bin and b/artifacts/program_methods/authenticated_transfer.bin differ diff --git a/artifacts/program_methods/pinata.bin b/artifacts/program_methods/pinata.bin index 7d98f6bb..aef1a70c 100644 Binary files a/artifacts/program_methods/pinata.bin and b/artifacts/program_methods/pinata.bin differ diff --git a/artifacts/program_methods/pinata_token.bin b/artifacts/program_methods/pinata_token.bin index 51692ae1..47ada3c2 100644 Binary files a/artifacts/program_methods/pinata_token.bin and b/artifacts/program_methods/pinata_token.bin differ diff --git a/artifacts/program_methods/privacy_preserving_circuit.bin b/artifacts/program_methods/privacy_preserving_circuit.bin index 28bbaba4..3a0330e6 100644 Binary files a/artifacts/program_methods/privacy_preserving_circuit.bin and b/artifacts/program_methods/privacy_preserving_circuit.bin differ diff --git a/artifacts/program_methods/token.bin b/artifacts/program_methods/token.bin index b26e52cd..dbbf3c07 100644 Binary files a/artifacts/program_methods/token.bin and b/artifacts/program_methods/token.bin differ diff --git a/artifacts/test_program_methods/burner.bin b/artifacts/test_program_methods/burner.bin index e2c3bdf1..a3a2839e 100644 Binary files a/artifacts/test_program_methods/burner.bin and b/artifacts/test_program_methods/burner.bin differ diff --git a/artifacts/test_program_methods/chain_caller.bin b/artifacts/test_program_methods/chain_caller.bin index f02b0621..b920b0e2 100644 Binary files a/artifacts/test_program_methods/chain_caller.bin and b/artifacts/test_program_methods/chain_caller.bin differ diff --git a/artifacts/test_program_methods/changer_claimer.bin b/artifacts/test_program_methods/changer_claimer.bin index 6c929b34..019b2df0 100644 Binary files a/artifacts/test_program_methods/changer_claimer.bin and b/artifacts/test_program_methods/changer_claimer.bin differ diff --git a/artifacts/test_program_methods/claimer.bin b/artifacts/test_program_methods/claimer.bin index c7885bba..6aaa6bba 100644 Binary files a/artifacts/test_program_methods/claimer.bin and b/artifacts/test_program_methods/claimer.bin differ diff --git a/artifacts/test_program_methods/data_changer.bin b/artifacts/test_program_methods/data_changer.bin index a45a027f..5712c28e 100644 Binary files a/artifacts/test_program_methods/data_changer.bin and b/artifacts/test_program_methods/data_changer.bin differ diff --git a/artifacts/test_program_methods/extra_output.bin b/artifacts/test_program_methods/extra_output.bin index c149d572..20e0e12a 100644 Binary files a/artifacts/test_program_methods/extra_output.bin and b/artifacts/test_program_methods/extra_output.bin differ diff --git a/artifacts/test_program_methods/malicious_authorization_changer.bin b/artifacts/test_program_methods/malicious_authorization_changer.bin index 2aa1b518..8c434e84 100644 Binary files a/artifacts/test_program_methods/malicious_authorization_changer.bin and b/artifacts/test_program_methods/malicious_authorization_changer.bin differ diff --git a/artifacts/test_program_methods/minter.bin b/artifacts/test_program_methods/minter.bin index 9b454fc2..648c9e2a 100644 Binary files a/artifacts/test_program_methods/minter.bin and b/artifacts/test_program_methods/minter.bin differ diff --git a/artifacts/test_program_methods/missing_output.bin b/artifacts/test_program_methods/missing_output.bin index eb41b0af..dcaf8cd0 100644 Binary files a/artifacts/test_program_methods/missing_output.bin and b/artifacts/test_program_methods/missing_output.bin differ diff --git a/artifacts/test_program_methods/modified_transfer.bin b/artifacts/test_program_methods/modified_transfer.bin index c424767b..b06bcdd4 100644 Binary files a/artifacts/test_program_methods/modified_transfer.bin and b/artifacts/test_program_methods/modified_transfer.bin differ diff --git a/artifacts/test_program_methods/nonce_changer.bin b/artifacts/test_program_methods/nonce_changer.bin index e1f1284e..c9434606 100644 Binary files a/artifacts/test_program_methods/nonce_changer.bin and b/artifacts/test_program_methods/nonce_changer.bin differ diff --git a/artifacts/test_program_methods/noop.bin b/artifacts/test_program_methods/noop.bin index 60ab5423..42cddfab 100644 Binary files a/artifacts/test_program_methods/noop.bin and b/artifacts/test_program_methods/noop.bin differ diff --git a/artifacts/test_program_methods/program_owner_changer.bin b/artifacts/test_program_methods/program_owner_changer.bin index 06e83e8a..66474082 100644 Binary files a/artifacts/test_program_methods/program_owner_changer.bin and b/artifacts/test_program_methods/program_owner_changer.bin differ diff --git a/artifacts/test_program_methods/simple_balance_transfer.bin b/artifacts/test_program_methods/simple_balance_transfer.bin index 8c6dbde7..f2383856 100644 Binary files a/artifacts/test_program_methods/simple_balance_transfer.bin and b/artifacts/test_program_methods/simple_balance_transfer.bin differ diff --git a/bedrock_client/Cargo.toml b/bedrock_client/Cargo.toml index bd275d8b..696174d8 100644 --- a/bedrock_client/Cargo.toml +++ b/bedrock_client/Cargo.toml @@ -13,6 +13,8 @@ tokio-retry.workspace = true futures.workspace = true log.workspace = true serde.workspace = true +humantime-serde.workspace = true logos-blockchain-common-http-client.workspace = true logos-blockchain-core.workspace = true logos-blockchain-chain-broadcast-service.workspace = true +logos-blockchain-chain-service.workspace = true diff --git a/bedrock_client/src/lib.rs b/bedrock_client/src/lib.rs index 7655a31c..534a0cf6 100644 --- a/bedrock_client/src/lib.rs +++ b/bedrock_client/src/lib.rs @@ -3,8 +3,11 @@ use std::time::Duration; use anyhow::{Context as _, Result}; use common::config::BasicAuth; use futures::{Stream, TryFutureExt}; +#[expect(clippy::single_component_path_imports, reason = "Satisfy machete")] +use humantime_serde; use log::{info, warn}; pub use logos_blockchain_chain_broadcast_service::BlockInfo; +use logos_blockchain_chain_service::CryptarchiaInfo; pub use logos_blockchain_common_http_client::{CommonHttpClient, Error}; pub use logos_blockchain_core::{block::Block, header::HeaderId, mantle::SignedMantleTx}; use reqwest::{Client, Url}; @@ -14,14 +17,15 @@ use tokio_retry::Retry; /// Fibonacci backoff retry strategy configuration #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct BackoffConfig { - pub start_delay_millis: u64, + #[serde(with = "humantime_serde")] + pub start_delay: Duration, pub max_retries: usize, } impl Default for BackoffConfig { fn default() -> Self { Self { - start_delay_millis: 100, + start_delay: Duration::from_millis(100), max_retries: 5, } } @@ -82,8 +86,19 @@ impl BedrockClient { .await } + pub async fn get_consensus_info(&self) -> Result { + Retry::spawn(self.backoff_strategy(), || { + self.http_client + .consensus_info(self.node_url.clone()) + .inspect_err(|err| warn!("Block fetching failed with error: {err:#}")) + }) + .await + } + fn backoff_strategy(&self) -> impl Iterator { - tokio_retry::strategy::FibonacciBackoff::from_millis(self.backoff.start_delay_millis) - .take(self.backoff.max_retries) + tokio_retry::strategy::FibonacciBackoff::from_millis( + self.backoff.start_delay.as_millis() as u64 + ) + .take(self.backoff.max_retries) } } diff --git a/common/Cargo.toml b/common/Cargo.toml index 2ef707f3..f7658304 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -18,6 +18,7 @@ sha2.workspace = true log.workspace = true hex.workspace = true borsh.workspace = true +bytesize.workspace = true base64.workspace = true url.workspace = true logos-blockchain-common-http-client.workspace = true diff --git a/common/src/block.rs b/common/src/block.rs index 90bbe357..0343435b 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -1,4 +1,6 @@ use borsh::{BorshDeserialize, BorshSerialize}; +use nssa::AccountId; +use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256, digest::FixedOutput}; use crate::{HashType, transaction::NSSATransaction}; @@ -50,7 +52,7 @@ pub enum BedrockStatus { Finalized, } -#[derive(Debug, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] pub struct Block { pub header: BlockHeader, pub body: BlockBody, @@ -107,6 +109,20 @@ impl From for HashableBlockData { } } +/// Helper struct for account (de-)serialization +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AccountInitialData { + pub account_id: AccountId, + pub balance: u128, +} + +/// Helper struct to (de-)serialize initial commitments +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CommitmentsInitialData { + pub npk: nssa_core::NullifierPublicKey, + pub account: nssa_core::account::Account, +} + #[cfg(test)] mod tests { use crate::{HashType, block::HashableBlockData, test_utils}; diff --git a/common/src/rpc_primitives/mod.rs b/common/src/rpc_primitives/mod.rs index ee64fb43..be5182e1 100644 --- a/common/src/rpc_primitives/mod.rs +++ b/common/src/rpc_primitives/mod.rs @@ -1,3 +1,4 @@ +use bytesize::ByteSize; use serde::{Deserialize, Serialize}; pub mod errors; @@ -8,13 +9,13 @@ pub mod requests; #[derive(Serialize, Deserialize, Clone, Debug)] pub struct RpcLimitsConfig { /// Maximum byte size of the json payload. - pub json_payload_max_size: usize, + pub json_payload_max_size: ByteSize, } impl Default for RpcLimitsConfig { fn default() -> Self { Self { - json_payload_max_size: 10 * 1024 * 1024, + json_payload_max_size: ByteSize::mib(10), } } } diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 833317dc..a996250b 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -1,4 +1,6 @@ use borsh::{BorshDeserialize, BorshSerialize}; +use log::warn; +use nssa::{AccountId, V02State}; use serde::{Deserialize, Serialize}; use crate::HashType; @@ -18,6 +20,54 @@ impl NSSATransaction { NSSATransaction::ProgramDeployment(tx) => tx.hash(), }) } + + pub fn affected_public_account_ids(&self) -> Vec { + match self { + NSSATransaction::ProgramDeployment(tx) => tx.affected_public_account_ids(), + NSSATransaction::Public(tx) => tx.affected_public_account_ids(), + NSSATransaction::PrivacyPreserving(tx) => tx.affected_public_account_ids(), + } + } + + // TODO: Introduce type-safe wrapper around checked transaction, e.g. AuthenticatedTransaction + pub fn transaction_stateless_check(self) -> Result { + // Stateless checks here + match self { + NSSATransaction::Public(tx) => { + if tx.witness_set().is_valid_for(tx.message()) { + Ok(NSSATransaction::Public(tx)) + } else { + Err(TransactionMalformationError::InvalidSignature) + } + } + NSSATransaction::PrivacyPreserving(tx) => { + if tx.witness_set().signatures_are_valid_for(tx.message()) { + Ok(NSSATransaction::PrivacyPreserving(tx)) + } else { + Err(TransactionMalformationError::InvalidSignature) + } + } + NSSATransaction::ProgramDeployment(tx) => Ok(NSSATransaction::ProgramDeployment(tx)), + } + } + + pub fn execute_check_on_state( + self, + state: &mut V02State, + ) -> Result { + match &self { + NSSATransaction::Public(tx) => state.transition_from_public_transaction(tx), + NSSATransaction::PrivacyPreserving(tx) => { + state.transition_from_privacy_preserving_transaction(tx) + } + NSSATransaction::ProgramDeployment(tx) => { + state.transition_from_program_deployment_transaction(tx) + } + } + .inspect_err(|err| warn!("Error at transition {err:#?}"))?; + + Ok(self) + } } impl From for NSSATransaction { @@ -46,3 +96,13 @@ pub enum TxKind { PrivacyPreserving, ProgramDeployment, } + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, thiserror::Error)] +pub enum TransactionMalformationError { + #[error("Invalid signature(-s)")] + InvalidSignature, + #[error("Failed to decode transaction with hash: {tx:?}")] + FailedToDecode { tx: HashType }, + #[error("Transaction size {size} exceeds maximum allowed size of {max} bytes")] + TransactionTooLarge { size: usize, max: usize }, +} diff --git a/completions/zsh/_wallet b/completions/zsh/_wallet index 9f40c4fb..e0c5f415 100644 --- a/completions/zsh/_wallet +++ b/completions/zsh/_wallet @@ -346,7 +346,7 @@ _wallet_config() { 'all' 'override_rust_log' 'sequencer_addr' - 'seq_poll_timeout_millis' + 'seq_poll_timeout' 'seq_tx_poll_max_blocks' 'seq_poll_max_retries' 'seq_block_poll_max_amount' diff --git a/configs/docker-all-in-one/indexer/indexer_config.json b/configs/docker-all-in-one/indexer/indexer_config.json index ae106b46..4c4fe085 100644 --- a/configs/docker-all-in-one/indexer/indexer_config.json +++ b/configs/docker-all-in-one/indexer/indexer_config.json @@ -1,9 +1,9 @@ { - "resubscribe_interval_millis": 1000, + "resubscribe_interval": "1s", "bedrock_client_config": { "addr": "http://logos-blockchain-node-0:18080", "backoff": { - "start_delay_millis": 100, + "start_delay": "100ms", "max_retries": 5 } }, diff --git a/configs/docker-all-in-one/sequencer/sequencer_config.json b/configs/docker-all-in-one/sequencer/sequencer_config.json index 579253dc..7217bf5a 100644 --- a/configs/docker-all-in-one/sequencer/sequencer_config.json +++ b/configs/docker-all-in-one/sequencer/sequencer_config.json @@ -4,13 +4,14 @@ "genesis_id": 1, "is_genesis_random": true, "max_num_tx_in_block": 20, + "max_block_size": "1 MiB", "mempool_max_size": 10000, - "block_create_timeout_millis": 10000, - "retry_pending_blocks_timeout_millis": 7000, + "block_create_timeout": "10s", + "retry_pending_blocks_timeout": "7s", "port": 3040, "bedrock_config": { "backoff": { - "start_delay_millis": 100, + "start_delay": "100ms", "max_retries": 5 }, "channel_id": "0101010101010101010101010101010101010101010101010101010101010101", diff --git a/examples/program_deployment/README.md b/examples/program_deployment/README.md index 63a14db7..61e530e3 100644 --- a/examples/program_deployment/README.md +++ b/examples/program_deployment/README.md @@ -3,7 +3,7 @@ This guide walks you through running the sequencer, compiling example programs, deploying a Hello World program, and interacting with accounts. You'll find: -- Programs: example NSSA programs under `methods/guest/src/bin`. +- Programs: example LEZ programs under `methods/guest/src/bin`. - Runners: scripts to create and submit transactions to invoke these programs publicly and privately under `src/bin`. # 0. Install the wallet @@ -13,16 +13,7 @@ cargo install --path wallet --force ``` # 1. Run the sequencer -From the project’s root directory, start the sequencer: -```bash -cd sequencer_runner -RUST_LOG=info cargo run $(pwd)/configs/debug -``` -Keep this terminal open. We’ll use it only to observe the node logs. - -> [!NOTE] -> If you have already ran this before you'll see a `rocksdb` directory with stored blocks. Be sure to remove that directory to follow this tutorial. - +From the project’s root directory, start the sequencer by following [these instructions](https://github.com/logos-blockchain/lssa#run-the-sequencer-and-node). ## Checking and setting up the wallet For sanity let's check that the wallet can connect to it. diff --git a/explorer_service/src/api.rs b/explorer_service/src/api.rs index c489c827..d0785949 100644 --- a/explorer_service/src/api.rs +++ b/explorer_service/src/api.rs @@ -1,5 +1,3 @@ -use std::str::FromStr as _; - use indexer_service_protocol::{Account, AccountId, Block, BlockId, HashType, Transaction}; use leptos::prelude::*; use serde::{Deserialize, Serialize}; @@ -30,7 +28,10 @@ pub async fn get_account(account_id: AccountId) -> Result Result { + use std::str::FromStr as _; + use indexer_service_rpc::RpcClient as _; + let client = expect_context::(); let mut blocks = Vec::new(); diff --git a/indexer/core/Cargo.toml b/indexer/core/Cargo.toml index c6e3cc35..792fb4b7 100644 --- a/indexer/core/Cargo.toml +++ b/indexer/core/Cargo.toml @@ -7,10 +7,14 @@ license = { workspace = true } [dependencies] common.workspace = true bedrock_client.workspace = true +nssa.workspace = true +nssa_core.workspace = true +storage.workspace = true anyhow.workspace = true log.workspace = true serde.workspace = true +humantime-serde.workspace = true tokio.workspace = true borsh.workspace = true futures.workspace = true diff --git a/indexer/core/src/block_store.rs b/indexer/core/src/block_store.rs new file mode 100644 index 00000000..fc7641a1 --- /dev/null +++ b/indexer/core/src/block_store.rs @@ -0,0 +1,115 @@ +use std::{path::Path, sync::Arc}; + +use anyhow::Result; +use bedrock_client::HeaderId; +use common::{block::Block, transaction::NSSATransaction}; +use nssa::{Account, AccountId, V02State}; +use storage::indexer::RocksDBIO; + +#[derive(Clone)] +pub struct IndexerStore { + dbio: Arc, +} + +impl IndexerStore { + /// Starting database at the start of new chain. + /// Creates files if necessary. + /// + /// ATTENTION: Will overwrite genesis block. + pub fn open_db_with_genesis( + location: &Path, + start_data: Option<(Block, V02State)>, + ) -> Result { + let dbio = RocksDBIO::open_or_create(location, start_data)?; + + Ok(Self { + dbio: Arc::new(dbio), + }) + } + + /// Reopening existing database + pub fn open_db_restart(location: &Path) -> Result { + Self::open_db_with_genesis(location, None) + } + + pub fn last_observed_l1_lib_header(&self) -> Result> { + Ok(self + .dbio + .get_meta_last_observed_l1_lib_header_in_db()? + .map(HeaderId::from)) + } + + pub fn get_last_block_id(&self) -> Result { + Ok(self.dbio.get_meta_last_block_in_db()?) + } + + pub fn get_block_at_id(&self, id: u64) -> Result { + Ok(self.dbio.get_block(id)?) + } + + pub fn get_block_batch(&self, offset: u64, limit: u64) -> Result> { + Ok(self.dbio.get_block_batch(offset, limit)?) + } + + pub fn get_transaction_by_hash(&self, tx_hash: [u8; 32]) -> Result { + let block = self.get_block_at_id(self.dbio.get_block_id_by_tx_hash(tx_hash)?)?; + let transaction = block + .body + .transactions + .iter() + .find(|enc_tx| enc_tx.hash().0 == tx_hash) + .ok_or_else(|| anyhow::anyhow!("Transaction not found in DB"))?; + + Ok(transaction.clone()) + } + + pub fn get_block_by_hash(&self, hash: [u8; 32]) -> Result { + self.get_block_at_id(self.dbio.get_block_id_by_hash(hash)?) + } + + pub fn get_transactions_by_account( + &self, + acc_id: [u8; 32], + offset: u64, + limit: u64, + ) -> Result> { + Ok(self.dbio.get_acc_transactions(acc_id, offset, limit)?) + } + + pub fn genesis_id(&self) -> u64 { + self.dbio + .get_meta_first_block_in_db() + .expect("Must be set at the DB startup") + } + + pub fn last_block(&self) -> u64 { + self.dbio + .get_meta_last_block_in_db() + .expect("Must be set at the DB startup") + } + + pub fn get_state_at_block(&self, block_id: u64) -> Result { + Ok(self.dbio.calculate_state_for_id(block_id)?) + } + + pub fn final_state(&self) -> Result { + Ok(self.dbio.final_state()?) + } + + pub fn get_account_final(&self, account_id: &AccountId) -> Result { + Ok(self.final_state()?.get_account_by_id(*account_id)) + } + + pub fn put_block(&self, block: Block, l1_header: HeaderId) -> Result<()> { + let mut final_state = self.dbio.final_state()?; + + for transaction in &block.body.transactions { + transaction + .clone() + .transaction_stateless_check()? + .execute_check_on_state(&mut final_state)?; + } + + Ok(self.dbio.put_block(block, l1_header.into())?) + } +} diff --git a/indexer/core/src/config.rs b/indexer/core/src/config.rs index f823f9ab..95e6147c 100644 --- a/indexer/core/src/config.rs +++ b/indexer/core/src/config.rs @@ -1,14 +1,23 @@ -use std::{fs::File, io::BufReader, path::Path}; +use std::{ + fs::File, + io::BufReader, + path::{Path, PathBuf}, + time::Duration, +}; use anyhow::{Context as _, Result}; pub use bedrock_client::BackoffConfig; -use common::config::BasicAuth; +use common::{ + block::{AccountInitialData, CommitmentsInitialData}, + config::BasicAuth, +}; +use humantime_serde; pub use logos_blockchain_core::mantle::ops::channel::ChannelId; use serde::{Deserialize, Serialize}; use url::Url; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BedrockClientConfig { +pub struct ClientConfig { /// For individual RPC requests we use Fibonacci backoff retry strategy. pub backoff: BackoffConfig, pub addr: Url, @@ -18,8 +27,17 @@ pub struct BedrockClientConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct IndexerConfig { - pub resubscribe_interval_millis: u64, - pub bedrock_client_config: BedrockClientConfig, + /// Home dir of sequencer storage + pub home: PathBuf, + /// List of initial accounts data + pub initial_accounts: Vec, + /// List of initial commitments + pub initial_commitments: Vec, + /// Sequencers signing key + pub signing_key: [u8; 32], + #[serde(with = "humantime_serde")] + pub consensus_info_polling_interval: Duration, + pub bedrock_client_config: ClientConfig, pub channel_id: ChannelId, } diff --git a/indexer/core/src/lib.rs b/indexer/core/src/lib.rs index 283842b8..b24cb1bd 100644 --- a/indexer/core/src/lib.rs +++ b/indexer/core/src/lib.rs @@ -1,110 +1,341 @@ -use std::sync::Arc; +use std::collections::VecDeque; -use anyhow::{Context as _, Result}; -use bedrock_client::BedrockClient; -use common::block::Block; -use futures::StreamExt; -use log::{debug, info}; +use anyhow::Result; +use bedrock_client::{BedrockClient, HeaderId}; +use common::block::{Block, HashableBlockData}; +// ToDo: Remove after testnet +use common::{HashType, PINATA_BASE58}; +use log::{debug, error, info}; use logos_blockchain_core::mantle::{ Op, SignedMantleTx, ops::channel::{ChannelId, inscribe::InscriptionOp}, }; -use tokio::sync::RwLock; -use crate::{config::IndexerConfig, state::IndexerState}; +use crate::{block_store::IndexerStore, config::IndexerConfig}; +pub mod block_store; pub mod config; pub mod state; #[derive(Clone)] pub struct IndexerCore { - bedrock_client: BedrockClient, - config: IndexerConfig, - state: IndexerState, + pub bedrock_client: BedrockClient, + pub config: IndexerConfig, + pub store: IndexerStore, +} + +#[derive(Clone)] +/// This struct represents one L1 block data fetched from backfilling +pub struct BackfillBlockData { + l2_blocks: Vec, + l1_header: HeaderId, +} + +#[derive(Clone)] +/// This struct represents data fetched fom backfilling in one iteration +pub struct BackfillData { + block_data: VecDeque, + curr_fin_l1_lib_header: HeaderId, } impl IndexerCore { pub fn new(config: IndexerConfig) -> Result { + let hashable_data = HashableBlockData { + block_id: 1, + transactions: vec![], + prev_block_hash: HashType([0; 32]), + timestamp: 0, + }; + + // Genesis creation is fine as it is, + // because it will be overwritten by sequencer. + // Therefore: + // ToDo: remove key from indexer config, use some default. + let signing_key = nssa::PrivateKey::try_new(config.signing_key).unwrap(); + let channel_genesis_msg_id = [0; 32]; + let start_block = hashable_data.into_pending_block(&signing_key, channel_genesis_msg_id); + + // This is a troubling moment, because changes in key protocol can + // affect this. And indexer can not reliably ask this data from sequencer + // because indexer must be independent from it. + // ToDo: move initial state generation into common and use the same method + // for indexer and sequencer. This way both services buit at same version + // could be in sync. + let initial_commitments: Vec = config + .initial_commitments + .iter() + .map(|init_comm_data| { + let npk = &init_comm_data.npk; + + let mut acc = init_comm_data.account.clone(); + + acc.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + + nssa_core::Commitment::new(npk, &acc) + }) + .collect(); + + let init_accs: Vec<(nssa::AccountId, u128)> = config + .initial_accounts + .iter() + .map(|acc_data| (acc_data.account_id, acc_data.balance)) + .collect(); + + let mut state = nssa::V02State::new_with_genesis_accounts(&init_accs, &initial_commitments); + + // ToDo: Remove after testnet + state.add_pinata_program(PINATA_BASE58.parse().unwrap()); + + let home = config.home.join("rocksdb"); + Ok(Self { bedrock_client: BedrockClient::new( config.bedrock_client_config.backoff, config.bedrock_client_config.addr.clone(), config.bedrock_client_config.auth.clone(), - ) - .context("Failed to create Bedrock client")?, + )?, config, - // No state setup for now, future task. - state: IndexerState { - latest_seen_block: Arc::new(RwLock::new(0)), - }, + store: IndexerStore::open_db_with_genesis(&home, Some((start_block, state)))?, }) } pub async fn subscribe_parse_block_stream(&self) -> impl futures::Stream> { - debug!("Subscribing to Bedrock block stream"); async_stream::stream! { - loop { - let mut stream_pinned = Box::pin(self.bedrock_client.get_lib_stream().await?); + info!("Searching for initial header"); - info!("Block stream joined"); + let last_l1_lib_header = self.store.last_observed_l1_lib_header()?; - while let Some(block_info) = stream_pinned.next().await { - let header_id = block_info.header_id; + let mut prev_last_l1_lib_header = match last_l1_lib_header { + Some(last_l1_lib_header) => { + info!("Last l1 lib header found: {last_l1_lib_header}"); + last_l1_lib_header + }, + None => { + info!("Last l1 lib header not found in DB"); + info!("Searching for the start of a channel"); - info!("Observed L1 block at height {}", block_info.height); + let BackfillData { + block_data: start_buff, + curr_fin_l1_lib_header: last_l1_lib_header, + } = self.search_for_channel_start().await?; - if let Some(l1_block) = self - .bedrock_client - .get_block_by_id(header_id) - .await? - { - info!("Extracted L1 block at height {}", block_info.height); - - let l2_blocks_parsed = parse_blocks( - l1_block.into_transactions().into_iter(), - &self.config.channel_id, - ).collect::>(); - - info!("Parsed {} L2 blocks", l2_blocks_parsed.len()); - - for l2_block in l2_blocks_parsed { - // State modification, will be updated in future - { - let mut guard = self.state.latest_seen_block.write().await; - if l2_block.header.block_id > *guard { - *guard = l2_block.header.block_id; - } - } + for BackfillBlockData { + l2_blocks: l2_block_vec, + l1_header, + } in start_buff { + for l2_block in l2_block_vec { + self.store.put_block(l2_block.clone(), l1_header)?; yield Ok(l2_block); } } + + last_l1_lib_header + }, + }; + + info!("Searching for initial header finished"); + + info!("Starting backfilling from {prev_last_l1_lib_header}"); + + loop { + let BackfillData { + block_data: buff, + curr_fin_l1_lib_header, + } = self + .backfill_to_last_l1_lib_header_id(prev_last_l1_lib_header, &self.config.channel_id) + .await + .inspect_err(|err| error!("Failed to backfill to last l1 lib header id with err {err:#?}"))?; + + prev_last_l1_lib_header = curr_fin_l1_lib_header; + + for BackfillBlockData { + l2_blocks: l2_block_vec, + l1_header: header, + } in buff { + for l2_block in l2_block_vec { + self.store.put_block(l2_block.clone(), header)?; + + yield Ok(l2_block); + } + } + } + } + } + + async fn get_lib(&self) -> Result { + Ok(self.bedrock_client.get_consensus_info().await?.lib) + } + + async fn get_next_lib(&self, prev_lib: HeaderId) -> Result { + loop { + let next_lib = self.get_lib().await?; + if next_lib != prev_lib { + break Ok(next_lib); + } else { + info!( + "Wait {:?} to not spam the node", + self.config.consensus_info_polling_interval + ); + tokio::time::sleep(self.config.consensus_info_polling_interval).await; + } + } + } + + /// WARNING: depending on channel state, + /// may take indefinite amount of time + pub async fn search_for_channel_start(&self) -> Result { + let mut curr_last_l1_lib_header = self.get_lib().await?; + let mut backfill_start = curr_last_l1_lib_header; + // ToDo: How to get root? + let mut backfill_limit = HeaderId::from([0; 32]); + // ToDo: Not scalable, initial buffer should be stored in DB to not run out of memory + // Don't want to complicate DB even more right now. + let mut block_buffer = VecDeque::new(); + + 'outer: loop { + let mut cycle_header = curr_last_l1_lib_header; + + loop { + let cycle_block = + if let Some(block) = self.bedrock_client.get_block_by_id(cycle_header).await? { + block + } else { + // First run can reach root easily + // so here we are optimistic about L1 + // failing to get parent. + break; + }; + + // It would be better to have id, but block does not have it, so slot will do. + info!( + "INITIAL SEARCH: Observed L1 block at slot {}", + cycle_block.header().slot().into_inner() + ); + debug!( + "INITIAL SEARCH: This block header is {}", + cycle_block.header().id() + ); + debug!( + "INITIAL SEARCH: This block parent is {}", + cycle_block.header().parent() + ); + + let (l2_block_vec, l1_header) = + parse_block_owned(&cycle_block, &self.config.channel_id); + + info!("Parsed {} L2 blocks", l2_block_vec.len()); + + if !l2_block_vec.is_empty() { + block_buffer.push_front(BackfillBlockData { + l2_blocks: l2_block_vec.clone(), + l1_header, + }); } - // Refetch stream after delay - tokio::time::sleep(std::time::Duration::from_millis( - self.config.resubscribe_interval_millis, - )) - .await; + if let Some(first_l2_block) = l2_block_vec.first() + && first_l2_block.header.block_id == 1 + { + info!("INITIAL_SEARCH: Found channel start"); + break 'outer; } + + // Step back to parent + let parent = cycle_block.header().parent(); + + if parent == backfill_limit { + break; + } + + cycle_header = parent; + } + + info!("INITIAL_SEARCH: Reached backfill limit, refetching last l1 lib header"); + + block_buffer.clear(); + backfill_limit = backfill_start; + curr_last_l1_lib_header = self.get_next_lib(curr_last_l1_lib_header).await?; + backfill_start = curr_last_l1_lib_header; } + + Ok(BackfillData { + block_data: block_buffer, + curr_fin_l1_lib_header: backfill_limit, + }) + } + + pub async fn backfill_to_last_l1_lib_header_id( + &self, + last_fin_l1_lib_header: HeaderId, + channel_id: &ChannelId, + ) -> Result { + let curr_fin_l1_lib_header = self.get_next_lib(last_fin_l1_lib_header).await?; + // ToDo: Not scalable, buffer should be stored in DB to not run out of memory + // Don't want to complicate DB even more right now. + let mut block_buffer = VecDeque::new(); + + let mut cycle_header = curr_fin_l1_lib_header; + loop { + let Some(cycle_block) = self.bedrock_client.get_block_by_id(cycle_header).await? else { + return Err(anyhow::anyhow!("Parent not found")); + }; + + if cycle_block.header().id() == last_fin_l1_lib_header { + break; + } else { + // Step back to parent + cycle_header = cycle_block.header().parent(); + } + + // It would be better to have id, but block does not have it, so slot will do. + info!( + "Observed L1 block at slot {}", + cycle_block.header().slot().into_inner() + ); + + let (l2_block_vec, l1_header) = parse_block_owned(&cycle_block, channel_id); + + info!("Parsed {} L2 blocks", l2_block_vec.len()); + + if !l2_block_vec.is_empty() { + block_buffer.push_front(BackfillBlockData { + l2_blocks: l2_block_vec, + l1_header, + }); + } + } + + Ok(BackfillData { + block_data: block_buffer, + curr_fin_l1_lib_header, + }) } } -fn parse_blocks( - block_txs: impl Iterator, +fn parse_block_owned( + l1_block: &bedrock_client::Block, decoded_channel_id: &ChannelId, -) -> impl Iterator { - block_txs.flat_map(|tx| { - tx.mantle_tx.ops.into_iter().filter_map(|op| match op { - Op::ChannelInscribe(InscriptionOp { - channel_id, - inscription, - .. - }) if channel_id == *decoded_channel_id => { - borsh::from_slice::(&inscription).ok() - } - _ => None, - }) - }) +) -> (Vec, HeaderId) { + ( + l1_block + .transactions() + .flat_map(|tx| { + tx.mantle_tx.ops.iter().filter_map(|op| match op { + Op::ChannelInscribe(InscriptionOp { + channel_id, + inscription, + .. + }) if channel_id == decoded_channel_id => { + borsh::from_slice::(inscription) + .inspect_err(|err| { + error!("Failed to deserialize our inscription with err: {err:#?}") + }) + .ok() + } + _ => None, + }) + }) + .collect(), + l1_block.header().id(), + ) } diff --git a/indexer/service/Dockerfile b/indexer/service/Dockerfile index b6a896b2..081e8b06 100644 --- a/indexer/service/Dockerfile +++ b/indexer/service/Dockerfile @@ -35,6 +35,11 @@ RUN strip /indexer_service/target/release/indexer_service # Runtime stage - minimal image FROM debian:trixie-slim +# Install runtime dependencies +RUN apt-get update \ + && apt-get install -y gosu jq \ + && rm -rf /var/lib/apt/lists/* + # Create non-root user for security RUN useradd -m -u 1000 -s /bin/bash indexer_service_user && \ mkdir -p /indexer_service /etc/indexer_service && \ @@ -43,6 +48,10 @@ RUN useradd -m -u 1000 -s /bin/bash indexer_service_user && \ # Copy binary from builder COPY --from=builder --chown=indexer_service_user:indexer_service_user /indexer_service/target/release/indexer_service /usr/local/bin/indexer_service +# Copy entrypoint script +COPY indexer/service/docker-entrypoint.sh /docker-entrypoint.sh +RUN chmod +x /docker-entrypoint.sh + # Expose default port EXPOSE 8779 @@ -60,7 +69,9 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ # Run the application ENV RUST_LOG=info -USER indexer_service_user +USER root + +ENTRYPOINT ["/docker-entrypoint.sh"] WORKDIR /indexer_service CMD ["indexer_service", "/etc/indexer_service/indexer_config.json"] diff --git a/indexer/service/configs/indexer_config.json b/indexer/service/configs/indexer_config.json index b83c4650..7d7a317c 100644 --- a/indexer/service/configs/indexer_config.json +++ b/indexer/service/configs/indexer_config.json @@ -1,11 +1,160 @@ { - "resubscribe_interval_millis": 1000, + "home": "./indexer/service", + "consensus_info_polling_interval": "1s", "bedrock_client_config": { "addr": "http://localhost:8080", "backoff": { - "start_delay_millis": 100, + "start_delay": "100ms", "max_retries": 5 } }, - "channel_id": "0101010101010101010101010101010101010101010101010101010101010101" + "channel_id": "0101010101010101010101010101010101010101010101010101010101010101", + "initial_accounts": [ + { + "account_id": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy", + "balance": 10000 + }, + { + "account_id": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw", + "balance": 20000 + } + ], + "initial_commitments": [ + { + "npk": [ + 63, + 202, + 178, + 231, + 183, + 82, + 237, + 212, + 216, + 221, + 215, + 255, + 153, + 101, + 177, + 161, + 254, + 210, + 128, + 122, + 54, + 190, + 230, + 151, + 183, + 64, + 225, + 229, + 113, + 1, + 228, + 97 + ], + "account": { + "program_owner": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "balance": 10000, + "data": [], + "nonce": 0 + } + }, + { + "npk": [ + 192, + 251, + 166, + 243, + 167, + 236, + 84, + 249, + 35, + 136, + 130, + 172, + 219, + 225, + 161, + 139, + 229, + 89, + 243, + 125, + 194, + 213, + 209, + 30, + 23, + 174, + 100, + 244, + 124, + 74, + 140, + 47 + ], + "account": { + "program_owner": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "balance": 20000, + "data": [], + "nonce": 0 + } + } + ], + "signing_key": [ + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37 + ] } diff --git a/indexer/service/docker-compose.yml b/indexer/service/docker-compose.yml index fe650a2a..73ac90ae 100644 --- a/indexer/service/docker-compose.yml +++ b/indexer/service/docker-compose.yml @@ -10,3 +10,5 @@ services: volumes: # Mount configuration - ./configs/indexer_config.json:/etc/indexer_service/indexer_config.json + # Mount data folder + - ./data:/var/lib/indexer_service diff --git a/indexer/service/docker-entrypoint.sh b/indexer/service/docker-entrypoint.sh new file mode 100644 index 00000000..d9408a05 --- /dev/null +++ b/indexer/service/docker-entrypoint.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# This is an entrypoint script for the indexer_service Docker container, +# it's not meant to be executed outside of the container. + +set -e + +CONFIG="/etc/indexer_service/indexer_service.json" + +# Check config file exists +if [ ! -f "$CONFIG" ]; then + echo "Config file not found: $CONFIG" >&2 + exit 1 +fi + +# Parse home dir +HOME_DIR=$(jq -r '.home' "$CONFIG") + +if [ -z "$HOME_DIR" ] || [ "$HOME_DIR" = "null" ]; then + echo "'home' key missing in config" >&2 + exit 1 +fi + +# Give permissions to the data directory and switch to non-root user +if [ "$(id -u)" = "0" ]; then + mkdir -p "$HOME_DIR" + chown -R indexer_service_user:indexer_service_user "$HOME_DIR" + exec gosu indexer_service_user "$@" +fi diff --git a/indexer/service/rpc/src/lib.rs b/indexer/service/rpc/src/lib.rs index ee0eab9b..a769a2a7 100644 --- a/indexer/service/rpc/src/lib.rs +++ b/indexer/service/rpc/src/lib.rs @@ -26,6 +26,9 @@ pub trait Rpc { #[subscription(name = "subscribeToFinalizedBlocks", item = BlockId)] async fn subscribe_to_finalized_blocks(&self) -> SubscriptionResult; + #[method(name = "getLastFinalizedBlockId")] + async fn get_last_finalized_block_id(&self) -> Result; + #[method(name = "getBlockById")] async fn get_block_by_id(&self, block_id: BlockId) -> Result; @@ -48,4 +51,8 @@ pub trait Rpc { limit: u32, offset: u32, ) -> Result, ErrorObjectOwned>; + + // ToDo: expand healthcheck response into some kind of report + #[method(name = "checkHealth")] + async fn healthcheck(&self) -> Result<(), ErrorObjectOwned>; } diff --git a/indexer/service/src/mock_service.rs b/indexer/service/src/mock_service.rs index e4cb816c..3343e9cc 100644 --- a/indexer/service/src/mock_service.rs +++ b/indexer/service/src/mock_service.rs @@ -180,6 +180,15 @@ impl indexer_service_rpc::RpcServer for MockIndexerService { Ok(()) } + async fn get_last_finalized_block_id(&self) -> Result { + self.blocks + .last() + .map(|bl| bl.header.block_id) + .ok_or_else(|| { + ErrorObjectOwned::owned(-32001, "Last block not found".to_string(), None::<()>) + }) + } + async fn get_block_by_id(&self, block_id: BlockId) -> Result { self.blocks .iter() @@ -268,4 +277,8 @@ impl indexer_service_rpc::RpcServer for MockIndexerService { .map(|(tx, _)| tx.clone()) .collect()) } + + async fn healthcheck(&self) -> Result<(), ErrorObjectOwned> { + Ok(()) + } } diff --git a/indexer/service/src/service.rs b/indexer/service/src/service.rs index a0356b2a..13ec2ea8 100644 --- a/indexer/service/src/service.rs +++ b/indexer/service/src/service.rs @@ -15,11 +15,6 @@ use tokio::sync::mpsc::UnboundedSender; pub struct IndexerService { subscription_service: SubscriptionService, - - #[expect( - dead_code, - reason = "Will be used in future implementations of RPC methods" - )] indexer: IndexerCore, } @@ -53,33 +48,88 @@ impl indexer_service_rpc::RpcServer for IndexerService { Ok(()) } - async fn get_block_by_id(&self, _block_id: BlockId) -> Result { - Err(not_yet_implemented_error()) + async fn get_last_finalized_block_id(&self) -> Result { + self.indexer.store.get_last_block_id().map_err(db_error) } - async fn get_block_by_hash(&self, _block_hash: HashType) -> Result { - Err(not_yet_implemented_error()) + async fn get_block_by_id(&self, block_id: BlockId) -> Result { + Ok(self + .indexer + .store + .get_block_at_id(block_id) + .map_err(db_error)? + .into()) } - async fn get_account(&self, _account_id: AccountId) -> Result { - Err(not_yet_implemented_error()) + async fn get_block_by_hash(&self, block_hash: HashType) -> Result { + Ok(self + .indexer + .store + .get_block_by_hash(block_hash.0) + .map_err(db_error)? + .into()) } - async fn get_transaction(&self, _tx_hash: HashType) -> Result { - Err(not_yet_implemented_error()) + async fn get_account(&self, account_id: AccountId) -> Result { + Ok(self + .indexer + .store + .get_account_final(&account_id.into()) + .map_err(db_error)? + .into()) } - async fn get_blocks(&self, _offset: u32, _limit: u32) -> Result, ErrorObjectOwned> { - Err(not_yet_implemented_error()) + async fn get_transaction(&self, tx_hash: HashType) -> Result { + Ok(self + .indexer + .store + .get_transaction_by_hash(tx_hash.0) + .map_err(db_error)? + .into()) + } + + async fn get_blocks(&self, offset: u32, limit: u32) -> Result, ErrorObjectOwned> { + let blocks = self + .indexer + .store + .get_block_batch(offset as u64, limit as u64) + .map_err(db_error)?; + + let mut block_res = vec![]; + + for block in blocks { + block_res.push(block.into()) + } + + Ok(block_res) } async fn get_transactions_by_account( &self, - _account_id: AccountId, - _limit: u32, - _offset: u32, + account_id: AccountId, + limit: u32, + offset: u32, ) -> Result, ErrorObjectOwned> { - Err(not_yet_implemented_error()) + let transactions = self + .indexer + .store + .get_transactions_by_account(account_id.value, offset as u64, limit as u64) + .map_err(db_error)?; + + let mut tx_res = vec![]; + + for tx in transactions { + tx_res.push(tx.into()) + } + + Ok(tx_res) + } + + async fn healthcheck(&self) -> Result<(), ErrorObjectOwned> { + // Checking, that indexer can calculate last state + let _ = self.indexer.store.final_state().map_err(db_error)?; + + Ok(()) } } @@ -219,10 +269,18 @@ impl Drop for Subscription { } } -fn not_yet_implemented_error() -> ErrorObjectOwned { +pub fn not_yet_implemented_error() -> ErrorObjectOwned { ErrorObject::owned( ErrorCode::InternalError.code(), "Not yet implemented", Option::::None, ) } + +fn db_error(err: anyhow::Error) -> ErrorObjectOwned { + ErrorObjectOwned::owned( + ErrorCode::InternalError.code(), + "DBError".to_string(), + Some(format!("{err:#?}")), + ) +} diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index 895bc7a0..ac14d183 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -12,19 +12,22 @@ sequencer_runner.workspace = true wallet.workspace = true common.workspace = true key_protocol.workspace = true -wallet-ffi.workspace = true -token_core.workspace = true indexer_service.workspace = true +serde_json.workspace = true +token_core.workspace = true +indexer_service_rpc.workspace = true +wallet-ffi.workspace = true url.workspace = true + anyhow.workspace = true env_logger.workspace = true log.workspace = true -serde_json.workspace = true base64.workspace = true tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } hex.workspace = true tempfile.workspace = true borsh.workspace = true +bytesize.workspace = true futures.workspace = true testcontainers = { version = "0.27.0", features = ["docker-compose"] } diff --git a/integration_tests/src/config.rs b/integration_tests/src/config.rs index 9d69fe22..8dd18a25 100644 --- a/integration_tests/src/config.rs +++ b/integration_tests/src/config.rs @@ -1,30 +1,38 @@ -use std::{net::SocketAddr, path::PathBuf}; +use std::{net::SocketAddr, path::PathBuf, time::Duration}; use anyhow::{Context, Result}; -use indexer_service::{BackoffConfig, BedrockClientConfig, ChannelId, IndexerConfig}; +use bytesize::ByteSize; +use common::block::{AccountInitialData, CommitmentsInitialData}; +use indexer_service::{BackoffConfig, ChannelId, ClientConfig, IndexerConfig}; use key_protocol::key_management::KeyChain; use nssa::{Account, AccountId, PrivateKey, PublicKey}; use nssa_core::{account::Data, program::DEFAULT_PROGRAM_ID}; -use sequencer_core::config::{ - AccountInitialData, BedrockConfig, CommitmentsInitialData, SequencerConfig, -}; +use sequencer_core::config::{BedrockConfig, SequencerConfig}; use url::Url; use wallet::config::{ InitialAccountData, InitialAccountDataPrivate, InitialAccountDataPublic, WalletConfig, }; -pub fn indexer_config(bedrock_addr: SocketAddr) -> Result { +pub fn indexer_config( + bedrock_addr: SocketAddr, + home: PathBuf, + initial_data: &InitialData, +) -> Result { Ok(IndexerConfig { - resubscribe_interval_millis: 1000, - bedrock_client_config: BedrockClientConfig { + home, + consensus_info_polling_interval: Duration::from_secs(1), + bedrock_client_config: ClientConfig { addr: addr_to_url(UrlProtocol::Http, bedrock_addr) .context("Failed to convert bedrock addr to URL")?, auth: None, backoff: BackoffConfig { - start_delay_millis: 100, + start_delay: Duration::from_millis(100), max_retries: 10, }, }, + initial_accounts: initial_data.sequencer_initial_accounts(), + initial_commitments: initial_data.sequencer_initial_commitments(), + signing_key: [37; 32], channel_id: bedrock_channel_id(), }) } @@ -32,16 +40,18 @@ pub fn indexer_config(bedrock_addr: SocketAddr) -> Result { /// Sequencer config options available for custom changes in integration tests. pub struct SequencerPartialConfig { pub max_num_tx_in_block: usize, + pub max_block_size: ByteSize, pub mempool_max_size: usize, - pub block_create_timeout_millis: u64, + pub block_create_timeout: Duration, } impl Default for SequencerPartialConfig { fn default() -> Self { Self { max_num_tx_in_block: 20, + max_block_size: ByteSize::mib(1), mempool_max_size: 10_000, - block_create_timeout_millis: 10_000, + block_create_timeout: Duration::from_secs(10), } } } @@ -55,8 +65,9 @@ pub fn sequencer_config( ) -> Result { let SequencerPartialConfig { max_num_tx_in_block, + max_block_size, mempool_max_size, - block_create_timeout_millis, + block_create_timeout, } = partial; Ok(SequencerConfig { @@ -65,16 +76,17 @@ pub fn sequencer_config( genesis_id: 1, is_genesis_random: true, max_num_tx_in_block, + max_block_size, mempool_max_size, - block_create_timeout_millis, - retry_pending_blocks_timeout_millis: 240_000, + block_create_timeout, + retry_pending_blocks_timeout: Duration::from_secs(120), port: 0, initial_accounts: initial_data.sequencer_initial_accounts(), initial_commitments: initial_data.sequencer_initial_commitments(), signing_key: [37; 32], bedrock_config: BedrockConfig { backoff: BackoffConfig { - start_delay_millis: 100, + start_delay: Duration::from_millis(100), max_retries: 5, }, channel_id: bedrock_channel_id(), @@ -95,7 +107,7 @@ pub fn wallet_config( override_rust_log: None, sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr) .context("Failed to convert sequencer addr to URL")?, - seq_poll_timeout_millis: 30_000, + seq_poll_timeout: Duration::from_secs(30), seq_tx_poll_max_blocks: 15, seq_poll_max_retries: 10, seq_block_poll_max_amount: 100, diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index cb940c85..f0111735 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -10,6 +10,7 @@ use indexer_service::IndexerHandle; use log::{debug, error, warn}; use nssa::{AccountId, PrivacyPreservingTransaction}; use nssa_core::Commitment; +use sequencer_core::indexer_client::{IndexerClient, IndexerClientTrait}; use sequencer_runner::SequencerHandle; use tempfile::TempDir; use testcontainers::compose::DockerCompose; @@ -20,6 +21,7 @@ pub mod config; // TODO: Remove this and control time from tests pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12; pub const NSSA_PROGRAM_FOR_TEST_DATA_CHANGER: &str = "data_changer.bin"; +pub const NSSA_PROGRAM_FOR_TEST_NOOP: &str = "noop.bin"; const BEDROCK_SERVICE_WITH_OPEN_PORT: &str = "logos-blockchain-node-0"; const BEDROCK_SERVICE_PORT: u16 = 18080; @@ -33,11 +35,13 @@ static LOGGER: LazyLock<()> = LazyLock::new(env_logger::init); // NOTE: Order of fields is important for proper drop order. pub struct TestContext { sequencer_client: SequencerClient, + indexer_client: IndexerClient, wallet: WalletCore, wallet_password: String, sequencer_handle: SequencerHandle, indexer_handle: IndexerHandle, bedrock_compose: DockerCompose, + _temp_indexer_dir: TempDir, _temp_sequencer_dir: TempDir, _temp_wallet_dir: TempDir, } @@ -63,7 +67,7 @@ impl TestContext { let (bedrock_compose, bedrock_addr) = Self::setup_bedrock_node().await?; - let indexer_handle = Self::setup_indexer(bedrock_addr) + let (indexer_handle, temp_indexer_dir) = Self::setup_indexer(bedrock_addr, &initial_data) .await .context("Failed to setup Indexer")?; @@ -83,16 +87,23 @@ impl TestContext { let sequencer_url = config::addr_to_url(config::UrlProtocol::Http, sequencer_handle.addr()) .context("Failed to convert sequencer addr to URL")?; + let indexer_url = config::addr_to_url(config::UrlProtocol::Ws, indexer_handle.addr()) + .context("Failed to convert indexer addr to URL")?; let sequencer_client = SequencerClient::new(sequencer_url).context("Failed to create sequencer client")?; + let indexer_client = IndexerClient::new(&indexer_url) + .await + .context("Failed to create indexer client")?; Ok(Self { sequencer_client, + indexer_client, wallet, wallet_password, bedrock_compose, sequencer_handle, indexer_handle, + _temp_indexer_dir: temp_indexer_dir, _temp_sequencer_dir: temp_sequencer_dir, _temp_wallet_dir: temp_wallet_dir, }) @@ -163,13 +174,26 @@ impl TestContext { Ok((compose, addr)) } - async fn setup_indexer(bedrock_addr: SocketAddr) -> Result { - let indexer_config = - config::indexer_config(bedrock_addr).context("Failed to create Indexer config")?; + async fn setup_indexer( + bedrock_addr: SocketAddr, + initial_data: &config::InitialData, + ) -> Result<(IndexerHandle, TempDir)> { + let temp_indexer_dir = + tempfile::tempdir().context("Failed to create temp dir for indexer home")?; + + debug!("Using temp indexer home at {:?}", temp_indexer_dir.path()); + + let indexer_config = config::indexer_config( + bedrock_addr, + temp_indexer_dir.path().to_owned(), + initial_data, + ) + .context("Failed to create Indexer config")?; indexer_service::run_server(indexer_config, 0) .await .context("Failed to run Indexer Service") + .map(|handle| (handle, temp_indexer_dir)) } async fn setup_sequencer( @@ -254,6 +278,11 @@ impl TestContext { &self.sequencer_client } + /// Get reference to the indexer client. + pub fn indexer_client(&self) -> &IndexerClient { + &self.indexer_client + } + /// Get existing public account IDs in the wallet. pub fn existing_public_accounts(&self) -> Vec { self.wallet @@ -279,9 +308,11 @@ impl Drop for TestContext { sequencer_handle, indexer_handle, bedrock_compose, + _temp_indexer_dir: _, _temp_sequencer_dir: _, _temp_wallet_dir: _, sequencer_client: _, + indexer_client: _, wallet: _, wallet_password: _, } = self; diff --git a/integration_tests/tests/block_size_limit.rs b/integration_tests/tests/block_size_limit.rs new file mode 100644 index 00000000..d8ee64dc --- /dev/null +++ b/integration_tests/tests/block_size_limit.rs @@ -0,0 +1,185 @@ +use std::time::Duration; + +use anyhow::Result; +use bytesize::ByteSize; +use common::{block::HashableBlockData, transaction::NSSATransaction}; +use integration_tests::{ + TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, config::SequencerPartialConfig, +}; +use nssa::program::Program; +use tokio::test; + +#[test] +async fn reject_oversized_transaction() -> Result<()> { + let ctx = TestContext::builder() + .with_sequencer_partial_config(SequencerPartialConfig { + max_num_tx_in_block: 100, + max_block_size: ByteSize::mib(1), + mempool_max_size: 1000, + block_create_timeout: Duration::from_secs(10), + }) + .build() + .await?; + + // Create a transaction that's definitely too large + // Block size is 1 MiB (1,048,576 bytes), minus ~200 bytes for header = ~1,048,376 bytes max tx + // Create a 1.1 MiB binary to ensure it exceeds the limit + let oversized_binary = vec![0u8; 1100 * 1024]; // 1.1 MiB binary + + let message = nssa::program_deployment_transaction::Message::new(oversized_binary); + let tx = nssa::ProgramDeploymentTransaction::new(message); + + // Try to submit the transaction and expect an error + let result = ctx.sequencer_client().send_tx_program(tx).await; + + assert!( + result.is_err(), + "Expected error when submitting oversized transaction" + ); + + let err = result.unwrap_err(); + let err_str = format!("{:?}", err); + + // Check if the error contains information about transaction being too large + assert!( + err_str.contains("TransactionTooLarge") || err_str.contains("too large"), + "Expected TransactionTooLarge error, got: {}", + err_str + ); + + Ok(()) +} + +#[test] +async fn accept_transaction_within_limit() -> Result<()> { + let ctx = TestContext::builder() + .with_sequencer_partial_config(SequencerPartialConfig { + max_num_tx_in_block: 100, + max_block_size: ByteSize::mib(1), + mempool_max_size: 1000, + block_create_timeout: Duration::from_secs(10), + }) + .build() + .await?; + + // Create a small program deployment that should fit + let small_binary = vec![0u8; 1024]; // 1 KiB binary + + let message = nssa::program_deployment_transaction::Message::new(small_binary); + let tx = nssa::ProgramDeploymentTransaction::new(message); + + // This should succeed + let result = ctx.sequencer_client().send_tx_program(tx).await; + + assert!( + result.is_ok(), + "Expected successful submission of small transaction, got error: {:?}", + result.as_ref().unwrap_err() + ); + + Ok(()) +} + +#[test] +async fn transaction_deferred_to_next_block_when_current_full() -> Result<()> { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let artifacts_dir = + std::path::PathBuf::from(manifest_dir).join("../artifacts/test_program_methods"); + + let burner_bytecode = std::fs::read(artifacts_dir.join("burner.bin"))?; + let chain_caller_bytecode = std::fs::read(artifacts_dir.join("chain_caller.bin"))?; + + // Calculate block size to fit only one of the two transactions, leaving some room for headers + // (e.g., 10 KiB) + let max_program_size = burner_bytecode.len().max(chain_caller_bytecode.len()); + let block_size = ByteSize::b((max_program_size + 10 * 1024) as u64); + + let ctx = TestContext::builder() + .with_sequencer_partial_config(SequencerPartialConfig { + max_num_tx_in_block: 100, + max_block_size: block_size, + mempool_max_size: 1000, + block_create_timeout: Duration::from_secs(10), + }) + .build() + .await?; + + let burner_id = Program::new(burner_bytecode.clone())?.id(); + let chain_caller_id = Program::new(chain_caller_bytecode.clone())?.id(); + + let initial_block_height = ctx.sequencer_client().get_last_block().await?.last_block; + + // Submit both program deployments + ctx.sequencer_client() + .send_tx_program(nssa::ProgramDeploymentTransaction::new( + nssa::program_deployment_transaction::Message::new(burner_bytecode), + )) + .await?; + + ctx.sequencer_client() + .send_tx_program(nssa::ProgramDeploymentTransaction::new( + nssa::program_deployment_transaction::Message::new(chain_caller_bytecode), + )) + .await?; + + // Wait for first block + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let block1_response = ctx + .sequencer_client() + .get_block(initial_block_height + 1) + .await?; + let block1: HashableBlockData = borsh::from_slice(&block1_response.block)?; + + // Check which program is in block 1 + let get_program_ids = |block: &HashableBlockData| -> Vec { + block + .transactions + .iter() + .filter_map(|tx| { + if let NSSATransaction::ProgramDeployment(deployment) = tx { + let bytecode = deployment.message.clone().into_bytecode(); + Program::new(bytecode).ok().map(|p| p.id()) + } else { + None + } + }) + .collect() + }; + + let block1_program_ids = get_program_ids(&block1); + + // First program should be in block 1, but not both due to block size limit + assert_eq!( + block1_program_ids.len(), + 1, + "Expected exactly one program deployment in block 1" + ); + assert_eq!( + block1_program_ids[0], burner_id, + "Expected burner program to be deployed in block 1" + ); + + // Wait for second block + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let block2_response = ctx + .sequencer_client() + .get_block(initial_block_height + 2) + .await?; + let block2: HashableBlockData = borsh::from_slice(&block2_response.block)?; + let block2_program_ids = get_program_ids(&block2); + + // The other program should be in block 2 + assert_eq!( + block2_program_ids.len(), + 1, + "Expected exactly one program deployment in block 2" + ); + assert_eq!( + block2_program_ids[0], chain_caller_id, + "Expected chain_caller program to be deployed in block 2" + ); + + Ok(()) +} diff --git a/integration_tests/tests/config.rs b/integration_tests/tests/config.rs index ca800d0f..ed301616 100644 --- a/integration_tests/tests/config.rs +++ b/integration_tests/tests/config.rs @@ -8,22 +8,22 @@ use wallet::cli::{Command, config::ConfigSubcommand}; async fn modify_config_field() -> Result<()> { let mut ctx = TestContext::new().await?; - let old_seq_poll_timeout_millis = ctx.wallet().config().seq_poll_timeout_millis; + let old_seq_poll_timeout = ctx.wallet().config().seq_poll_timeout; // Change config field let command = Command::Config(ConfigSubcommand::Set { - key: "seq_poll_timeout_millis".to_string(), - value: "1000".to_string(), + key: "seq_poll_timeout".to_string(), + value: "1s".to_string(), }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; - let new_seq_poll_timeout_millis = ctx.wallet().config().seq_poll_timeout_millis; - assert_eq!(new_seq_poll_timeout_millis, 1000); + let new_seq_poll_timeout = ctx.wallet().config().seq_poll_timeout; + assert_eq!(new_seq_poll_timeout, std::time::Duration::from_secs(1)); // Return how it was at the beginning let command = Command::Config(ConfigSubcommand::Set { - key: "seq_poll_timeout_millis".to_string(), - value: old_seq_poll_timeout_millis.to_string(), + key: "seq_poll_timeout".to_string(), + value: format!("{:?}", old_seq_poll_timeout), }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; diff --git a/integration_tests/tests/indexer.rs b/integration_tests/tests/indexer.rs new file mode 100644 index 00000000..d5207a41 --- /dev/null +++ b/integration_tests/tests/indexer.rs @@ -0,0 +1,177 @@ +use std::time::Duration; + +use anyhow::{Context, Result}; +use indexer_service_rpc::RpcClient; +use integration_tests::{ + TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_private_account_id, + format_public_account_id, verify_commitment_is_in_state, +}; +use log::info; +use nssa::AccountId; +use tokio::test; +use wallet::cli::{Command, programs::native_token_transfer::AuthTransferSubcommand}; + +/// Timeout in milliseconds to reliably await for block finalization +const L2_TO_L1_TIMEOUT_MILLIS: u64 = 600000; + +#[test] +async fn indexer_test_run() -> Result<()> { + let ctx = TestContext::new().await?; + + // RUN OBSERVATION + tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await; + + let last_block_seq = ctx + .sequencer_client() + .get_last_block() + .await + .unwrap() + .last_block; + + info!("Last block on seq now is {last_block_seq}"); + + let last_block_indexer = ctx + .indexer_client() + .get_last_finalized_block_id() + .await + .unwrap(); + + info!("Last block on ind now is {last_block_indexer}"); + + assert!(last_block_indexer > 1); + + Ok(()) +} + +#[test] +async fn indexer_block_batching() -> Result<()> { + let ctx = TestContext::new().await?; + + // WAIT + info!("Waiting for indexer to parse blocks"); + tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await; + + let last_block_indexer = ctx + .indexer_client() + .get_last_finalized_block_id() + .await + .unwrap(); + + info!("Last block on ind now is {last_block_indexer}"); + + assert!(last_block_indexer > 1); + + // Getting wide batch to fit all blocks + let block_batch = ctx.indexer_client().get_blocks(1, 100).await.unwrap(); + + // Checking chain consistency + let mut prev_block_hash = block_batch.first().unwrap().header.hash; + + for block in &block_batch[1..] { + assert_eq!(block.header.prev_block_hash, prev_block_hash); + + info!("Block {} chain-consistent", block.header.block_id); + + prev_block_hash = block.header.hash; + } + + Ok(()) +} + +#[test] +async fn indexer_state_consistency() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_public_account_id(ctx.existing_public_accounts()[0]), + to: Some(format_public_account_id(ctx.existing_public_accounts()[1])), + to_npk: None, + to_vpk: None, + amount: 100, + }); + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + info!("Checking correct balance move"); + let acc_1_balance = ctx + .sequencer_client() + .get_account_balance(ctx.existing_public_accounts()[0]) + .await?; + let acc_2_balance = ctx + .sequencer_client() + .get_account_balance(ctx.existing_public_accounts()[1]) + .await?; + + info!("Balance of sender: {acc_1_balance:#?}"); + info!("Balance of receiver: {acc_2_balance:#?}"); + + assert_eq!(acc_1_balance.balance, 9900); + assert_eq!(acc_2_balance.balance, 20100); + + let from: AccountId = ctx.existing_private_accounts()[0]; + let to: AccountId = ctx.existing_private_accounts()[1]; + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_private_account_id(from), + to: Some(format_private_account_id(to)), + to_npk: None, + to_vpk: None, + amount: 100, + }); + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment1 = ctx + .wallet() + .get_private_account_commitment(from) + .context("Failed to get private account commitment for sender")?; + assert!(verify_commitment_is_in_state(new_commitment1, ctx.sequencer_client()).await); + + let new_commitment2 = ctx + .wallet() + .get_private_account_commitment(to) + .context("Failed to get private account commitment for receiver")?; + assert!(verify_commitment_is_in_state(new_commitment2, ctx.sequencer_client()).await); + + info!("Successfully transferred privately to owned account"); + + // WAIT + info!("Waiting for indexer to parse blocks"); + tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await; + + let acc1_ind_state = ctx + .indexer_client() + .get_account(ctx.existing_public_accounts()[0].into()) + .await + .unwrap(); + let acc2_ind_state = ctx + .indexer_client() + .get_account(ctx.existing_public_accounts()[1].into()) + .await + .unwrap(); + + info!("Checking correct state transition"); + let acc1_seq_state = ctx + .sequencer_client() + .get_account(ctx.existing_public_accounts()[0]) + .await? + .account; + let acc2_seq_state = ctx + .sequencer_client() + .get_account(ctx.existing_public_accounts()[1]) + .await? + .account; + + assert_eq!(acc1_ind_state, acc1_seq_state.into()); + assert_eq!(acc2_ind_state, acc2_seq_state.into()); + + // ToDo: Check private state transition + + Ok(()) +} diff --git a/integration_tests/tests/tps.rs b/integration_tests/tests/tps.rs index 2c58721e..12669f90 100644 --- a/integration_tests/tests/tps.rs +++ b/integration_tests/tests/tps.rs @@ -1,6 +1,7 @@ use std::time::{Duration, Instant}; use anyhow::Result; +use bytesize::ByteSize; use integration_tests::{ TestContext, config::{InitialData, SequencerPartialConfig}, @@ -178,8 +179,9 @@ impl TpsTestManager { fn generate_sequencer_partial_config() -> SequencerPartialConfig { SequencerPartialConfig { max_num_tx_in_block: 300, + max_block_size: ByteSize::mb(500), mempool_max_size: 10_000, - block_create_timeout_millis: 12_000, + block_create_timeout: Duration::from_secs(12), } } } diff --git a/mempool/src/lib.rs b/mempool/src/lib.rs index ff6163ff..fae52b3e 100644 --- a/mempool/src/lib.rs +++ b/mempool/src/lib.rs @@ -2,13 +2,17 @@ use tokio::sync::mpsc::{Receiver, Sender}; pub struct MemPool { receiver: Receiver, + front_buffer: Vec, } impl MemPool { pub fn new(max_size: usize) -> (Self, MemPoolHandle) { let (sender, receiver) = tokio::sync::mpsc::channel(max_size); - let mem_pool = Self { receiver }; + let mem_pool = Self { + receiver, + front_buffer: Vec::new(), + }; let sender = MemPoolHandle::new(sender); (mem_pool, sender) } @@ -16,6 +20,13 @@ impl MemPool { pub fn pop(&mut self) -> Option { use tokio::sync::mpsc::error::TryRecvError; + // First check if there are any items in the front buffer (LIFO) + if let Some(item) = self.front_buffer.pop() { + return Some(item); + } + + // Otherwise, try to receive from the channel (FIFO) + match self.receiver.try_recv() { Ok(item) => Some(item), Err(TryRecvError::Empty) => None, @@ -24,6 +35,11 @@ impl MemPool { } } } + + /// Push an item to the front of the mempool (will be popped first) + pub fn push_front(&mut self, item: T) { + self.front_buffer.push(item); + } } pub struct MemPoolHandle { @@ -96,4 +112,24 @@ mod tests { assert_eq!(pool.pop(), Some(1)); assert_eq!(pool.pop(), Some(2)); } + + #[test] + async fn test_push_front() { + let (mut pool, handle) = MemPool::new(10); + + handle.push(1).await.unwrap(); + handle.push(2).await.unwrap(); + + // Push items to the front - these should be popped first + pool.push_front(10); + pool.push_front(20); + + // Items pushed to front are popped in LIFO order + assert_eq!(pool.pop(), Some(20)); + assert_eq!(pool.pop(), Some(10)); + // Original items are then popped in FIFO order + assert_eq!(pool.pop(), Some(1)); + assert_eq!(pool.pop(), Some(2)); + assert_eq!(pool.pop(), None); + } } diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index 93f2a4a8..3b6b430f 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -11,6 +11,7 @@ serde.workspace = true serde_with.workspace = true thiserror.workspace = true bytemuck.workspace = true +bytesize.workspace = true base58.workspace = true k256 = { workspace = true, optional = true } chacha20 = { version = "0.9", default-features = false } diff --git a/nssa/core/src/account/data.rs b/nssa/core/src/account/data.rs index 396bbe6e..91c58516 100644 --- a/nssa/core/src/account/data.rs +++ b/nssa/core/src/account/data.rs @@ -1,9 +1,10 @@ use std::ops::Deref; use borsh::{BorshDeserialize, BorshSerialize}; +use bytesize::ByteSize; use serde::{Deserialize, Serialize}; -pub const DATA_MAX_LENGTH_IN_BYTES: usize = 100 * 1024; // 100 KiB +pub const DATA_MAX_LENGTH: ByteSize = ByteSize::kib(100); #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, BorshSerialize)] pub struct Data(Vec); @@ -22,7 +23,7 @@ impl Data { let mut u32_bytes = [0u8; 4]; cursor.read_exact(&mut u32_bytes)?; let data_length = u32::from_le_bytes(u32_bytes); - if data_length as usize > DATA_MAX_LENGTH_IN_BYTES { + if data_length as usize > DATA_MAX_LENGTH.as_u64() as usize { return Err( std::io::Error::new(std::io::ErrorKind::InvalidData, DataTooBigError).into(), ); @@ -35,7 +36,7 @@ impl Data { } #[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)] -#[error("data length exceeds maximum allowed length of {DATA_MAX_LENGTH_IN_BYTES} bytes")] +#[error("data length exceeds maximum allowed length of {} bytes", DATA_MAX_LENGTH.as_u64())] pub struct DataTooBigError; impl From for Vec { @@ -48,7 +49,7 @@ impl TryFrom> for Data { type Error = DataTooBigError; fn try_from(value: Vec) -> Result { - if value.len() > DATA_MAX_LENGTH_IN_BYTES { + if value.len() > DATA_MAX_LENGTH.as_u64() as usize { Err(DataTooBigError) } else { Ok(Self(value)) @@ -78,7 +79,7 @@ impl<'de> Deserialize<'de> for Data { /// Data deserialization visitor. /// /// Compared to a simple deserialization into a `Vec`, this visitor enforces - /// early length check defined by [`DATA_MAX_LENGTH_IN_BYTES`]. + /// early length check defined by [`DATA_MAX_LENGTH`]. struct DataVisitor; impl<'de> serde::de::Visitor<'de> for DataVisitor { @@ -88,7 +89,7 @@ impl<'de> Deserialize<'de> for Data { write!( formatter, "a byte array with length not exceeding {} bytes", - DATA_MAX_LENGTH_IN_BYTES + DATA_MAX_LENGTH.as_u64() ) } @@ -96,11 +97,14 @@ impl<'de> Deserialize<'de> for Data { where A: serde::de::SeqAccess<'de>, { - let mut vec = - Vec::with_capacity(seq.size_hint().unwrap_or(0).min(DATA_MAX_LENGTH_IN_BYTES)); + let mut vec = Vec::with_capacity( + seq.size_hint() + .unwrap_or(0) + .min(DATA_MAX_LENGTH.as_u64() as usize), + ); while let Some(value) = seq.next_element()? { - if vec.len() >= DATA_MAX_LENGTH_IN_BYTES { + if vec.len() >= DATA_MAX_LENGTH.as_u64() as usize { return Err(serde::de::Error::custom(DataTooBigError)); } vec.push(value); @@ -121,7 +125,7 @@ impl BorshDeserialize for Data { let len = u32::deserialize_reader(reader)?; match len { 0 => Ok(Self::default()), - len if len as usize > DATA_MAX_LENGTH_IN_BYTES => Err(std::io::Error::new( + len if len as usize > DATA_MAX_LENGTH.as_u64() as usize => Err(std::io::Error::new( std::io::ErrorKind::InvalidData, DataTooBigError, )), @@ -140,21 +144,21 @@ mod tests { #[test] fn test_data_max_length_allowed() { - let max_vec = vec![0u8; DATA_MAX_LENGTH_IN_BYTES]; + let max_vec = vec![0u8; DATA_MAX_LENGTH.as_u64() as usize]; let result = Data::try_from(max_vec); assert!(result.is_ok()); } #[test] fn test_data_too_big_error() { - let big_vec = vec![0u8; DATA_MAX_LENGTH_IN_BYTES + 1]; + let big_vec = vec![0u8; DATA_MAX_LENGTH.as_u64() as usize + 1]; let result = Data::try_from(big_vec); assert!(matches!(result, Err(DataTooBigError))); } #[test] fn test_borsh_deserialize_exceeding_limit_error() { - let too_big_data = vec![0u8; DATA_MAX_LENGTH_IN_BYTES + 1]; + let too_big_data = vec![0u8; DATA_MAX_LENGTH.as_u64() as usize + 1]; let mut serialized = Vec::new(); <_ as BorshSerialize>::serialize(&too_big_data, &mut serialized).unwrap(); @@ -164,7 +168,7 @@ mod tests { #[test] fn test_json_deserialize_exceeding_limit_error() { - let data = vec![0u8; DATA_MAX_LENGTH_IN_BYTES + 1]; + let data = vec![0u8; DATA_MAX_LENGTH.as_u64() as usize + 1]; let json = serde_json::to_string(&data).unwrap(); let result: Result = serde_json::from_str(&json); diff --git a/nssa/src/merkle_tree/mod.rs b/nssa/src/merkle_tree/mod.rs index b3637b13..9c981b62 100644 --- a/nssa/src/merkle_tree/mod.rs +++ b/nssa/src/merkle_tree/mod.rs @@ -21,7 +21,7 @@ fn hash_value(value: &Value) -> Node { } #[cfg_attr(test, derive(Debug, PartialEq, Eq))] -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Clone, BorshSerialize, BorshDeserialize)] pub struct MerkleTree { nodes: Vec, capacity: usize, diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index 9a28badd..98e0be24 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -7,7 +7,7 @@ use nssa_core::{ account::AccountWithMetadata, program::{ChainedCall, InstructionData, ProgramId, ProgramOutput}, }; -use risc0_zkvm::{ExecutorEnv, InnerReceipt, Receipt, default_prover}; +use risc0_zkvm::{ExecutorEnv, InnerReceipt, ProverOpts, Receipt, default_prover}; use crate::{ error::NssaError, @@ -126,8 +126,9 @@ pub fn execute_and_prove( env_builder.write(&circuit_input).unwrap(); let env = env_builder.build().unwrap(); let prover = default_prover(); + let opts = ProverOpts::succinct(); let prove_info = prover - .prove(env, PRIVACY_PRESERVING_CIRCUIT_ELF) + .prove_with_opts(env, PRIVACY_PRESERVING_CIRCUIT_ELF, &opts) .map_err(|e| NssaError::CircuitProvingError(e.to_string()))?; let proof = Proof(borsh::to_vec(&prove_info.receipt.inner)?); diff --git a/nssa/src/privacy_preserving_transaction/transaction.rs b/nssa/src/privacy_preserving_transaction/transaction.rs index 1020f305..9e0ca30a 100644 --- a/nssa/src/privacy_preserving_transaction/transaction.rs +++ b/nssa/src/privacy_preserving_transaction/transaction.rs @@ -146,6 +146,16 @@ impl PrivacyPreservingTransaction { .map(|(_, public_key)| AccountId::from(public_key)) .collect() } + + pub fn affected_public_account_ids(&self) -> Vec { + let mut acc_set = self + .signer_account_ids() + .into_iter() + .collect::>(); + acc_set.extend(&self.message.public_account_ids); + + acc_set.into_iter().collect() + } } fn check_privacy_preserving_circuit_proof_is_valid( diff --git a/nssa/src/program_deployment_transaction/transaction.rs b/nssa/src/program_deployment_transaction/transaction.rs index 188b73ea..8e77bfe0 100644 --- a/nssa/src/program_deployment_transaction/transaction.rs +++ b/nssa/src/program_deployment_transaction/transaction.rs @@ -1,4 +1,5 @@ use borsh::{BorshDeserialize, BorshSerialize}; +use nssa_core::account::AccountId; use sha2::{Digest as _, digest::FixedOutput as _}; use crate::{ @@ -38,4 +39,8 @@ impl ProgramDeploymentTransaction { hasher.update(&bytes); hasher.finalize_fixed().into() } + + pub fn affected_public_account_ids(&self) -> Vec { + vec![] + } } diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index f5fcc0fb..06c61987 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -45,6 +45,16 @@ impl PublicTransaction { .collect() } + pub fn affected_public_account_ids(&self) -> Vec { + let mut acc_set = self + .signer_account_ids() + .into_iter() + .collect::>(); + acc_set.extend(&self.message.account_ids); + + acc_set.into_iter().collect() + } + pub fn hash(&self) -> [u8; 32] { let bytes = self.to_bytes(); let mut hasher = sha2::Sha256::new(); diff --git a/nssa/src/state.rs b/nssa/src/state.rs index f5ec2b46..1e347552 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -16,7 +16,7 @@ use crate::{ pub const MAX_NUMBER_CHAINED_CALLS: usize = 10; -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Clone, BorshSerialize, BorshDeserialize)] #[cfg_attr(test, derive(Debug, PartialEq, Eq))] pub(crate) struct CommitmentSet { merkle_tree: MerkleTree, @@ -64,6 +64,7 @@ impl CommitmentSet { } #[cfg_attr(test, derive(Debug, PartialEq, Eq))] +#[derive(Clone)] struct NullifierSet(BTreeSet); impl NullifierSet { @@ -104,7 +105,7 @@ impl BorshDeserialize for NullifierSet { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Clone, BorshSerialize, BorshDeserialize)] #[cfg_attr(test, derive(Debug, PartialEq, Eq))] pub struct V02State { public_state: HashMap, @@ -1331,7 +1332,8 @@ pub mod tests { AccountId::new([0; 32]), ); - let large_data: Vec = vec![0; nssa_core::account::data::DATA_MAX_LENGTH_IN_BYTES + 1]; + let large_data: Vec = + vec![0; nssa_core::account::data::DATA_MAX_LENGTH.as_u64() as usize + 1]; let result = execute_and_prove( vec![public_account], diff --git a/sequencer_core/Cargo.toml b/sequencer_core/Cargo.toml index 870e22df..e939c7ae 100644 --- a/sequencer_core/Cargo.toml +++ b/sequencer_core/Cargo.toml @@ -16,6 +16,7 @@ base58.workspace = true anyhow.workspace = true serde.workspace = true serde_json.workspace = true +humantime-serde.workspace = true tempfile.workspace = true chrono.workspace = true log.workspace = true @@ -24,6 +25,7 @@ logos-blockchain-key-management-system-service.workspace = true logos-blockchain-core.workspace = true rand.workspace = true borsh.workspace = true +bytesize.workspace = true url.workspace = true jsonrpsee = { workspace = true, features = ["ws-client"] } diff --git a/sequencer_core/src/block_settlement_client.rs b/sequencer_core/src/block_settlement_client.rs index 16afa9c1..075ebd11 100644 --- a/sequencer_core/src/block_settlement_client.rs +++ b/sequencer_core/src/block_settlement_client.rs @@ -28,6 +28,11 @@ pub trait BlockSettlementClientTrait: Clone { /// Create and sign a transaction for inscribing data. fn create_inscribe_tx(&self, block: &Block) -> Result<(SignedMantleTx, MsgId)> { let inscription_data = borsh::to_vec(block)?; + log::info!( + "The size of the block {} is {} bytes", + block.header.block_id, + inscription_data.len() + ); let verifying_key_bytes = self.bedrock_signing_key().public_key().to_bytes(); let verifying_key = Ed25519PublicKey::from_bytes(&verifying_key_bytes).expect("valid ed25519 public key"); @@ -90,11 +95,17 @@ impl BlockSettlementClientTrait for BlockSettlementClient { } async fn submit_inscribe_tx_to_bedrock(&self, tx: SignedMantleTx) -> Result<()> { + let (parent_id, msg_id) = match tx.mantle_tx.ops.first() { + Some(Op::ChannelInscribe(inscribe)) => (inscribe.parent, inscribe.id()), + _ => panic!("Expected ChannelInscribe op"), + }; self.bedrock_client .post_transaction(tx) .await .context("Failed to post transaction to Bedrock")?; + log::info!("Posted block to Bedrock with parent id {parent_id:?} and msg id: {msg_id:?}"); + Ok(()) } diff --git a/sequencer_core/src/block_store.rs b/sequencer_core/src/block_store.rs index 8c06d992..bfa3be99 100644 --- a/sequencer_core/src/block_store.rs +++ b/sequencer_core/src/block_store.rs @@ -7,7 +7,7 @@ use common::{ transaction::NSSATransaction, }; use nssa::V02State; -use storage::RocksDBIO; +use storage::sequencer::RocksDBIO; pub struct SequencerStore { dbio: RocksDBIO, diff --git a/sequencer_core/src/config.rs b/sequencer_core/src/config.rs index 3f285bf3..003b82e8 100644 --- a/sequencer_core/src/config.rs +++ b/sequencer_core/src/config.rs @@ -2,30 +2,21 @@ use std::{ fs::File, io::BufReader, path::{Path, PathBuf}, + time::Duration, }; use anyhow::Result; -pub use bedrock_client::BackoffConfig; -use common::config::BasicAuth; +use bedrock_client::BackoffConfig; +use bytesize::ByteSize; +use common::{ + block::{AccountInitialData, CommitmentsInitialData}, + config::BasicAuth, +}; +use humantime_serde; use logos_blockchain_core::mantle::ops::channel::ChannelId; -use nssa::AccountId; use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Debug, Serialize, Deserialize, Clone)] -/// Helperstruct for account serialization -pub struct AccountInitialData { - pub account_id: AccountId, - pub balance: u128, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -/// Helperstruct to initialize commitments -pub struct CommitmentsInitialData { - pub npk: nssa_core::NullifierPublicKey, - pub account: nssa_core::account::Account, -} - // TODO: Provide default values #[derive(Clone, Serialize, Deserialize)] pub struct SequencerConfig { @@ -39,12 +30,17 @@ pub struct SequencerConfig { pub is_genesis_random: bool, /// Maximum number of transactions in block pub max_num_tx_in_block: usize, + /// Maximum block size (includes header and transactions) + #[serde(default = "default_max_block_size")] + pub max_block_size: ByteSize, /// Mempool maximum size pub mempool_max_size: usize, /// Interval in which blocks produced - pub block_create_timeout_millis: u64, + #[serde(with = "humantime_serde")] + pub block_create_timeout: Duration, /// Interval in which pending blocks are retried - pub retry_pending_blocks_timeout_millis: u64, + #[serde(with = "humantime_serde")] + pub retry_pending_blocks_timeout: Duration, /// Port to listen pub port: u16, /// List of initial accounts data @@ -80,3 +76,7 @@ impl SequencerConfig { Ok(serde_json::from_reader(reader)?) } } + +fn default_max_block_size() -> ByteSize { + ByteSize::mib(1) +} diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index cad7e20e..083728bf 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -1,4 +1,4 @@ -use std::{fmt::Display, path::Path, time::Instant}; +use std::{path::Path, time::Instant}; use anyhow::{Context as _, Result, anyhow}; use bedrock_client::SignedMantleTx; @@ -13,7 +13,6 @@ use config::SequencerConfig; use log::{error, info, warn}; use logos_blockchain_key_management_system_service::keys::{ED25519_SECRET_KEY_SIZE, Ed25519Key}; use mempool::{MemPool, MemPoolHandle}; -use serde::{Deserialize, Serialize}; use crate::{ block_settlement_client::{BlockSettlementClient, BlockSettlementClientTrait, MsgId}, @@ -44,20 +43,6 @@ pub struct SequencerCore< indexer_client: IC, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub enum TransactionMalformationError { - InvalidSignature, - FailedToDecode { tx: HashType }, -} - -impl Display for TransactionMalformationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:#?}") - } -} - -impl std::error::Error for TransactionMalformationError {} - impl SequencerCore { /// Starts the sequencer using the provided configuration. /// If an existing database is found, the sequencer state is loaded from it and @@ -177,9 +162,19 @@ impl SequencerCore Result { - let (_tx, _msg_id) = self + let (tx, _msg_id) = self .produce_new_block_with_mempool_transactions() .context("Failed to produce new block with mempool transactions")?; + match self + .block_settlement_client + .submit_inscribe_tx_to_bedrock(tx) + .await + { + Ok(()) => {} + Err(err) => { + error!("Failed to post block data to Bedrock with error: {err:#}"); + } + } Ok(self.chain_height) } @@ -194,13 +189,49 @@ impl SequencerCore max_block_size { + // Block would exceed size limit, remove last transaction and push back + warn!( + "Transaction with hash {tx_hash} deferred to next block: \ + block size {block_size} bytes would exceed limit of {max_block_size} bytes", + ); + + self.mempool.push_front(tx); + break; + } + match self.execute_check_transaction_on_state(tx) { Ok(valid_tx) => { - info!("Validated transaction with hash {tx_hash}, including it in block",); valid_transactions.push(valid_tx); + info!("Validated transaction with hash {tx_hash}, including it in block"); + if valid_transactions.len() >= self.sequencer_config.max_num_tx_in_block { break; } @@ -214,13 +245,6 @@ impl SequencerCore SequencerCore Result { - // Stateless checks here - match tx { - NSSATransaction::Public(tx) => { - if tx.witness_set().is_valid_for(tx.message()) { - Ok(NSSATransaction::Public(tx)) - } else { - Err(TransactionMalformationError::InvalidSignature) - } - } - NSSATransaction::PrivacyPreserving(tx) => { - if tx.witness_set().signatures_are_valid_for(tx.message()) { - Ok(NSSATransaction::PrivacyPreserving(tx)) - } else { - Err(TransactionMalformationError::InvalidSignature) - } - } - NSSATransaction::ProgramDeployment(tx) => Ok(NSSATransaction::ProgramDeployment(tx)), - } -} - /// Load signing key from file or generate a new one if it doesn't exist fn load_or_create_signing_key(path: &Path) -> Result { if path.exists() { @@ -360,19 +360,21 @@ fn load_or_create_signing_key(path: &Path) -> Result { #[cfg(all(test, feature = "mock"))] mod tests { - use std::{pin::pin, str::FromStr as _}; + use std::{pin::pin, str::FromStr as _, time::Duration}; use base58::ToBase58; use bedrock_client::BackoffConfig; - use common::{test_utils::sequencer_sign_key_for_testing, transaction::NSSATransaction}; + use common::{ + block::AccountInitialData, test_utils::sequencer_sign_key_for_testing, + transaction::NSSATransaction, + }; use logos_blockchain_core::mantle::ops::channel::ChannelId; use mempool::MemPoolHandle; use nssa::{AccountId, PrivateKey}; use crate::{ - config::{AccountInitialData, BedrockConfig, SequencerConfig}, + config::{BedrockConfig, SequencerConfig}, mock::SequencerCoreWithMockClients, - transaction_pre_check, }; fn setup_sequencer_config_variable_initial_accounts( @@ -387,22 +389,23 @@ mod tests { genesis_id: 1, is_genesis_random: false, max_num_tx_in_block: 10, + max_block_size: bytesize::ByteSize::mib(1), mempool_max_size: 10000, - block_create_timeout_millis: 1000, + block_create_timeout: Duration::from_secs(1), port: 8080, initial_accounts, initial_commitments: vec![], signing_key: *sequencer_sign_key_for_testing().value(), bedrock_config: BedrockConfig { backoff: BackoffConfig { - start_delay_millis: 100, + start_delay: Duration::from_millis(100), max_retries: 5, }, channel_id: ChannelId::from([0; 32]), node_url: "http://not-used-in-unit-tests".parse().unwrap(), auth: None, }, - retry_pending_blocks_timeout_millis: 1000 * 60 * 4, + retry_pending_blocks_timeout: Duration::from_secs(60 * 4), indexer_rpc_url: "ws://localhost:8779".parse().unwrap(), } } @@ -526,7 +529,7 @@ mod tests { #[test] fn test_transaction_pre_check_pass() { let tx = common::test_utils::produce_dummy_empty_transaction(); - let result = transaction_pre_check(tx); + let result = tx.transaction_stateless_check(); assert!(result.is_ok()); } @@ -543,7 +546,7 @@ mod tests { let tx = common::test_utils::create_transaction_native_token_transfer( acc1, 0, acc2, 10, sign_key1, ); - let result = transaction_pre_check(tx); + let result = tx.transaction_stateless_check(); assert!(result.is_ok()); } @@ -562,7 +565,7 @@ mod tests { ); // Signature is valid, stateless check pass - let tx = transaction_pre_check(tx).unwrap(); + let tx = tx.transaction_stateless_check().unwrap(); // Signature is not from sender. Execution fails let result = sequencer.execute_check_transaction_on_state(tx); @@ -586,7 +589,7 @@ mod tests { acc1, 0, acc2, 10000000, sign_key1, ); - let result = transaction_pre_check(tx); + let result = tx.transaction_stateless_check(); // Passed pre-check assert!(result.is_ok()); diff --git a/sequencer_rpc/Cargo.toml b/sequencer_rpc/Cargo.toml index 210f3077..42aa978f 100644 --- a/sequencer_rpc/Cargo.toml +++ b/sequencer_rpc/Cargo.toml @@ -8,7 +8,8 @@ license = { workspace = true } nssa.workspace = true common.workspace = true mempool.workspace = true -sequencer_core.workspace = true +sequencer_core = { workspace = true } +bedrock_client.workspace = true anyhow.workspace = true serde_json.workspace = true @@ -24,6 +25,7 @@ itertools.workspace = true actix-web.workspace = true tokio.workspace = true borsh.workspace = true +bytesize.workspace = true [dev-dependencies] sequencer_core = { workspace = true, features = ["mock"] } diff --git a/sequencer_rpc/src/lib.rs b/sequencer_rpc/src/lib.rs index ac92ff45..074ea284 100644 --- a/sequencer_rpc/src/lib.rs +++ b/sequencer_rpc/src/lib.rs @@ -28,6 +28,7 @@ pub struct JsonHandler< > { sequencer_state: Arc>>, mempool_handle: MemPoolHandle, + max_block_size: usize, } fn respond(val: T) -> Result { diff --git a/sequencer_rpc/src/net_utils.rs b/sequencer_rpc/src/net_utils.rs index ee9f6aa1..a15cabec 100644 --- a/sequencer_rpc/src/net_utils.rs +++ b/sequencer_rpc/src/net_utils.rs @@ -52,7 +52,7 @@ fn get_cors(cors_allowed_origins: &[String]) -> Cors { .max_age(3600) } -pub fn new_http_server( +pub async fn new_http_server( config: RpcConfig, seuquencer_core: Arc>, mempool_handle: MemPoolHandle, @@ -63,9 +63,16 @@ pub fn new_http_server( limits_config, } = config; info!(target:NETWORK, "Starting HTTP server at {addr}"); + let max_block_size = seuquencer_core + .lock() + .await + .sequencer_config() + .max_block_size + .as_u64() as usize; let handler = web::Data::new(JsonHandler { sequencer_state: seuquencer_core.clone(), mempool_handle, + max_block_size, }); // HTTP server @@ -73,7 +80,10 @@ pub fn new_http_server( App::new() .wrap(get_cors(&cors_allowed_origins)) .app_data(handler.clone()) - .app_data(web::JsonConfig::default().limit(limits_config.json_payload_max_size)) + .app_data( + web::JsonConfig::default() + .limit(limits_config.json_payload_max_size.as_u64() as usize), + ) .wrap(middleware::Logger::default()) .service(web::resource("/").route(web::post().to(rpc_handler::))) }) diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 7185ed9c..b3dca691 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use actix_web::Error as HttpError; use base64::{Engine, engine::general_purpose}; use common::{ - block::HashableBlockData, + block::{AccountInitialData, HashableBlockData}, rpc_primitives::{ errors::RpcError, message::{Message, Request}, @@ -20,14 +20,13 @@ use common::{ SendTxResponse, }, }, - transaction::NSSATransaction, + transaction::{NSSATransaction, TransactionMalformationError}, }; use itertools::Itertools as _; use log::warn; use nssa::{self, program::Program}; use sequencer_core::{ - block_settlement_client::BlockSettlementClientTrait, config::AccountInitialData, - indexer_client::IndexerClientTrait, + block_settlement_client::BlockSettlementClientTrait, indexer_client::IndexerClientTrait, }; use serde_json::Value; @@ -95,7 +94,25 @@ impl JsonHandler let tx = borsh::from_slice::(&send_tx_req.transaction).unwrap(); let tx_hash = tx.hash(); - let authenticated_tx = sequencer_core::transaction_pre_check(tx) + // Check transaction size against block size limit + // Reserve ~200 bytes for block header overhead + const BLOCK_HEADER_OVERHEAD: usize = 200; + let tx_size = borsh::to_vec(&tx) + .map_err(|_| TransactionMalformationError::FailedToDecode { tx: tx_hash })? + .len(); + + let max_tx_size = self.max_block_size.saturating_sub(BLOCK_HEADER_OVERHEAD); + + if tx_size > max_tx_size { + return Err(TransactionMalformationError::TransactionTooLarge { + size: tx_size, + max: max_tx_size, + } + .into()); + } + + let authenticated_tx = tx + .transaction_stateless_check() .inspect_err(|err| warn!("Error at pre_check {err:#?}"))?; // TODO: Do we need a timeout here? It will be usable if we have too many transactions to @@ -323,16 +340,18 @@ impl JsonHandler #[cfg(test)] mod tests { - use std::{str::FromStr as _, sync::Arc}; + use std::{str::FromStr as _, sync::Arc, time::Duration}; use base58::ToBase58; use base64::{Engine, engine::general_purpose}; + use bedrock_client::BackoffConfig; use common::{ - config::BasicAuth, test_utils::sequencer_sign_key_for_testing, transaction::NSSATransaction, + block::AccountInitialData, config::BasicAuth, test_utils::sequencer_sign_key_for_testing, + transaction::NSSATransaction, }; use nssa::AccountId; use sequencer_core::{ - config::{AccountInitialData, BackoffConfig, BedrockConfig, SequencerConfig}, + config::{BedrockConfig, SequencerConfig}, mock::{MockBlockSettlementClient, MockIndexerClient, SequencerCoreWithMockClients}, }; use serde_json::Value; @@ -375,16 +394,17 @@ mod tests { genesis_id: 1, is_genesis_random: false, max_num_tx_in_block: 10, + max_block_size: bytesize::ByteSize::mib(1), mempool_max_size: 1000, - block_create_timeout_millis: 1000, + block_create_timeout: Duration::from_secs(1), port: 8080, initial_accounts, initial_commitments: vec![], signing_key: *sequencer_sign_key_for_testing().value(), - retry_pending_blocks_timeout_millis: 1000 * 60 * 4, + retry_pending_blocks_timeout: Duration::from_secs(60 * 4), bedrock_config: BedrockConfig { backoff: BackoffConfig { - start_delay_millis: 100, + start_delay: Duration::from_millis(100), max_retries: 5, }, channel_id: [42; 32].into(), @@ -435,12 +455,14 @@ mod tests { .produce_new_block_with_mempool_transactions() .unwrap(); + let max_block_size = sequencer_core.sequencer_config().max_block_size.as_u64() as usize; let sequencer_core = Arc::new(Mutex::new(sequencer_core)); ( JsonHandlerWithMockClients { sequencer_state: sequencer_core, mempool_handle, + max_block_size, }, initial_accounts, tx, diff --git a/sequencer_rpc/src/types/err_rpc.rs b/sequencer_rpc/src/types/err_rpc.rs index 14807d5f..92214c54 100644 --- a/sequencer_rpc/src/types/err_rpc.rs +++ b/sequencer_rpc/src/types/err_rpc.rs @@ -1,6 +1,8 @@ -use common::rpc_primitives::errors::{RpcError, RpcParseError}; +use common::{ + rpc_primitives::errors::{RpcError, RpcParseError}, + transaction::TransactionMalformationError, +}; use log::debug; -use sequencer_core::TransactionMalformationError; pub struct RpcErr(pub RpcError); @@ -42,10 +44,7 @@ impl RpcErrKind for RpcErrInternal { impl RpcErrKind for TransactionMalformationError { fn into_rpc_err(self) -> RpcError { - RpcError::new_internal_error( - Some(serde_json::to_value(self).unwrap()), - "transaction not accepted", - ) + RpcError::invalid_params(Some(serde_json::to_value(self).unwrap())) } } diff --git a/sequencer_runner/configs/debug/sequencer_config.json b/sequencer_runner/configs/debug/sequencer_config.json index 4ac8e15b..002228f4 100644 --- a/sequencer_runner/configs/debug/sequencer_config.json +++ b/sequencer_runner/configs/debug/sequencer_config.json @@ -1,16 +1,17 @@ { - "home": ".", + "home": "./sequencer_runner", "override_rust_log": null, "genesis_id": 1, "is_genesis_random": true, "max_num_tx_in_block": 20, + "max_block_size": "1 MiB", "mempool_max_size": 1000, - "block_create_timeout_millis": 12000, - "retry_pending_blocks_timeout_millis": 6000, + "block_create_timeout": "15s", + "retry_pending_blocks_timeout": "5s", "port": 3040, "bedrock_config": { "backoff": { - "start_delay_millis": 100, + "start_delay": "100ms", "max_retries": 5 }, "channel_id": "0101010101010101010101010101010101010101010101010101010101010101", diff --git a/sequencer_runner/configs/docker/sequencer_config.json b/sequencer_runner/configs/docker/sequencer_config.json index 4dd817c9..ce79f4e2 100644 --- a/sequencer_runner/configs/docker/sequencer_config.json +++ b/sequencer_runner/configs/docker/sequencer_config.json @@ -4,13 +4,14 @@ "genesis_id": 1, "is_genesis_random": true, "max_num_tx_in_block": 20, + "max_block_size": "1 MiB", "mempool_max_size": 10000, - "block_create_timeout_millis": 10000, + "block_create_timeout": "10s", "port": 3040, - "retry_pending_blocks_timeout_millis": 7000, + "retry_pending_blocks_timeout": "7s", "bedrock_config": { "backoff": { - "start_delay_millis": 100, + "start_delay": "100ms", "max_retries": 5 }, "channel_id": "0101010101010101010101010101010101010101010101010101010101010101", diff --git a/sequencer_runner/src/lib.rs b/sequencer_runner/src/lib.rs index 74ebae49..7a02bfc3 100644 --- a/sequencer_runner/src/lib.rs +++ b/sequencer_runner/src/lib.rs @@ -99,9 +99,8 @@ impl Drop for SequencerHandle { } pub async fn startup_sequencer(app_config: SequencerConfig) -> Result { - let block_timeout = Duration::from_millis(app_config.block_create_timeout_millis); - let retry_pending_blocks_timeout = - Duration::from_millis(app_config.retry_pending_blocks_timeout_millis); + let block_timeout = app_config.block_create_timeout; + let retry_pending_blocks_timeout = app_config.retry_pending_blocks_timeout; let port = app_config.port; let (sequencer_core, mempool_handle) = SequencerCore::start_from_config(app_config).await; @@ -114,11 +113,20 @@ pub async fn startup_sequencer(app_config: SequencerConfig) -> Result>, block_timeout: Duration) } } +#[cfg(not(feature = "standalone"))] +async fn retry_pending_blocks(seq_core: &Arc>) -> Result<()> { + use std::time::Instant; + + use log::debug; + + let (pending_blocks, block_settlement_client) = { + let sequencer_core = seq_core.lock().await; + let client = sequencer_core.block_settlement_client(); + let pending_blocks = sequencer_core + .get_pending_blocks() + .expect("Sequencer should be able to retrieve pending blocks"); + (pending_blocks, client) + }; + + for block in pending_blocks.iter() { + info!( + "Resubmitting pending block with id {}", + block.header.block_id + ); + // TODO: We could cache the inscribe tx for each pending block to avoid re-creating it + // on every retry. + let now = Instant::now(); + let (tx, _msg_id) = block_settlement_client + .create_inscribe_tx(block) + .context("Failed to create inscribe tx for pending block")?; + + debug!(">>>> Create inscribe: {:?}", now.elapsed()); + + let now = Instant::now(); + if let Err(e) = block_settlement_client + .submit_inscribe_tx_to_bedrock(tx) + .await + { + warn!( + "Failed to resubmit block with id {} with error {e:#}", + block.header.block_id + ); + } + debug!(">>>> Post: {:?}", now.elapsed()); + } + Ok(()) +} + #[cfg(not(feature = "standalone"))] async fn retry_pending_blocks_loop( seq_core: Arc>, @@ -166,40 +218,7 @@ async fn retry_pending_blocks_loop( ) -> Result { loop { tokio::time::sleep(retry_pending_blocks_timeout).await; - - let (pending_blocks, block_settlement_client) = { - let sequencer_core = seq_core.lock().await; - let client = sequencer_core.block_settlement_client(); - let pending_blocks = sequencer_core - .get_pending_blocks() - .expect("Sequencer should be able to retrieve pending blocks"); - (pending_blocks, client) - }; - - if let Some(block) = pending_blocks - .iter() - .min_by_key(|block| block.header.block_id) - { - info!( - "Resubmitting pending block with id {}", - block.header.block_id - ); - // TODO: We could cache the inscribe tx for each pending block to avoid re-creating - // it on every retry. - let (tx, _msg_id) = block_settlement_client - .create_inscribe_tx(block) - .context("Failed to create inscribe tx for pending block")?; - - if let Err(e) = block_settlement_client - .submit_inscribe_tx_to_bedrock(tx) - .await - { - warn!( - "Failed to resubmit block with id {} with error {e:#}", - block.header.block_id - ); - } - } + retry_pending_blocks(&seq_core).await?; } } diff --git a/storage/Cargo.toml b/storage/Cargo.toml index 8da47de3..b6374249 100644 --- a/storage/Cargo.toml +++ b/storage/Cargo.toml @@ -6,8 +6,9 @@ license = { workspace = true } [dependencies] common.workspace = true +nssa.workspace = true thiserror.workspace = true borsh.workspace = true rocksdb.workspace = true -nssa.workspace = true +tempfile.workspace = true diff --git a/storage/src/indexer.rs b/storage/src/indexer.rs new file mode 100644 index 00000000..94a1a1a0 --- /dev/null +++ b/storage/src/indexer.rs @@ -0,0 +1,1306 @@ +use std::{collections::HashMap, ops::Div, path::Path, sync::Arc}; + +use common::{block::Block, transaction::NSSATransaction}; +use nssa::V02State; +use rocksdb::{ + BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options, WriteBatch, +}; + +use crate::error::DbError; + +/// Maximal size of stored blocks in base +/// +/// Used to control db size +/// +/// Currently effectively unbounded. +pub const BUFF_SIZE_ROCKSDB: usize = usize::MAX; + +/// Size of stored blocks cache in memory +/// +/// Keeping small to not run out of memory +pub const CACHE_SIZE: usize = 1000; + +/// Key base for storing metainformation about id of first block in db +pub const DB_META_FIRST_BLOCK_IN_DB_KEY: &str = "first_block_in_db"; +/// Key base for storing metainformation about id of last current block in db +pub const DB_META_LAST_BLOCK_IN_DB_KEY: &str = "last_block_in_db"; +/// Key base for storing metainformation about id of last observed L1 lib header in db +pub const DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY: &str = + "last_observed_l1_lib_header_in_db"; +/// Key base for storing metainformation which describe if first block has been set +pub const DB_META_FIRST_BLOCK_SET_KEY: &str = "first_block_set"; +/// Key base for storing metainformation about the last breakpoint +pub const DB_META_LAST_BREAKPOINT_ID: &str = "last_breakpoint_id"; + +/// Interval between state breakpoints +pub const BREAKPOINT_INTERVAL: u64 = 100; + +/// Name of block column family +pub const CF_BLOCK_NAME: &str = "cf_block"; +/// Name of meta column family +pub const CF_META_NAME: &str = "cf_meta"; +/// Name of breakpoint column family +pub const CF_BREAKPOINT_NAME: &str = "cf_breakpoint"; +/// Name of hash to id map column family +pub const CF_HASH_TO_ID: &str = "cf_hash_to_id"; +/// Name of tx hash to id map column family +pub const CF_TX_TO_ID: &str = "cf_tx_to_id"; +/// Name of account meta column family +pub const CF_ACC_META: &str = "cf_acc_meta"; +/// Name of account id to tx hash map column family +pub const CF_ACC_TO_TX: &str = "cf_acc_to_tx"; + +pub type DbResult = Result; + +fn closest_breakpoint_id(block_id: u64) -> u64 { + block_id.saturating_sub(1).div(BREAKPOINT_INTERVAL) +} + +pub struct RocksDBIO { + pub db: DBWithThreadMode, +} + +impl RocksDBIO { + pub fn open_or_create(path: &Path, start_data: Option<(Block, V02State)>) -> DbResult { + let mut cf_opts = Options::default(); + cf_opts.set_max_write_buffer_number(16); + // ToDo: Add more column families for different data + let cfb = ColumnFamilyDescriptor::new(CF_BLOCK_NAME, cf_opts.clone()); + let cfmeta = ColumnFamilyDescriptor::new(CF_META_NAME, cf_opts.clone()); + let cfbreakpoint = ColumnFamilyDescriptor::new(CF_BREAKPOINT_NAME, cf_opts.clone()); + let cfhti = ColumnFamilyDescriptor::new(CF_HASH_TO_ID, cf_opts.clone()); + let cftti = ColumnFamilyDescriptor::new(CF_TX_TO_ID, cf_opts.clone()); + let cfameta = ColumnFamilyDescriptor::new(CF_ACC_META, cf_opts.clone()); + let cfatt = ColumnFamilyDescriptor::new(CF_ACC_TO_TX, cf_opts.clone()); + + let mut db_opts = Options::default(); + db_opts.create_missing_column_families(true); + db_opts.create_if_missing(true); + let db = DBWithThreadMode::::open_cf_descriptors( + &db_opts, + path, + vec![cfb, cfmeta, cfbreakpoint, cfhti, cftti, cfameta, cfatt], + ); + + let dbio = Self { + // There is no point in handling this from runner code + db: db.unwrap(), + }; + + let is_start_set = dbio.get_meta_is_first_block_set()?; + + if is_start_set { + Ok(dbio) + } else if let Some((block, initial_state)) = start_data { + let block_id = block.header.block_id; + dbio.put_meta_last_block_in_db(block_id)?; + dbio.put_meta_first_block_in_db(block)?; + dbio.put_meta_is_first_block_set()?; + + // First breakpoint setup + dbio.put_breakpoint(0, initial_state)?; + dbio.put_meta_last_breakpoint_id(0)?; + + Ok(dbio) + } else { + // Here we are trying to start a DB without a block, one should not do it. + unreachable!() + } + } + + pub fn destroy(path: &Path) -> DbResult<()> { + let mut cf_opts = Options::default(); + cf_opts.set_max_write_buffer_number(16); + // ToDo: Add more column families for different data + let _cfb = ColumnFamilyDescriptor::new(CF_BLOCK_NAME, cf_opts.clone()); + let _cfmeta = ColumnFamilyDescriptor::new(CF_META_NAME, cf_opts.clone()); + let _cfsnapshot = ColumnFamilyDescriptor::new(CF_BREAKPOINT_NAME, cf_opts.clone()); + let _cfhti = ColumnFamilyDescriptor::new(CF_HASH_TO_ID, cf_opts.clone()); + let _cftti = ColumnFamilyDescriptor::new(CF_TX_TO_ID, cf_opts.clone()); + let _cfameta = ColumnFamilyDescriptor::new(CF_ACC_META, cf_opts.clone()); + let _cfatt = ColumnFamilyDescriptor::new(CF_ACC_TO_TX, cf_opts.clone()); + + let mut db_opts = Options::default(); + db_opts.create_missing_column_families(true); + db_opts.create_if_missing(true); + DBWithThreadMode::::destroy(&db_opts, path) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None)) + } + + // Columns + + pub fn meta_column(&self) -> Arc> { + self.db.cf_handle(CF_META_NAME).unwrap() + } + + pub fn block_column(&self) -> Arc> { + self.db.cf_handle(CF_BLOCK_NAME).unwrap() + } + + pub fn breakpoint_column(&self) -> Arc> { + self.db.cf_handle(CF_BREAKPOINT_NAME).unwrap() + } + + pub fn hash_to_id_column(&self) -> Arc> { + self.db.cf_handle(CF_HASH_TO_ID).unwrap() + } + + pub fn tx_hash_to_id_column(&self) -> Arc> { + self.db.cf_handle(CF_TX_TO_ID).unwrap() + } + + pub fn account_id_to_tx_hash_column(&self) -> Arc> { + self.db.cf_handle(CF_ACC_TO_TX).unwrap() + } + + pub fn account_meta_column(&self) -> Arc> { + self.db.cf_handle(CF_ACC_META).unwrap() + } + + // Meta + + pub fn get_meta_first_block_in_db(&self) -> DbResult { + let cf_meta = self.meta_column(); + let res = self + .db + .get_cf( + &cf_meta, + borsh::to_vec(&DB_META_FIRST_BLOCK_IN_DB_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_FIRST_BLOCK_IN_DB_KEY".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to deserialize first block".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "First block not found".to_string(), + )) + } + } + + pub fn get_meta_last_block_in_db(&self) -> DbResult { + let cf_meta = self.meta_column(); + let res = self + .db + .get_cf( + &cf_meta, + borsh::to_vec(&DB_META_LAST_BLOCK_IN_DB_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LAST_BLOCK_IN_DB_KEY".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to deserialize last block".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "Last block not found".to_string(), + )) + } + } + + pub fn get_meta_last_observed_l1_lib_header_in_db(&self) -> DbResult> { + let cf_meta = self.meta_column(); + let res = self + .db + .get_cf( + &cf_meta, + borsh::to_vec(&DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY).map_err( + |err| { + DbError::borsh_cast_message( + err, + Some( + "Failed to serialize DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY" + .to_string(), + ), + ) + }, + )?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + res.map(|data| { + borsh::from_slice::<[u8; 32]>(&data).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to deserialize last l1 lib header".to_string()), + ) + }) + }) + .transpose() + } + + pub fn get_meta_is_first_block_set(&self) -> DbResult { + let cf_meta = self.meta_column(); + let res = self + .db + .get_cf( + &cf_meta, + borsh::to_vec(&DB_META_FIRST_BLOCK_SET_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_FIRST_BLOCK_SET_KEY".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + Ok(res.is_some()) + } + + pub fn get_meta_last_breakpoint_id(&self) -> DbResult { + let cf_meta = self.meta_column(); + let res = self + .db + .get_cf( + &cf_meta, + borsh::to_vec(&DB_META_LAST_BREAKPOINT_ID).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LAST_BREAKPOINT_ID".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to deserialize last breakpoint id".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "Last breakpoint id not found".to_string(), + )) + } + } + + pub fn put_meta_first_block_in_db(&self, block: Block) -> DbResult<()> { + let cf_meta = self.meta_column(); + self.db + .put_cf( + &cf_meta, + borsh::to_vec(&DB_META_FIRST_BLOCK_IN_DB_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_FIRST_BLOCK_IN_DB_KEY".to_string()), + ) + })?, + borsh::to_vec(&block.header.block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize first block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + self.put_block(block, [0; 32])?; + Ok(()) + } + + pub fn put_meta_last_block_in_db(&self, block_id: u64) -> DbResult<()> { + let cf_meta = self.meta_column(); + self.db + .put_cf( + &cf_meta, + borsh::to_vec(&DB_META_LAST_BLOCK_IN_DB_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LAST_BLOCK_IN_DB_KEY".to_string()), + ) + })?, + borsh::to_vec(&block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize last block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + Ok(()) + } + + pub fn put_meta_last_observed_l1_lib_header_in_db( + &self, + l1_lib_header: [u8; 32], + ) -> DbResult<()> { + let cf_meta = self.meta_column(); + self.db + .put_cf( + &cf_meta, + borsh::to_vec(&DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY).map_err( + |err| { + DbError::borsh_cast_message( + err, + Some( + "Failed to serialize DB_META_LAST_OBSERVED_L1_LIB_HEADER_ID_IN_DB_KEY" + .to_string(), + ), + ) + }, + )?, + borsh::to_vec(&l1_lib_header).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize last l1 block header".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + Ok(()) + } + + pub fn put_meta_last_breakpoint_id(&self, br_id: u64) -> DbResult<()> { + let cf_meta = self.meta_column(); + self.db + .put_cf( + &cf_meta, + borsh::to_vec(&DB_META_LAST_BREAKPOINT_ID).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LAST_BREAKPOINT_ID".to_string()), + ) + })?, + borsh::to_vec(&br_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize last block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + Ok(()) + } + + pub fn put_meta_is_first_block_set(&self) -> DbResult<()> { + let cf_meta = self.meta_column(); + self.db + .put_cf( + &cf_meta, + borsh::to_vec(&DB_META_FIRST_BLOCK_SET_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_FIRST_BLOCK_SET_KEY".to_string()), + ) + })?, + [1u8; 1], + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + Ok(()) + } + + // Block + + pub fn put_block(&self, block: Block, l1_lib_header: [u8; 32]) -> DbResult<()> { + let cf_block = self.block_column(); + let cf_hti = self.hash_to_id_column(); + let cf_tti: Arc> = self.tx_hash_to_id_column(); + + // ToDo: rewrite this with write batching + + self.db + .put_cf( + &cf_block, + borsh::to_vec(&block.header.block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block id".to_string()), + ) + })?, + borsh::to_vec(&block).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block data".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + let last_curr_block = self.get_meta_last_block_in_db()?; + + if block.header.block_id > last_curr_block { + self.put_meta_last_block_in_db(block.header.block_id)?; + self.put_meta_last_observed_l1_lib_header_in_db(l1_lib_header)?; + } + + self.db + .put_cf( + &cf_hti, + borsh::to_vec(&block.header.hash).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block hash".to_string()), + ) + })?, + borsh::to_vec(&block.header.block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + let mut acc_to_tx_map: HashMap<[u8; 32], Vec<[u8; 32]>> = HashMap::new(); + + for tx in block.body.transactions { + let tx_hash = tx.hash(); + + self.db + .put_cf( + &cf_tti, + borsh::to_vec(&tx_hash).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize tx hash".to_string()), + ) + })?, + borsh::to_vec(&block.header.block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + let acc_ids = tx + .affected_public_account_ids() + .into_iter() + .map(|account_id| account_id.into_value()) + .collect::>(); + + for acc_id in acc_ids { + acc_to_tx_map + .entry(acc_id) + .and_modify(|tx_hashes| tx_hashes.push(tx_hash.into())) + .or_insert(vec![tx_hash.into()]); + } + } + + for (acc_id, tx_hashes) in acc_to_tx_map { + self.put_account_transactions(acc_id, tx_hashes)?; + } + + if block.header.block_id.is_multiple_of(BREAKPOINT_INTERVAL) { + self.put_next_breakpoint()?; + } + + Ok(()) + } + + pub fn get_block(&self, block_id: u64) -> DbResult { + let cf_block = self.block_column(); + let res = self + .db + .get_cf( + &cf_block, + borsh::to_vec(&block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|serr| { + DbError::borsh_cast_message( + serr, + Some("Failed to deserialize block data".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "Block on this id not found".to_string(), + )) + } + } + + pub fn get_block_batch(&self, offset: u64, limit: u64) -> DbResult> { + let cf_block = self.block_column(); + let mut block_batch = vec![]; + + // ToDo: Multi get this + + for block_id in offset..(offset + limit) { + let res = self + .db + .get_cf( + &cf_block, + borsh::to_vec(&block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + let block = if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|serr| { + DbError::borsh_cast_message( + serr, + Some("Failed to deserialize block data".to_string()), + ) + })?) + } else { + // Block not found, assuming that previous one was the last + break; + }?; + + block_batch.push(block); + } + + Ok(block_batch) + } + + // State + + pub fn put_breakpoint(&self, br_id: u64, breakpoint: V02State) -> DbResult<()> { + let cf_br = self.breakpoint_column(); + + self.db + .put_cf( + &cf_br, + borsh::to_vec(&br_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize breakpoint id".to_string()), + ) + })?, + borsh::to_vec(&breakpoint).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize breakpoint data".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None)) + } + + pub fn get_breakpoint(&self, br_id: u64) -> DbResult { + let cf_br = self.breakpoint_column(); + let res = self + .db + .get_cf( + &cf_br, + borsh::to_vec(&br_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize breakpoint id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|serr| { + DbError::borsh_cast_message( + serr, + Some("Failed to deserialize breakpoint data".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "Breakpoint on this id not found".to_string(), + )) + } + } + + pub fn calculate_state_for_id(&self, block_id: u64) -> DbResult { + let last_block = self.get_meta_last_block_in_db()?; + + if block_id <= last_block { + let br_id = closest_breakpoint_id(block_id); + let mut breakpoint = self.get_breakpoint(br_id)?; + + // ToDo: update it to handle any genesis id + // right now works correctly only if genesis_id < BREAKPOINT_INTERVAL + let start = if br_id != 0 { + BREAKPOINT_INTERVAL * br_id + } else { + self.get_meta_first_block_in_db()? + }; + + for id in start..=block_id { + let block = self.get_block(id)?; + + for transaction in block.body.transactions { + transaction + .transaction_stateless_check() + .map_err(|err| { + DbError::db_interaction_error(format!( + "transaction pre check failed with err {err:?}" + )) + })? + .execute_check_on_state(&mut breakpoint) + .map_err(|err| { + DbError::db_interaction_error(format!( + "transaction execution failed with err {err:?}" + )) + })?; + } + } + + Ok(breakpoint) + } else { + Err(DbError::db_interaction_error( + "Block on this id not found".to_string(), + )) + } + } + + pub fn final_state(&self) -> DbResult { + self.calculate_state_for_id(self.get_meta_last_block_in_db()?) + } + + pub fn put_next_breakpoint(&self) -> DbResult<()> { + let last_block = self.get_meta_last_block_in_db()?; + let next_breakpoint_id = self.get_meta_last_breakpoint_id()? + 1; + let block_to_break_id = next_breakpoint_id * BREAKPOINT_INTERVAL; + + if block_to_break_id <= last_block { + let next_breakpoint = self.calculate_state_for_id(block_to_break_id)?; + + self.put_breakpoint(next_breakpoint_id, next_breakpoint)?; + self.put_meta_last_breakpoint_id(next_breakpoint_id) + } else { + Err(DbError::db_interaction_error( + "Breakpoint not yet achieved".to_string(), + )) + } + } + + // Mappings + + pub fn get_block_id_by_hash(&self, hash: [u8; 32]) -> DbResult { + let cf_hti = self.hash_to_id_column(); + let res = self + .db + .get_cf( + &cf_hti, + borsh::to_vec(&hash).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block hash".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|serr| { + DbError::borsh_cast_message( + serr, + Some("Failed to deserialize block id".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "Block on this hash not found".to_string(), + )) + } + } + + pub fn get_block_id_by_tx_hash(&self, tx_hash: [u8; 32]) -> DbResult { + let cf_tti = self.tx_hash_to_id_column(); + let res = self + .db + .get_cf( + &cf_tti, + borsh::to_vec(&tx_hash).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize transaction hash".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|serr| { + DbError::borsh_cast_message( + serr, + Some("Failed to deserialize block id".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "Block for this tx hash not found".to_string(), + )) + } + } + + // Accounts meta + + fn update_acc_meta_batch( + &self, + acc_id: [u8; 32], + num_tx: u64, + write_batch: &mut WriteBatch, + ) -> DbResult<()> { + let cf_ameta = self.account_meta_column(); + + write_batch.put_cf( + &cf_ameta, + borsh::to_vec(&acc_id).map_err(|err| { + DbError::borsh_cast_message(err, Some("Failed to serialize account id".to_string())) + })?, + borsh::to_vec(&num_tx).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize acc metadata".to_string()), + ) + })?, + ); + + Ok(()) + } + + fn get_acc_meta_num_tx(&self, acc_id: [u8; 32]) -> DbResult> { + let cf_ameta = self.account_meta_column(); + let res = self.db.get_cf(&cf_ameta, acc_id).map_err(|rerr| { + DbError::rocksdb_cast_message(rerr, Some("Failed to read from acc meta cf".to_string())) + })?; + + res.map(|data| { + borsh::from_slice::(&data).map_err(|serr| { + DbError::borsh_cast_message(serr, Some("Failed to deserialize num tx".to_string())) + }) + }) + .transpose() + } + + // Account + + pub fn put_account_transactions( + &self, + acc_id: [u8; 32], + tx_hashes: Vec<[u8; 32]>, + ) -> DbResult<()> { + let acc_num_tx = self.get_acc_meta_num_tx(acc_id)?.unwrap_or(0); + let cf_att = self.account_id_to_tx_hash_column(); + let mut write_batch = WriteBatch::new(); + + for (tx_id, tx_hash) in tx_hashes.iter().enumerate() { + let put_id = acc_num_tx + tx_id as u64; + + let mut prefix = borsh::to_vec(&acc_id).map_err(|berr| { + DbError::borsh_cast_message( + berr, + Some("Failed to serialize account id".to_string()), + ) + })?; + let suffix = borsh::to_vec(&put_id).map_err(|berr| { + DbError::borsh_cast_message(berr, Some("Failed to serialize tx id".to_string())) + })?; + + prefix.extend_from_slice(&suffix); + + write_batch.put_cf( + &cf_att, + prefix, + borsh::to_vec(tx_hash).map_err(|berr| { + DbError::borsh_cast_message( + berr, + Some("Failed to serialize tx hash".to_string()), + ) + })?, + ); + } + + self.update_acc_meta_batch( + acc_id, + acc_num_tx + (tx_hashes.len() as u64), + &mut write_batch, + )?; + + self.db.write(write_batch).map_err(|rerr| { + DbError::rocksdb_cast_message(rerr, Some("Failed to write batch".to_string())) + }) + } + + fn get_acc_transaction_hashes( + &self, + acc_id: [u8; 32], + offset: u64, + limit: u64, + ) -> DbResult> { + let cf_att = self.account_id_to_tx_hash_column(); + let mut tx_batch = vec![]; + + // ToDo: Multi get this + + for tx_id in offset..(offset + limit) { + let mut prefix = borsh::to_vec(&acc_id).map_err(|berr| { + DbError::borsh_cast_message( + berr, + Some("Failed to serialize account id".to_string()), + ) + })?; + let suffix = borsh::to_vec(&tx_id).map_err(|berr| { + DbError::borsh_cast_message(berr, Some("Failed to serialize tx id".to_string())) + })?; + + prefix.extend_from_slice(&suffix); + + let res = self + .db + .get_cf(&cf_att, prefix) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + let tx_hash = if let Some(data) = res { + Ok(borsh::from_slice::<[u8; 32]>(&data).map_err(|serr| { + DbError::borsh_cast_message( + serr, + Some("Failed to deserialize tx_hash".to_string()), + ) + })?) + } else { + // Tx hash not found, assuming that previous one was the last + break; + }?; + + tx_batch.push(tx_hash); + } + + Ok(tx_batch) + } + + pub fn get_acc_transactions( + &self, + acc_id: [u8; 32], + offset: u64, + limit: u64, + ) -> DbResult> { + let mut tx_batch = vec![]; + + for tx_hash in self.get_acc_transaction_hashes(acc_id, offset, limit)? { + let block_id = self.get_block_id_by_tx_hash(tx_hash)?; + let block = self.get_block(block_id)?; + + let transaction = block + .body + .transactions + .iter() + .find(|tx| tx.hash().0 == tx_hash) + .ok_or(DbError::db_interaction_error(format!( + "Missing transaction in block {} with hash {:#?}", + block.header.block_id, tx_hash + )))?; + + tx_batch.push(transaction.clone()); + } + + Ok(tx_batch) + } +} + +#[cfg(test)] +mod tests { + use nssa::AccountId; + use tempfile::tempdir; + + use super::*; + + fn genesis_block() -> Block { + common::test_utils::produce_dummy_block(1, None, vec![]) + } + + fn acc1() -> AccountId { + AccountId::new([ + 148, 179, 206, 253, 199, 51, 82, 86, 232, 2, 152, 122, 80, 243, 54, 207, 237, 112, 83, + 153, 44, 59, 204, 49, 128, 84, 160, 227, 216, 149, 97, 102, + ]) + } + + fn acc2() -> AccountId { + AccountId::new([ + 30, 145, 107, 3, 207, 73, 192, 230, 160, 63, 238, 207, 18, 69, 54, 216, 103, 244, 92, + 94, 124, 248, 42, 16, 141, 19, 119, 18, 14, 226, 140, 204, + ]) + } + + fn acc1_sign_key() -> nssa::PrivateKey { + nssa::PrivateKey::try_new([1; 32]).unwrap() + } + + fn acc2_sign_key() -> nssa::PrivateKey { + nssa::PrivateKey::try_new([2; 32]).unwrap() + } + + fn initial_state() -> V02State { + nssa::V02State::new_with_genesis_accounts(&[(acc1(), 10000), (acc2(), 20000)], &[]) + } + + fn transfer(amount: u128, nonce: u128, direction: bool) -> NSSATransaction { + let from; + let to; + let sign_key; + + if direction { + from = acc1(); + to = acc2(); + sign_key = acc1_sign_key(); + } else { + from = acc2(); + to = acc1(); + sign_key = acc2_sign_key(); + } + + common::test_utils::create_transaction_native_token_transfer( + from, nonce, to, amount, sign_key, + ) + } + + #[test] + fn test_start_db() { + let temp_dir = tempdir().unwrap(); + let temdir_path = temp_dir.path(); + + let dbio = RocksDBIO::open_or_create(temdir_path, Some((genesis_block(), initial_state()))) + .unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let first_id = dbio.get_meta_first_block_in_db().unwrap(); + let is_first_set = dbio.get_meta_is_first_block_set().unwrap(); + let last_br_id = dbio.get_meta_last_breakpoint_id().unwrap(); + let last_block = dbio.get_block(1).unwrap(); + let breakpoint = dbio.get_breakpoint(0).unwrap(); + let final_state = dbio.final_state().unwrap(); + + assert_eq!(last_id, 1); + assert_eq!(first_id, 1); + assert!(is_first_set); + assert_eq!(last_br_id, 0); + assert_eq!(last_block.header.hash, genesis_block().header.hash); + assert_eq!( + breakpoint.get_account_by_id(acc1()), + final_state.get_account_by_id(acc1()) + ); + assert_eq!( + breakpoint.get_account_by_id(acc2()), + final_state.get_account_by_id(acc2()) + ); + } + + #[test] + fn test_one_block_insertion() { + let temp_dir = tempdir().unwrap(); + let temdir_path = temp_dir.path(); + + let dbio = RocksDBIO::open_or_create(temdir_path, Some((genesis_block(), initial_state()))) + .unwrap(); + + let prev_hash = genesis_block().header.hash; + let transfer_tx = transfer(1, 0, true); + let block = common::test_utils::produce_dummy_block(2, Some(prev_hash), vec![transfer_tx]); + + dbio.put_block(block, [1; 32]).unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let first_id = dbio.get_meta_first_block_in_db().unwrap(); + let is_first_set = dbio.get_meta_is_first_block_set().unwrap(); + let last_br_id = dbio.get_meta_last_breakpoint_id().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + let breakpoint = dbio.get_breakpoint(0).unwrap(); + let final_state = dbio.final_state().unwrap(); + + assert_eq!(last_id, 2); + assert_eq!(first_id, 1); + assert!(is_first_set); + assert_eq!(last_br_id, 0); + assert_ne!(last_block.header.hash, genesis_block().header.hash); + assert_eq!( + breakpoint.get_account_by_id(acc1()).balance + - final_state.get_account_by_id(acc1()).balance, + 1 + ); + assert_eq!( + final_state.get_account_by_id(acc2()).balance + - breakpoint.get_account_by_id(acc2()).balance, + 1 + ); + } + + #[test] + fn test_new_breakpoint() { + let temp_dir = tempdir().unwrap(); + let temdir_path = temp_dir.path(); + + let dbio = RocksDBIO::open_or_create(temdir_path, Some((genesis_block(), initial_state()))) + .unwrap(); + + for i in 1..BREAKPOINT_INTERVAL { + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, (i - 1) as u128, true); + let block = + common::test_utils::produce_dummy_block(i + 1, Some(prev_hash), vec![transfer_tx]); + dbio.put_block(block, [i as u8; 32]).unwrap(); + } + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let first_id = dbio.get_meta_first_block_in_db().unwrap(); + let is_first_set = dbio.get_meta_is_first_block_set().unwrap(); + let last_br_id = dbio.get_meta_last_breakpoint_id().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + let prev_breakpoint = dbio.get_breakpoint(0).unwrap(); + let breakpoint = dbio.get_breakpoint(1).unwrap(); + let final_state = dbio.final_state().unwrap(); + + assert_eq!(last_id, 100); + assert_eq!(first_id, 1); + assert!(is_first_set); + assert_eq!(last_br_id, 1); + assert_ne!(last_block.header.hash, genesis_block().header.hash); + assert_eq!( + prev_breakpoint.get_account_by_id(acc1()).balance + - final_state.get_account_by_id(acc1()).balance, + 99 + ); + assert_eq!( + final_state.get_account_by_id(acc2()).balance + - prev_breakpoint.get_account_by_id(acc2()).balance, + 99 + ); + assert_eq!( + breakpoint.get_account_by_id(acc1()), + final_state.get_account_by_id(acc1()) + ); + assert_eq!( + breakpoint.get_account_by_id(acc2()), + final_state.get_account_by_id(acc2()) + ); + } + + #[test] + fn test_simple_maps() { + let temp_dir = tempdir().unwrap(); + let temdir_path = temp_dir.path(); + + let dbio = RocksDBIO::open_or_create(temdir_path, Some((genesis_block(), initial_state()))) + .unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 0, true); + let block = common::test_utils::produce_dummy_block(2, Some(prev_hash), vec![transfer_tx]); + + let control_hash1 = block.header.hash; + + dbio.put_block(block, [1; 32]).unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 1, true); + let block = common::test_utils::produce_dummy_block(3, Some(prev_hash), vec![transfer_tx]); + + let control_hash2 = block.header.hash; + + dbio.put_block(block, [2; 32]).unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 2, true); + + let control_tx_hash1 = transfer_tx.hash(); + + let block = common::test_utils::produce_dummy_block(4, Some(prev_hash), vec![transfer_tx]); + dbio.put_block(block, [3; 32]).unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 3, true); + + let control_tx_hash2 = transfer_tx.hash(); + + let block = common::test_utils::produce_dummy_block(5, Some(prev_hash), vec![transfer_tx]); + dbio.put_block(block, [4; 32]).unwrap(); + + let control_block_id1 = dbio.get_block_id_by_hash(control_hash1.0).unwrap(); + let control_block_id2 = dbio.get_block_id_by_hash(control_hash2.0).unwrap(); + let control_block_id3 = dbio.get_block_id_by_tx_hash(control_tx_hash1.0).unwrap(); + let control_block_id4 = dbio.get_block_id_by_tx_hash(control_tx_hash2.0).unwrap(); + + assert_eq!(control_block_id1, 2); + assert_eq!(control_block_id2, 3); + assert_eq!(control_block_id3, 4); + assert_eq!(control_block_id4, 5); + } + + #[test] + fn test_block_batch() { + let temp_dir = tempdir().unwrap(); + let temdir_path = temp_dir.path(); + + let mut block_res = vec![]; + + let dbio = RocksDBIO::open_or_create(temdir_path, Some((genesis_block(), initial_state()))) + .unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 0, true); + let block = common::test_utils::produce_dummy_block(2, Some(prev_hash), vec![transfer_tx]); + + block_res.push(block.clone()); + dbio.put_block(block, [1; 32]).unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 1, true); + let block = common::test_utils::produce_dummy_block(3, Some(prev_hash), vec![transfer_tx]); + + block_res.push(block.clone()); + dbio.put_block(block, [2; 32]).unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 2, true); + + let block = common::test_utils::produce_dummy_block(4, Some(prev_hash), vec![transfer_tx]); + block_res.push(block.clone()); + dbio.put_block(block, [3; 32]).unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 3, true); + + let block = common::test_utils::produce_dummy_block(5, Some(prev_hash), vec![transfer_tx]); + block_res.push(block.clone()); + dbio.put_block(block, [4; 32]).unwrap(); + + let block_hashes_mem: Vec<[u8; 32]> = + block_res.into_iter().map(|bl| bl.header.hash.0).collect(); + + let batch_res = dbio.get_block_batch(2, 4).unwrap(); + + let block_hashes_db: Vec<[u8; 32]> = + batch_res.into_iter().map(|bl| bl.header.hash.0).collect(); + + assert_eq!(block_hashes_mem, block_hashes_db); + + let block_hashes_mem_limited = &block_hashes_mem[1..]; + + let batch_res_limited = dbio.get_block_batch(3, 4).unwrap(); + + let block_hashes_db_limited: Vec<[u8; 32]> = batch_res_limited + .into_iter() + .map(|bl| bl.header.hash.0) + .collect(); + + assert_eq!(block_hashes_mem_limited, block_hashes_db_limited.as_slice()); + } + + #[test] + fn test_account_map() { + let temp_dir = tempdir().unwrap(); + let temdir_path = temp_dir.path(); + + let mut tx_hash_res = vec![]; + + let dbio = RocksDBIO::open_or_create(temdir_path, Some((genesis_block(), initial_state()))) + .unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 0, true); + + tx_hash_res.push(transfer_tx.hash().0); + + let block = common::test_utils::produce_dummy_block(2, Some(prev_hash), vec![transfer_tx]); + + dbio.put_block(block, [1; 32]).unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 1, true); + + tx_hash_res.push(transfer_tx.hash().0); + + let block = common::test_utils::produce_dummy_block(3, Some(prev_hash), vec![transfer_tx]); + + dbio.put_block(block, [2; 32]).unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 2, true); + + tx_hash_res.push(transfer_tx.hash().0); + + let block = common::test_utils::produce_dummy_block(4, Some(prev_hash), vec![transfer_tx]); + + dbio.put_block(block, [3; 32]).unwrap(); + + let last_id = dbio.get_meta_last_block_in_db().unwrap(); + let last_block = dbio.get_block(last_id).unwrap(); + + let prev_hash = last_block.header.hash; + let transfer_tx = transfer(1, 3, true); + + tx_hash_res.push(transfer_tx.hash().0); + + let block = common::test_utils::produce_dummy_block(5, Some(prev_hash), vec![transfer_tx]); + + dbio.put_block(block, [4; 32]).unwrap(); + + let acc1_tx = dbio.get_acc_transactions(*acc1().value(), 0, 4).unwrap(); + let acc1_tx_hashes: Vec<[u8; 32]> = acc1_tx.into_iter().map(|tx| tx.hash().0).collect(); + + assert_eq!(acc1_tx_hashes, tx_hash_res); + + let acc1_tx_limited = dbio.get_acc_transactions(*acc1().value(), 1, 4).unwrap(); + let acc1_tx_limited_hashes: Vec<[u8; 32]> = + acc1_tx_limited.into_iter().map(|tx| tx.hash().0).collect(); + + assert_eq!(acc1_tx_limited_hashes.as_slice(), &tx_hash_res[1..]) + } +} diff --git a/storage/src/lib.rs b/storage/src/lib.rs index c9379e33..05c4a374 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -1,601 +1,3 @@ -use std::{path::Path, sync::Arc}; - -use common::block::{BedrockStatus, Block, BlockMeta, MantleMsgId}; -use error::DbError; -use nssa::V02State; -use rocksdb::{ - BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options, WriteBatch, -}; - pub mod error; - -/// Maximal size of stored blocks in base -/// -/// Used to control db size -/// -/// Currently effectively unbounded. -pub const BUFF_SIZE_ROCKSDB: usize = usize::MAX; - -/// Size of stored blocks cache in memory -/// -/// Keeping small to not run out of memory -pub const CACHE_SIZE: usize = 1000; - -/// Key base for storing metainformation about id of first block in db -pub const DB_META_FIRST_BLOCK_IN_DB_KEY: &str = "first_block_in_db"; -/// Key base for storing metainformation about id of last current block in db -pub const DB_META_LAST_BLOCK_IN_DB_KEY: &str = "last_block_in_db"; -/// Key base for storing metainformation which describe if first block has been set -pub const DB_META_FIRST_BLOCK_SET_KEY: &str = "first_block_set"; -/// Key base for storing metainformation about the last finalized block on Bedrock -pub const DB_META_LAST_FINALIZED_BLOCK_ID: &str = "last_finalized_block_id"; -/// Key base for storing metainformation about the latest block meta -pub const DB_META_LATEST_BLOCK_META_KEY: &str = "latest_block_meta"; - -/// Key base for storing the NSSA state -pub const DB_NSSA_STATE_KEY: &str = "nssa_state"; - -/// Name of block column family -pub const CF_BLOCK_NAME: &str = "cf_block"; -/// Name of meta column family -pub const CF_META_NAME: &str = "cf_meta"; -/// Name of state column family -pub const CF_NSSA_STATE_NAME: &str = "cf_nssa_state"; - -pub type DbResult = Result; - -pub struct RocksDBIO { - pub db: DBWithThreadMode, -} - -impl RocksDBIO { - pub fn open_or_create( - path: &Path, - start_block: Option<(&Block, MantleMsgId)>, - ) -> DbResult { - let mut cf_opts = Options::default(); - cf_opts.set_max_write_buffer_number(16); - // ToDo: Add more column families for different data - let cfb = ColumnFamilyDescriptor::new(CF_BLOCK_NAME, cf_opts.clone()); - let cfmeta = ColumnFamilyDescriptor::new(CF_META_NAME, cf_opts.clone()); - let cfstate = ColumnFamilyDescriptor::new(CF_NSSA_STATE_NAME, cf_opts.clone()); - - let mut db_opts = Options::default(); - db_opts.create_missing_column_families(true); - db_opts.create_if_missing(true); - let db = DBWithThreadMode::::open_cf_descriptors( - &db_opts, - path, - vec![cfb, cfmeta, cfstate], - ); - - let dbio = Self { - // There is no point in handling this from runner code - db: db.unwrap(), - }; - - let is_start_set = dbio.get_meta_is_first_block_set()?; - - if is_start_set { - Ok(dbio) - } else if let Some((block, msg_id)) = start_block { - let block_id = block.header.block_id; - dbio.put_meta_first_block_in_db(block, msg_id)?; - dbio.put_meta_is_first_block_set()?; - dbio.put_meta_last_block_in_db(block_id)?; - dbio.put_meta_last_finalized_block_id(None)?; - dbio.put_meta_latest_block_meta(&BlockMeta { - id: block.header.block_id, - hash: block.header.hash, - msg_id, - })?; - - Ok(dbio) - } else { - // Here we are trying to start a DB without a block, one should not do it. - unreachable!() - } - } - - pub fn destroy(path: &Path) -> DbResult<()> { - let mut cf_opts = Options::default(); - cf_opts.set_max_write_buffer_number(16); - // ToDo: Add more column families for different data - let _cfb = ColumnFamilyDescriptor::new(CF_BLOCK_NAME, cf_opts.clone()); - let _cfmeta = ColumnFamilyDescriptor::new(CF_META_NAME, cf_opts.clone()); - let _cfstate = ColumnFamilyDescriptor::new(CF_NSSA_STATE_NAME, cf_opts.clone()); - - let mut db_opts = Options::default(); - db_opts.create_missing_column_families(true); - db_opts.create_if_missing(true); - DBWithThreadMode::::destroy(&db_opts, path) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None)) - } - - pub fn meta_column(&self) -> Arc> { - self.db.cf_handle(CF_META_NAME).unwrap() - } - - pub fn block_column(&self) -> Arc> { - self.db.cf_handle(CF_BLOCK_NAME).unwrap() - } - - pub fn nssa_state_column(&self) -> Arc> { - self.db.cf_handle(CF_NSSA_STATE_NAME).unwrap() - } - - pub fn get_meta_first_block_in_db(&self) -> DbResult { - let cf_meta = self.meta_column(); - let res = self - .db - .get_cf( - &cf_meta, - borsh::to_vec(&DB_META_FIRST_BLOCK_IN_DB_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_META_FIRST_BLOCK_IN_DB_KEY".to_string()), - ) - })?, - ) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - - if let Some(data) = res { - Ok(borsh::from_slice::(&data).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to deserialize first block".to_string()), - ) - })?) - } else { - Err(DbError::db_interaction_error( - "First block not found".to_string(), - )) - } - } - - pub fn get_meta_last_block_in_db(&self) -> DbResult { - let cf_meta = self.meta_column(); - let res = self - .db - .get_cf( - &cf_meta, - borsh::to_vec(&DB_META_LAST_BLOCK_IN_DB_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_META_LAST_BLOCK_IN_DB_KEY".to_string()), - ) - })?, - ) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - - if let Some(data) = res { - Ok(borsh::from_slice::(&data).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to deserialize last block".to_string()), - ) - })?) - } else { - Err(DbError::db_interaction_error( - "Last block not found".to_string(), - )) - } - } - - pub fn get_meta_is_first_block_set(&self) -> DbResult { - let cf_meta = self.meta_column(); - let res = self - .db - .get_cf( - &cf_meta, - borsh::to_vec(&DB_META_FIRST_BLOCK_SET_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_META_FIRST_BLOCK_SET_KEY".to_string()), - ) - })?, - ) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - - Ok(res.is_some()) - } - - pub fn put_nssa_state_in_db(&self, state: &V02State, batch: &mut WriteBatch) -> DbResult<()> { - let cf_nssa_state = self.nssa_state_column(); - batch.put_cf( - &cf_nssa_state, - borsh::to_vec(&DB_NSSA_STATE_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_NSSA_STATE_KEY".to_string()), - ) - })?, - borsh::to_vec(state).map_err(|err| { - DbError::borsh_cast_message(err, Some("Failed to serialize NSSA state".to_string())) - })?, - ); - - Ok(()) - } - - pub fn put_meta_first_block_in_db(&self, block: &Block, msg_id: MantleMsgId) -> DbResult<()> { - let cf_meta = self.meta_column(); - self.db - .put_cf( - &cf_meta, - borsh::to_vec(&DB_META_FIRST_BLOCK_IN_DB_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_META_FIRST_BLOCK_IN_DB_KEY".to_string()), - ) - })?, - borsh::to_vec(&block.header.block_id).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize first block id".to_string()), - ) - })?, - ) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - - let mut batch = WriteBatch::default(); - self.put_block(block, msg_id, true, &mut batch)?; - self.db.write(batch).map_err(|rerr| { - DbError::rocksdb_cast_message( - rerr, - Some("Failed to write first block in db".to_string()), - ) - })?; - - Ok(()) - } - - pub fn put_meta_last_block_in_db(&self, block_id: u64) -> DbResult<()> { - let cf_meta = self.meta_column(); - self.db - .put_cf( - &cf_meta, - borsh::to_vec(&DB_META_LAST_BLOCK_IN_DB_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_META_LAST_BLOCK_IN_DB_KEY".to_string()), - ) - })?, - borsh::to_vec(&block_id).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize last block id".to_string()), - ) - })?, - ) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - Ok(()) - } - - fn put_meta_last_block_in_db_batch( - &self, - block_id: u64, - batch: &mut WriteBatch, - ) -> DbResult<()> { - let cf_meta = self.meta_column(); - batch.put_cf( - &cf_meta, - borsh::to_vec(&DB_META_LAST_BLOCK_IN_DB_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_META_LAST_BLOCK_IN_DB_KEY".to_string()), - ) - })?, - borsh::to_vec(&block_id).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize last block id".to_string()), - ) - })?, - ); - Ok(()) - } - - pub fn put_meta_last_finalized_block_id(&self, block_id: Option) -> DbResult<()> { - let cf_meta = self.meta_column(); - self.db - .put_cf( - &cf_meta, - borsh::to_vec(&DB_META_LAST_FINALIZED_BLOCK_ID).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_META_LAST_FINALIZED_BLOCK_ID".to_string()), - ) - })?, - borsh::to_vec(&block_id).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize last block id".to_string()), - ) - })?, - ) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - Ok(()) - } - - pub fn put_meta_is_first_block_set(&self) -> DbResult<()> { - let cf_meta = self.meta_column(); - self.db - .put_cf( - &cf_meta, - borsh::to_vec(&DB_META_FIRST_BLOCK_SET_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_META_FIRST_BLOCK_SET_KEY".to_string()), - ) - })?, - [1u8; 1], - ) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - Ok(()) - } - - fn put_meta_latest_block_meta(&self, block_meta: &BlockMeta) -> DbResult<()> { - let cf_meta = self.meta_column(); - self.db - .put_cf( - &cf_meta, - borsh::to_vec(&DB_META_LATEST_BLOCK_META_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_META_LATEST_BLOCK_META_KEY".to_string()), - ) - })?, - borsh::to_vec(&block_meta).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize latest block meta".to_string()), - ) - })?, - ) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - Ok(()) - } - - fn put_meta_latest_block_meta_batch( - &self, - block_meta: &BlockMeta, - batch: &mut WriteBatch, - ) -> DbResult<()> { - let cf_meta = self.meta_column(); - batch.put_cf( - &cf_meta, - borsh::to_vec(&DB_META_LATEST_BLOCK_META_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_META_LATEST_BLOCK_META_KEY".to_string()), - ) - })?, - borsh::to_vec(&block_meta).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize latest block meta".to_string()), - ) - })?, - ); - Ok(()) - } - - pub fn latest_block_meta(&self) -> DbResult { - let cf_meta = self.meta_column(); - let res = self - .db - .get_cf( - &cf_meta, - borsh::to_vec(&DB_META_LATEST_BLOCK_META_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize DB_META_LATEST_BLOCK_META_KEY".to_string()), - ) - })?, - ) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - - if let Some(data) = res { - Ok(borsh::from_slice::(&data).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to deserialize latest block meta".to_string()), - ) - })?) - } else { - Err(DbError::db_interaction_error( - "Latest block meta not found".to_string(), - )) - } - } - - pub fn put_block( - &self, - block: &Block, - msg_id: MantleMsgId, - first: bool, - batch: &mut WriteBatch, - ) -> DbResult<()> { - let cf_block = self.block_column(); - - if !first { - let last_curr_block = self.get_meta_last_block_in_db()?; - - if block.header.block_id > last_curr_block { - self.put_meta_last_block_in_db_batch(block.header.block_id, batch)?; - self.put_meta_latest_block_meta_batch( - &BlockMeta { - id: block.header.block_id, - hash: block.header.hash, - msg_id, - }, - batch, - )?; - } - } - - batch.put_cf( - &cf_block, - borsh::to_vec(&block.header.block_id).map_err(|err| { - DbError::borsh_cast_message(err, Some("Failed to serialize block id".to_string())) - })?, - borsh::to_vec(block).map_err(|err| { - DbError::borsh_cast_message(err, Some("Failed to serialize block data".to_string())) - })?, - ); - Ok(()) - } - - pub fn get_block(&self, block_id: u64) -> DbResult { - let cf_block = self.block_column(); - let res = self - .db - .get_cf( - &cf_block, - borsh::to_vec(&block_id).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize block id".to_string()), - ) - })?, - ) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - - if let Some(data) = res { - Ok(borsh::from_slice::(&data).map_err(|serr| { - DbError::borsh_cast_message( - serr, - Some("Failed to deserialize block data".to_string()), - ) - })?) - } else { - Err(DbError::db_interaction_error( - "Block on this id not found".to_string(), - )) - } - } - - pub fn get_nssa_state(&self) -> DbResult { - let cf_nssa_state = self.nssa_state_column(); - let res = self - .db - .get_cf( - &cf_nssa_state, - borsh::to_vec(&DB_NSSA_STATE_KEY).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize block id".to_string()), - ) - })?, - ) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - - if let Some(data) = res { - Ok(borsh::from_slice::(&data).map_err(|serr| { - DbError::borsh_cast_message( - serr, - Some("Failed to deserialize block data".to_string()), - ) - })?) - } else { - Err(DbError::db_interaction_error( - "Block on this id not found".to_string(), - )) - } - } - - pub fn delete_block(&self, block_id: u64) -> DbResult<()> { - let cf_block = self.block_column(); - let key = borsh::to_vec(&block_id).map_err(|err| { - DbError::borsh_cast_message(err, Some("Failed to serialize block id".to_string())) - })?; - - if self - .db - .get_cf(&cf_block, &key) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))? - .is_none() - { - return Err(DbError::db_interaction_error( - "Block on this id not found".to_string(), - )); - } - - self.db - .delete_cf(&cf_block, key) - .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - - Ok(()) - } - - pub fn mark_block_as_finalized(&self, block_id: u64) -> DbResult<()> { - let mut block = self.get_block(block_id)?; - block.bedrock_status = BedrockStatus::Finalized; - - let cf_block = self.block_column(); - self.db - .put_cf( - &cf_block, - borsh::to_vec(&block_id).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize block id".to_string()), - ) - })?, - borsh::to_vec(&block).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to serialize block data".to_string()), - ) - })?, - ) - .map_err(|rerr| { - DbError::rocksdb_cast_message( - rerr, - Some(format!("Failed to mark block {block_id} as finalized")), - ) - })?; - - Ok(()) - } - - pub fn get_all_blocks(&self) -> impl Iterator> { - let cf_block = self.block_column(); - self.db - .iterator_cf(&cf_block, rocksdb::IteratorMode::Start) - .map(|res| { - let (_key, value) = res.map_err(|rerr| { - DbError::rocksdb_cast_message( - rerr, - Some("Failed to get key value pair".to_string()), - ) - })?; - - borsh::from_slice::(&value).map_err(|err| { - DbError::borsh_cast_message( - err, - Some("Failed to deserialize block data".to_string()), - ) - }) - }) - } - - pub fn atomic_update( - &self, - block: &Block, - msg_id: MantleMsgId, - state: &V02State, - ) -> DbResult<()> { - let block_id = block.header.block_id; - let mut batch = WriteBatch::default(); - self.put_block(block, msg_id, false, &mut batch)?; - self.put_nssa_state_in_db(state, &mut batch)?; - self.db.write(batch).map_err(|rerr| { - DbError::rocksdb_cast_message( - rerr, - Some(format!("Failed to udpate db with block {block_id}")), - ) - }) - } -} +pub mod indexer; +pub mod sequencer; diff --git a/storage/src/sequencer.rs b/storage/src/sequencer.rs new file mode 100644 index 00000000..2de123dc --- /dev/null +++ b/storage/src/sequencer.rs @@ -0,0 +1,600 @@ +use std::{path::Path, sync::Arc}; + +use common::block::{BedrockStatus, Block, BlockMeta, MantleMsgId}; +use nssa::V02State; +use rocksdb::{ + BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options, WriteBatch, +}; + +use crate::error::DbError; + +/// Maximal size of stored blocks in base +/// +/// Used to control db size +/// +/// Currently effectively unbounded. +pub const BUFF_SIZE_ROCKSDB: usize = usize::MAX; + +/// Size of stored blocks cache in memory +/// +/// Keeping small to not run out of memory +pub const CACHE_SIZE: usize = 1000; + +/// Key base for storing metainformation about id of first block in db +pub const DB_META_FIRST_BLOCK_IN_DB_KEY: &str = "first_block_in_db"; +/// Key base for storing metainformation about id of last current block in db +pub const DB_META_LAST_BLOCK_IN_DB_KEY: &str = "last_block_in_db"; +/// Key base for storing metainformation which describe if first block has been set +pub const DB_META_FIRST_BLOCK_SET_KEY: &str = "first_block_set"; +/// Key base for storing metainformation about the last finalized block on Bedrock +pub const DB_META_LAST_FINALIZED_BLOCK_ID: &str = "last_finalized_block_id"; +/// Key base for storing metainformation about the latest block meta +pub const DB_META_LATEST_BLOCK_META_KEY: &str = "latest_block_meta"; + +/// Key base for storing the NSSA state +pub const DB_NSSA_STATE_KEY: &str = "nssa_state"; + +/// Name of block column family +pub const CF_BLOCK_NAME: &str = "cf_block"; +/// Name of meta column family +pub const CF_META_NAME: &str = "cf_meta"; +/// Name of state column family +pub const CF_NSSA_STATE_NAME: &str = "cf_nssa_state"; + +pub type DbResult = Result; + +pub struct RocksDBIO { + pub db: DBWithThreadMode, +} + +impl RocksDBIO { + pub fn open_or_create( + path: &Path, + start_block: Option<(&Block, MantleMsgId)>, + ) -> DbResult { + let mut cf_opts = Options::default(); + cf_opts.set_max_write_buffer_number(16); + // ToDo: Add more column families for different data + let cfb = ColumnFamilyDescriptor::new(CF_BLOCK_NAME, cf_opts.clone()); + let cfmeta = ColumnFamilyDescriptor::new(CF_META_NAME, cf_opts.clone()); + let cfstate = ColumnFamilyDescriptor::new(CF_NSSA_STATE_NAME, cf_opts.clone()); + + let mut db_opts = Options::default(); + db_opts.create_missing_column_families(true); + db_opts.create_if_missing(true); + let db = DBWithThreadMode::::open_cf_descriptors( + &db_opts, + path, + vec![cfb, cfmeta, cfstate], + ); + + let dbio = Self { + // There is no point in handling this from runner code + db: db.unwrap(), + }; + + let is_start_set = dbio.get_meta_is_first_block_set()?; + + if is_start_set { + Ok(dbio) + } else if let Some((block, msg_id)) = start_block { + let block_id = block.header.block_id; + dbio.put_meta_first_block_in_db(block, msg_id)?; + dbio.put_meta_is_first_block_set()?; + dbio.put_meta_last_block_in_db(block_id)?; + dbio.put_meta_last_finalized_block_id(None)?; + dbio.put_meta_latest_block_meta(&BlockMeta { + id: block.header.block_id, + hash: block.header.hash, + msg_id, + })?; + + Ok(dbio) + } else { + // Here we are trying to start a DB without a block, one should not do it. + unreachable!() + } + } + + pub fn destroy(path: &Path) -> DbResult<()> { + let mut cf_opts = Options::default(); + cf_opts.set_max_write_buffer_number(16); + // ToDo: Add more column families for different data + let _cfb = ColumnFamilyDescriptor::new(CF_BLOCK_NAME, cf_opts.clone()); + let _cfmeta = ColumnFamilyDescriptor::new(CF_META_NAME, cf_opts.clone()); + let _cfstate = ColumnFamilyDescriptor::new(CF_NSSA_STATE_NAME, cf_opts.clone()); + + let mut db_opts = Options::default(); + db_opts.create_missing_column_families(true); + db_opts.create_if_missing(true); + DBWithThreadMode::::destroy(&db_opts, path) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None)) + } + + pub fn meta_column(&self) -> Arc> { + self.db.cf_handle(CF_META_NAME).unwrap() + } + + pub fn block_column(&self) -> Arc> { + self.db.cf_handle(CF_BLOCK_NAME).unwrap() + } + + pub fn nssa_state_column(&self) -> Arc> { + self.db.cf_handle(CF_NSSA_STATE_NAME).unwrap() + } + + pub fn get_meta_first_block_in_db(&self) -> DbResult { + let cf_meta = self.meta_column(); + let res = self + .db + .get_cf( + &cf_meta, + borsh::to_vec(&DB_META_FIRST_BLOCK_IN_DB_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_FIRST_BLOCK_IN_DB_KEY".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to deserialize first block".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "First block not found".to_string(), + )) + } + } + + pub fn get_meta_last_block_in_db(&self) -> DbResult { + let cf_meta = self.meta_column(); + let res = self + .db + .get_cf( + &cf_meta, + borsh::to_vec(&DB_META_LAST_BLOCK_IN_DB_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LAST_BLOCK_IN_DB_KEY".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to deserialize last block".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "Last block not found".to_string(), + )) + } + } + + pub fn get_meta_is_first_block_set(&self) -> DbResult { + let cf_meta = self.meta_column(); + let res = self + .db + .get_cf( + &cf_meta, + borsh::to_vec(&DB_META_FIRST_BLOCK_SET_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_FIRST_BLOCK_SET_KEY".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + Ok(res.is_some()) + } + + pub fn put_nssa_state_in_db(&self, state: &V02State, batch: &mut WriteBatch) -> DbResult<()> { + let cf_nssa_state = self.nssa_state_column(); + batch.put_cf( + &cf_nssa_state, + borsh::to_vec(&DB_NSSA_STATE_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_NSSA_STATE_KEY".to_string()), + ) + })?, + borsh::to_vec(state).map_err(|err| { + DbError::borsh_cast_message(err, Some("Failed to serialize NSSA state".to_string())) + })?, + ); + + Ok(()) + } + + pub fn put_meta_first_block_in_db(&self, block: &Block, msg_id: MantleMsgId) -> DbResult<()> { + let cf_meta = self.meta_column(); + self.db + .put_cf( + &cf_meta, + borsh::to_vec(&DB_META_FIRST_BLOCK_IN_DB_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_FIRST_BLOCK_IN_DB_KEY".to_string()), + ) + })?, + borsh::to_vec(&block.header.block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize first block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + let mut batch = WriteBatch::default(); + self.put_block(block, msg_id, true, &mut batch)?; + self.db.write(batch).map_err(|rerr| { + DbError::rocksdb_cast_message( + rerr, + Some("Failed to write first block in db".to_string()), + ) + })?; + + Ok(()) + } + + pub fn put_meta_last_block_in_db(&self, block_id: u64) -> DbResult<()> { + let cf_meta = self.meta_column(); + self.db + .put_cf( + &cf_meta, + borsh::to_vec(&DB_META_LAST_BLOCK_IN_DB_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LAST_BLOCK_IN_DB_KEY".to_string()), + ) + })?, + borsh::to_vec(&block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize last block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + Ok(()) + } + + fn put_meta_last_block_in_db_batch( + &self, + block_id: u64, + batch: &mut WriteBatch, + ) -> DbResult<()> { + let cf_meta = self.meta_column(); + batch.put_cf( + &cf_meta, + borsh::to_vec(&DB_META_LAST_BLOCK_IN_DB_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LAST_BLOCK_IN_DB_KEY".to_string()), + ) + })?, + borsh::to_vec(&block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize last block id".to_string()), + ) + })?, + ); + Ok(()) + } + + pub fn put_meta_last_finalized_block_id(&self, block_id: Option) -> DbResult<()> { + let cf_meta = self.meta_column(); + self.db + .put_cf( + &cf_meta, + borsh::to_vec(&DB_META_LAST_FINALIZED_BLOCK_ID).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LAST_FINALIZED_BLOCK_ID".to_string()), + ) + })?, + borsh::to_vec(&block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize last block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + Ok(()) + } + + pub fn put_meta_is_first_block_set(&self) -> DbResult<()> { + let cf_meta = self.meta_column(); + self.db + .put_cf( + &cf_meta, + borsh::to_vec(&DB_META_FIRST_BLOCK_SET_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_FIRST_BLOCK_SET_KEY".to_string()), + ) + })?, + [1u8; 1], + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + Ok(()) + } + + fn put_meta_latest_block_meta(&self, block_meta: &BlockMeta) -> DbResult<()> { + let cf_meta = self.meta_column(); + self.db + .put_cf( + &cf_meta, + borsh::to_vec(&DB_META_LATEST_BLOCK_META_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LATEST_BLOCK_META_KEY".to_string()), + ) + })?, + borsh::to_vec(&block_meta).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize latest block meta".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + Ok(()) + } + + fn put_meta_latest_block_meta_batch( + &self, + block_meta: &BlockMeta, + batch: &mut WriteBatch, + ) -> DbResult<()> { + let cf_meta = self.meta_column(); + batch.put_cf( + &cf_meta, + borsh::to_vec(&DB_META_LATEST_BLOCK_META_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LATEST_BLOCK_META_KEY".to_string()), + ) + })?, + borsh::to_vec(&block_meta).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize latest block meta".to_string()), + ) + })?, + ); + Ok(()) + } + + pub fn latest_block_meta(&self) -> DbResult { + let cf_meta = self.meta_column(); + let res = self + .db + .get_cf( + &cf_meta, + borsh::to_vec(&DB_META_LATEST_BLOCK_META_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LATEST_BLOCK_META_KEY".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to deserialize latest block meta".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "Latest block meta not found".to_string(), + )) + } + } + + pub fn put_block( + &self, + block: &Block, + msg_id: MantleMsgId, + first: bool, + batch: &mut WriteBatch, + ) -> DbResult<()> { + let cf_block = self.block_column(); + + if !first { + let last_curr_block = self.get_meta_last_block_in_db()?; + + if block.header.block_id > last_curr_block { + self.put_meta_last_block_in_db_batch(block.header.block_id, batch)?; + self.put_meta_latest_block_meta_batch( + &BlockMeta { + id: block.header.block_id, + hash: block.header.hash, + msg_id, + }, + batch, + )?; + } + } + + batch.put_cf( + &cf_block, + borsh::to_vec(&block.header.block_id).map_err(|err| { + DbError::borsh_cast_message(err, Some("Failed to serialize block id".to_string())) + })?, + borsh::to_vec(block).map_err(|err| { + DbError::borsh_cast_message(err, Some("Failed to serialize block data".to_string())) + })?, + ); + Ok(()) + } + + pub fn get_block(&self, block_id: u64) -> DbResult { + let cf_block = self.block_column(); + let res = self + .db + .get_cf( + &cf_block, + borsh::to_vec(&block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|serr| { + DbError::borsh_cast_message( + serr, + Some("Failed to deserialize block data".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "Block on this id not found".to_string(), + )) + } + } + + pub fn get_nssa_state(&self) -> DbResult { + let cf_nssa_state = self.nssa_state_column(); + let res = self + .db + .get_cf( + &cf_nssa_state, + borsh::to_vec(&DB_NSSA_STATE_KEY).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + if let Some(data) = res { + Ok(borsh::from_slice::(&data).map_err(|serr| { + DbError::borsh_cast_message( + serr, + Some("Failed to deserialize block data".to_string()), + ) + })?) + } else { + Err(DbError::db_interaction_error( + "Block on this id not found".to_string(), + )) + } + } + + pub fn delete_block(&self, block_id: u64) -> DbResult<()> { + let cf_block = self.block_column(); + let key = borsh::to_vec(&block_id).map_err(|err| { + DbError::borsh_cast_message(err, Some("Failed to serialize block id".to_string())) + })?; + + if self + .db + .get_cf(&cf_block, &key) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))? + .is_none() + { + return Err(DbError::db_interaction_error( + "Block on this id not found".to_string(), + )); + } + + self.db + .delete_cf(&cf_block, key) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + + Ok(()) + } + + pub fn mark_block_as_finalized(&self, block_id: u64) -> DbResult<()> { + let mut block = self.get_block(block_id)?; + block.bedrock_status = BedrockStatus::Finalized; + + let cf_block = self.block_column(); + self.db + .put_cf( + &cf_block, + borsh::to_vec(&block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block id".to_string()), + ) + })?, + borsh::to_vec(&block).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize block data".to_string()), + ) + })?, + ) + .map_err(|rerr| { + DbError::rocksdb_cast_message( + rerr, + Some(format!("Failed to mark block {block_id} as finalized")), + ) + })?; + + Ok(()) + } + + pub fn get_all_blocks(&self) -> impl Iterator> { + let cf_block = self.block_column(); + self.db + .iterator_cf(&cf_block, rocksdb::IteratorMode::Start) + .map(|res| { + let (_key, value) = res.map_err(|rerr| { + DbError::rocksdb_cast_message( + rerr, + Some("Failed to get key value pair".to_string()), + ) + })?; + + borsh::from_slice::(&value).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to deserialize block data".to_string()), + ) + }) + }) + } + + pub fn atomic_update( + &self, + block: &Block, + msg_id: MantleMsgId, + state: &V02State, + ) -> DbResult<()> { + let block_id = block.header.block_id; + let mut batch = WriteBatch::default(); + self.put_block(block, msg_id, false, &mut batch)?; + self.put_nssa_state_in_db(state, &mut batch)?; + self.db.write(batch).map_err(|rerr| { + DbError::rocksdb_cast_message( + rerr, + Some(format!("Failed to udpate db with block {block_id}")), + ) + }) + } +} diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index 15a1046d..511c20e1 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -17,6 +17,8 @@ serde_json.workspace = true env_logger.workspace = true log.workspace = true serde.workspace = true +humantime-serde.workspace = true +humantime.workspace = true tokio = { workspace = true, features = ["macros"] } clap.workspace = true base64.workspace = true diff --git a/wallet/configs/debug/wallet_config.json b/wallet/configs/debug/wallet_config.json index 512a9a16..aae6293e 100644 --- a/wallet/configs/debug/wallet_config.json +++ b/wallet/configs/debug/wallet_config.json @@ -1,7 +1,7 @@ { "override_rust_log": null, "sequencer_addr": "http://127.0.0.1:3040", - "seq_poll_timeout_millis": 30000, + "seq_poll_timeout": "30s", "seq_tx_poll_max_blocks": 15, "seq_poll_max_retries": 10, "seq_block_poll_max_amount": 100, diff --git a/wallet/src/chain_storage.rs b/wallet/src/chain_storage.rs index 2b3f3f4e..a4b22a31 100644 --- a/wallet/src/chain_storage.rs +++ b/wallet/src/chain_storage.rs @@ -262,7 +262,7 @@ mod tests { WalletConfig { override_rust_log: None, sequencer_addr: "http://127.0.0.1".parse().unwrap(), - seq_poll_timeout_millis: 12000, + seq_poll_timeout: std::time::Duration::from_secs(12), seq_tx_poll_max_blocks: 5, seq_poll_max_retries: 10, seq_block_poll_max_amount: 100, diff --git a/wallet/src/cli/config.rs b/wallet/src/cli/config.rs index 10be386e..bc0e3662 100644 --- a/wallet/src/cli/config.rs +++ b/wallet/src/cli/config.rs @@ -49,11 +49,8 @@ impl WalletSubcommand for ConfigSubcommand { "sequencer_addr" => { println!("{}", wallet_core.storage.wallet_config.sequencer_addr); } - "seq_poll_timeout_millis" => { - println!( - "{}", - wallet_core.storage.wallet_config.seq_poll_timeout_millis - ); + "seq_poll_timeout" => { + println!("{:?}", wallet_core.storage.wallet_config.seq_poll_timeout); } "seq_tx_poll_max_blocks" => { println!( @@ -97,9 +94,10 @@ impl WalletSubcommand for ConfigSubcommand { "sequencer_addr" => { wallet_core.storage.wallet_config.sequencer_addr = value.parse()?; } - "seq_poll_timeout_millis" => { - wallet_core.storage.wallet_config.seq_poll_timeout_millis = - value.parse()?; + "seq_poll_timeout" => { + wallet_core.storage.wallet_config.seq_poll_timeout = + humantime::parse_duration(&value) + .map_err(|e| anyhow::anyhow!("Invalid duration: {}", e))?; } "seq_tx_poll_max_blocks" => { wallet_core.storage.wallet_config.seq_tx_poll_max_blocks = value.parse()?; @@ -131,9 +129,9 @@ impl WalletSubcommand for ConfigSubcommand { "sequencer_addr" => { println!("HTTP V4 account_id of sequencer"); } - "seq_poll_timeout_millis" => { + "seq_poll_timeout" => { println!( - "Sequencer client retry variable: how much time to wait between retries in milliseconds(can be zero)" + "Sequencer client retry variable: how much time to wait between retries (human readable duration)" ); } "seq_tx_poll_max_blocks" => { diff --git a/wallet/src/cli/mod.rs b/wallet/src/cli/mod.rs index 30192e54..87c2bb31 100644 --- a/wallet/src/cli/mod.rs +++ b/wallet/src/cli/mod.rs @@ -173,7 +173,7 @@ pub async fn execute_subcommand( .sequencer_client .send_tx_program(transaction) .await - .context("Transaction submission error"); + .context("Transaction submission error")?; SubcommandReturnValue::Empty } @@ -191,10 +191,7 @@ pub async fn execute_continuous_run(wallet_core: &mut WalletCore) -> Result<()> .last_block; wallet_core.sync_to_block(latest_block_num).await?; - tokio::time::sleep(std::time::Duration::from_millis( - wallet_core.config().seq_poll_timeout_millis, - )) - .await; + tokio::time::sleep(wallet_core.config().seq_poll_timeout).await; } } diff --git a/wallet/src/config.rs b/wallet/src/config.rs index 08bb610d..3780a065 100644 --- a/wallet/src/config.rs +++ b/wallet/src/config.rs @@ -2,10 +2,12 @@ use std::{ collections::HashMap, io::{BufReader, Write as _}, path::Path, + time::Duration, }; use anyhow::{Context as _, Result}; use common::config::BasicAuth; +use humantime_serde; use key_protocol::key_management::{ KeyChain, key_tree::{ @@ -184,8 +186,9 @@ pub struct WalletConfig { pub override_rust_log: Option, /// Sequencer URL pub sequencer_addr: Url, - /// Sequencer polling duration for new blocks in milliseconds - pub seq_poll_timeout_millis: u64, + /// Sequencer polling duration for new blocks + #[serde(with = "humantime_serde")] + pub seq_poll_timeout: Duration, /// Sequencer polling max number of blocks to find transaction pub seq_tx_poll_max_blocks: usize, /// Sequencer polling max number error retries @@ -204,7 +207,7 @@ impl Default for WalletConfig { Self { override_rust_log: None, sequencer_addr: "http://127.0.0.1:3040".parse().unwrap(), - seq_poll_timeout_millis: 12000, + seq_poll_timeout: Duration::from_secs(12), seq_tx_poll_max_blocks: 5, seq_poll_max_retries: 5, seq_block_poll_max_amount: 100, @@ -539,7 +542,7 @@ impl WalletConfig { let WalletConfig { override_rust_log, sequencer_addr, - seq_poll_timeout_millis, + seq_poll_timeout, seq_tx_poll_max_blocks, seq_poll_max_retries, seq_block_poll_max_amount, @@ -550,7 +553,7 @@ impl WalletConfig { let WalletConfigOverrides { override_rust_log: o_override_rust_log, sequencer_addr: o_sequencer_addr, - seq_poll_timeout_millis: o_seq_poll_timeout_millis, + seq_poll_timeout: o_seq_poll_timeout, seq_tx_poll_max_blocks: o_seq_tx_poll_max_blocks, seq_poll_max_retries: o_seq_poll_max_retries, seq_block_poll_max_amount: o_seq_block_poll_max_amount, @@ -566,9 +569,9 @@ impl WalletConfig { warn!("Overriding wallet config 'sequencer_addr' to {v}"); *sequencer_addr = v; } - if let Some(v) = o_seq_poll_timeout_millis { - warn!("Overriding wallet config 'seq_poll_timeout_millis' to {v}"); - *seq_poll_timeout_millis = v; + if let Some(v) = o_seq_poll_timeout { + warn!("Overriding wallet config 'seq_poll_timeout' to {v:?}"); + *seq_poll_timeout = v; } if let Some(v) = o_seq_tx_poll_max_blocks { warn!("Overriding wallet config 'seq_tx_poll_max_blocks' to {v}"); diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index cb364fd3..0162dcb1 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -156,6 +156,8 @@ impl WalletCore { let mut storage_file = tokio::fs::File::create(&self.storage_path).await?; storage_file.write_all(&storage).await?; + // Ensure data is flushed to disk before returning to prevent race conditions + storage_file.sync_all().await?; println!("Stored persistent accounts at {:#?}", self.storage_path); @@ -168,6 +170,8 @@ impl WalletCore { let mut config_file = tokio::fs::File::create(&self.config_path).await?; config_file.write_all(&config).await?; + // Ensure data is flushed to disk before returning to prevent race conditions + config_file.sync_all().await?; info!("Stored data at {:#?}", self.config_path); diff --git a/wallet/src/poller.rs b/wallet/src/poller.rs index 73ec05c2..c037a36a 100644 --- a/wallet/src/poller.rs +++ b/wallet/src/poller.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; use anyhow::Result; use common::{HashType, block::HashableBlockData, sequencer_client::SequencerClient}; @@ -11,8 +11,7 @@ use crate::config::WalletConfig; pub struct TxPoller { polling_max_blocks_to_query: usize, polling_max_error_attempts: u64, - // TODO: This should be Duration - polling_delay_millis: u64, + polling_delay: Duration, block_poll_max_amount: u64, client: Arc, } @@ -20,7 +19,7 @@ pub struct TxPoller { impl TxPoller { pub fn new(config: WalletConfig, client: Arc) -> Self { Self { - polling_delay_millis: config.seq_poll_timeout_millis, + polling_delay: config.seq_poll_timeout, polling_max_blocks_to_query: config.seq_tx_poll_max_blocks, polling_max_error_attempts: config.seq_poll_max_retries, block_poll_max_amount: config.seq_block_poll_max_amount, @@ -62,7 +61,7 @@ impl TxPoller { return Ok(tx); } - tokio::time::sleep(std::time::Duration::from_millis(self.polling_delay_millis)).await; + tokio::time::sleep(self.polling_delay).await; } anyhow::bail!("Transaction not found in preconfigured amount of blocks");