From b9f27ae40df004f035adb25597614805a4142746 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 16 Dec 2024 14:56:04 -0500 Subject: [PATCH] feat(katana): `getStorageProof` endpoint (#2809) --- Cargo.lock | 674 +++++++++--------- Cargo.toml | 5 + crates/katana/core/src/backend/mod.rs | 39 +- crates/katana/core/src/backend/storage.rs | 12 +- crates/katana/executor/Cargo.toml | 1 + crates/katana/executor/src/abstraction/mod.rs | 35 +- .../src/implementation/blockifier/state.rs | 40 +- .../executor/src/implementation/noop.rs | 37 +- crates/katana/primitives/src/lib.rs | 1 + crates/katana/rpc/rpc-api/src/starknet.rs | 16 +- .../rpc/rpc-types-builder/src/state_update.rs | 19 +- crates/katana/rpc/rpc-types/Cargo.toml | 1 + crates/katana/rpc/rpc-types/src/lib.rs | 1 + crates/katana/rpc/rpc-types/src/trie.rs | 193 +++++ crates/katana/rpc/rpc/Cargo.toml | 1 + crates/katana/rpc/rpc/src/starknet/mod.rs | 75 +- crates/katana/rpc/rpc/src/starknet/read.rs | 17 +- crates/katana/rpc/rpc/tests/proofs.rs | 64 ++ crates/katana/runner/macro/Cargo.toml | 6 +- .../katana/storage/codecs/derive/Cargo.toml | 6 +- crates/katana/storage/db/Cargo.toml | 4 +- .../storage/db/src/abstraction/transaction.rs | 112 +++ crates/katana/storage/db/src/mdbx/tx.rs | 2 +- crates/katana/storage/db/src/models/trie.rs | 49 +- crates/katana/storage/db/src/tables.rs | 110 ++- crates/katana/storage/db/src/trie/class.rs | 55 -- crates/katana/storage/db/src/trie/contract.rs | 83 --- crates/katana/storage/db/src/trie/mod.rs | 293 +++++++- crates/katana/storage/db/src/trie/snapshot.rs | 123 ++++ crates/katana/storage/db/src/version.rs | 4 +- crates/katana/storage/provider/src/lib.rs | 79 +- .../storage/provider/src/providers/db/mod.rs | 22 +- .../provider/src/providers/db/state.rs | 119 +++- .../storage/provider/src/providers/db/trie.rs | 121 ++-- .../provider/src/providers/fork/backend.rs | 37 +- .../provider/src/providers/fork/mod.rs | 34 +- .../provider/src/providers/fork/state.rs | 97 ++- .../storage/provider/src/traits/state.rs | 36 +- .../storage/provider/src/traits/trie.rs | 9 +- crates/katana/storage/provider/tests/block.rs | 14 +- crates/katana/trie/Cargo.toml | 4 +- crates/katana/trie/src/classes.rs | 45 ++ crates/katana/trie/src/contracts.rs | 43 ++ crates/katana/trie/src/id.rs | 38 + crates/katana/trie/src/lib.rs | 82 ++- crates/katana/trie/src/storages.rs | 47 ++ crates/saya/core/src/blockchain/mod.rs | 6 +- spawn-and-move-db.tar.gz | Bin 5711864 -> 1791575 bytes types-test-db.tar.gz | Bin 1293323 -> 1406613 bytes 49 files changed, 2130 insertions(+), 781 deletions(-) create mode 100644 crates/katana/rpc/rpc-types/src/trie.rs create mode 100644 crates/katana/rpc/rpc/tests/proofs.rs delete mode 100644 crates/katana/storage/db/src/trie/class.rs delete mode 100644 crates/katana/storage/db/src/trie/contract.rs create mode 100644 crates/katana/storage/db/src/trie/snapshot.rs create mode 100644 crates/katana/trie/src/classes.rs create mode 100644 crates/katana/trie/src/contracts.rs create mode 100644 crates/katana/trie/src/id.rs create mode 100644 crates/katana/trie/src/storages.rs diff --git a/Cargo.lock b/Cargo.lock index 388e03e252..9a1c82a5c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,7 +45,7 @@ dependencies = [ "starknet 0.12.0", "starknet-crypto 0.7.2", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", "tokio", "toml 0.8.19", "tsify-next", @@ -251,7 +251,7 @@ dependencies = [ "alloy-transport 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures", "futures-util", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -270,7 +270,7 @@ dependencies = [ "alloy-transport 0.3.6 (git+https://github.com/alloy-rs/alloy)", "futures", "futures-util", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -391,7 +391,7 @@ dependencies = [ "alloy-sol-types", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tracing", ] @@ -404,7 +404,7 @@ dependencies = [ "alloy-sol-types", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tracing", ] @@ -426,7 +426,7 @@ dependencies = [ "async-trait", "auto_impl", "futures-utils-wasm", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -446,7 +446,7 @@ dependencies = [ "async-trait", "auto_impl", "futures-utils-wasm", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -483,7 +483,7 @@ dependencies = [ "rand 0.8.5", "serde_json", "tempfile", - "thiserror", + "thiserror 1.0.63", "tracing", "url", ] @@ -541,7 +541,7 @@ dependencies = [ "reqwest 0.12.7", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "url", @@ -577,7 +577,7 @@ dependencies = [ "reqwest 0.12.7", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "url", @@ -602,7 +602,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -729,7 +729,7 @@ dependencies = [ "auto_impl", "elliptic-curve", "k256", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -742,7 +742,7 @@ dependencies = [ "auto_impl", "elliptic-curve", "k256", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -757,7 +757,7 @@ dependencies = [ "async-trait", "k256", "rand 0.8.5", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -771,7 +771,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -788,7 +788,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "syn-solidity", "tiny-keccak", ] @@ -806,7 +806,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.77", + "syn 2.0.90", "syn-solidity", ] @@ -845,7 +845,7 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tokio", "tower 0.5.1", "tracing", @@ -863,7 +863,7 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tokio", "tower 0.5.1", "tracing", @@ -1017,7 +1017,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1259,7 +1259,7 @@ dependencies = [ "nom", "num-traits 0.2.19", "rusticata-macros", - "thiserror", + "thiserror 1.0.63", "time", ] @@ -1275,7 +1275,7 @@ dependencies = [ "nom", "num-traits 0.2.19", "rusticata-macros", - "thiserror", + "thiserror 1.0.63", "time", ] @@ -1299,7 +1299,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "synstructure 0.13.1", ] @@ -1322,7 +1322,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1445,7 +1445,7 @@ dependencies = [ "serde_urlencoded", "static_assertions_next", "tempfile", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1461,8 +1461,8 @@ dependencies = [ "proc-macro2", "quote", "strum 0.26.3", - "syn 2.0.77", - "thiserror", + "syn 2.0.90", + "thiserror 1.0.63", ] [[package]] @@ -1568,7 +1568,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1616,7 +1616,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1633,7 +1633,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1683,7 +1683,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1989,7 +1989,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2011,7 +2011,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.77", + "syn 2.0.90", "which 4.4.2", ] @@ -2143,7 +2143,7 @@ dependencies = [ "starknet_api", "strum 0.25.0", "strum_macros 0.25.3", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2168,7 +2168,7 @@ dependencies = [ "cid", "dashmap 6.1.0", "multihash 0.19.1", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2186,7 +2186,7 @@ dependencies = [ [[package]] name = "bonsai-trie" version = "0.1.0" -source = "git+https://github.com/madara-alliance/bonsai-trie/?rev=56d7d62#56d7d62232fd72419f1d50de8bc747b70a9db68f" +source = "git+https://github.com/dojoengine/bonsai-trie/?branch=kariy/public-path#9108a1264fd82f5e68f5056f8df767042440825f" dependencies = [ "bitvec", "derive_more 0.99.18", @@ -2195,8 +2195,10 @@ dependencies = [ "parity-scale-codec", "rayon", "serde", + "slotmap", "smallvec", "starknet-types-core", + "thiserror 2.0.6", ] [[package]] @@ -2219,7 +2221,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "syn_derive", ] @@ -2403,7 +2405,7 @@ dependencies = [ "hashbrown 0.13.2", "instant", "once_cell", - "thiserror", + "thiserror 1.0.63", "tokio", ] @@ -2445,7 +2447,7 @@ dependencies = [ "serde_json", "starknet 0.12.0", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", "tracing", "tracing-subscriber", "url", @@ -2471,7 +2473,7 @@ dependencies = [ "serde_json", "starknet 0.12.0", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", "tracing", "tracing-subscriber", "url", @@ -2497,7 +2499,7 @@ dependencies = [ "serde_json", "starknet 0.12.0", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", "tracing", "tracing-subscriber", "url", @@ -2512,7 +2514,7 @@ dependencies = [ "serde", "serde_with 3.11.0", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2522,7 +2524,7 @@ source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.2#4e3924fb82b7299 dependencies = [ "serde", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2532,7 +2534,7 @@ source = "git+https://github.com/cartridge-gg/cainome?rev=5c2616c273faca7700d2ba dependencies = [ "serde", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2542,7 +2544,7 @@ source = "git+https://github.com/cartridge-gg/cainome?tag=v0.4.10#6fefd8bf4370c7 dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "unzip-n", ] @@ -2553,7 +2555,7 @@ source = "git+https://github.com/cartridge-gg/cainome?rev=5c2616c273faca7700d2ba dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "unzip-n", ] @@ -2566,8 +2568,8 @@ dependencies = [ "quote", "serde_json", "starknet 0.12.0", - "syn 2.0.77", - "thiserror", + "syn 2.0.90", + "thiserror 1.0.63", ] [[package]] @@ -2579,8 +2581,8 @@ dependencies = [ "quote", "serde_json", "starknet 0.12.0", - "syn 2.0.77", - "thiserror", + "syn 2.0.90", + "thiserror 1.0.63", ] [[package]] @@ -2592,8 +2594,8 @@ dependencies = [ "quote", "serde_json", "starknet 0.12.0", - "syn 2.0.77", - "thiserror", + "syn 2.0.90", + "thiserror 1.0.63", ] [[package]] @@ -2610,8 +2612,8 @@ dependencies = [ "quote", "serde_json", "starknet 0.12.0", - "syn 2.0.77", - "thiserror", + "syn 2.0.90", + "thiserror 1.0.63", ] [[package]] @@ -2628,8 +2630,8 @@ dependencies = [ "quote", "serde_json", "starknet 0.12.0", - "syn 2.0.77", - "thiserror", + "syn 2.0.90", + "thiserror 1.0.63", ] [[package]] @@ -2646,8 +2648,8 @@ dependencies = [ "quote", "serde_json", "starknet 0.12.0", - "syn 2.0.77", - "thiserror", + "syn 2.0.90", + "thiserror 1.0.63", ] [[package]] @@ -2664,8 +2666,8 @@ dependencies = [ "quote", "serde_json", "starknet 0.12.0", - "syn 2.0.77", - "thiserror", + "syn 2.0.90", + "thiserror 1.0.63", ] [[package]] @@ -2682,8 +2684,8 @@ dependencies = [ "quote", "serde_json", "starknet 0.12.0", - "syn 2.0.77", - "thiserror", + "syn 2.0.90", + "thiserror 1.0.63", ] [[package]] @@ -2700,8 +2702,8 @@ dependencies = [ "quote", "serde_json", "starknet 0.12.0", - "syn 2.0.77", - "thiserror", + "syn 2.0.90", + "thiserror 1.0.63", ] [[package]] @@ -2741,7 +2743,7 @@ dependencies = [ "rust-analyzer-salsa", "semver 1.0.23", "smol_str", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2840,7 +2842,7 @@ dependencies = [ "rust-analyzer-salsa", "serde", "smol_str", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2922,7 +2924,7 @@ checksum = "e32e958decd95ae122ee64daa26721da2f76e83231047f947fd9cdc5d3c90cc6" dependencies = [ "quote", "scarb-stable-hash 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2978,7 +2980,7 @@ checksum = "d72f17373740f242d6995e896b9195c2cedff7e8b14e496afdd16b405039d1fb" dependencies = [ "cairo-lang-debug", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2991,7 +2993,7 @@ dependencies = [ "cairo-lang-utils", "serde", "smol_str", - "thiserror", + "thiserror 1.0.63", "toml 0.8.19", ] @@ -3023,7 +3025,7 @@ dependencies = [ "sha2 0.10.8", "smol_str", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3076,7 +3078,7 @@ dependencies = [ "sha3", "smol_str", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3092,7 +3094,7 @@ dependencies = [ "itertools 0.12.1", "num-bigint", "num-traits 0.2.19", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3108,7 +3110,7 @@ dependencies = [ "itertools 0.12.1", "num-bigint", "num-traits 0.2.19", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3153,7 +3155,7 @@ dependencies = [ "num-bigint", "num-traits 0.2.19", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3193,7 +3195,7 @@ dependencies = [ "serde_json", "smol_str", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3216,7 +3218,7 @@ dependencies = [ "sha3", "smol_str", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3434,7 +3436,7 @@ dependencies = [ "serde", "serde_json", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3466,7 +3468,7 @@ dependencies = [ "semver 1.0.23", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3532,7 +3534,7 @@ dependencies = [ "http 1.1.0", "jsonrpsee 0.24.6", "serde", - "thiserror", + "thiserror 1.0.63", "tracing", ] @@ -3608,7 +3610,7 @@ dependencies = [ "serde", "serde_repr", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.63", "time", ] @@ -3802,7 +3804,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -3814,7 +3816,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -3938,7 +3940,7 @@ dependencies = [ "mime", "mime_guess", "rand 0.8.5", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -4367,7 +4369,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -4428,7 +4430,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -4450,7 +4452,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -4533,7 +4535,7 @@ dependencies = [ "monch", "os_pipe", "path-dedot", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-util", ] @@ -4606,7 +4608,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -4627,7 +4629,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -4637,7 +4639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" dependencies = [ "derive_builder_core", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -4650,7 +4652,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -4670,7 +4672,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "unicode-xid", ] @@ -4683,7 +4685,7 @@ dependencies = [ "console", "shell-words", "tempfile", - "thiserror", + "thiserror 1.0.63", "zeroize", ] @@ -4808,7 +4810,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -4836,7 +4838,7 @@ dependencies = [ "serde_json", "sozo-scarbext", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", "tokio", ] @@ -4928,7 +4930,7 @@ dependencies = [ "metrics-exporter-prometheus", "metrics-process", "metrics-util", - "thiserror", + "thiserror 1.0.63", "tracing", ] @@ -4950,7 +4952,7 @@ dependencies = [ "serde", "serde_json", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", "toml 0.8.19", "url", ] @@ -4973,7 +4975,7 @@ dependencies = [ "starknet-crypto 0.7.2", "strum 0.25.0", "strum_macros 0.25.3", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -4994,7 +4996,7 @@ dependencies = [ "starknet-crypto 0.7.2", "strum 0.25.0", "strum_macros 0.25.3", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -5010,7 +5012,7 @@ dependencies = [ "rpassword", "serde_json", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -5035,7 +5037,7 @@ dependencies = [ "serde_with 3.11.0", "starknet 0.12.0", "starknet-crypto 0.7.2", - "thiserror", + "thiserror 1.0.63", "tokio", "toml 0.8.19", "tracing", @@ -5215,7 +5217,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -5227,7 +5229,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -5308,7 +5310,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "sha3", - "thiserror", + "thiserror 1.0.63", "uuid 0.8.2", ] @@ -5706,7 +5708,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -5796,7 +5798,7 @@ checksum = "553630feadf7b76442b0849fd25fdf89b860d933623aec9693fed19af0400c78" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -5904,7 +5906,7 @@ dependencies = [ "regex", "signal-hook", "smallvec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -5917,7 +5919,7 @@ dependencies = [ "gix-date", "gix-utils", "itoa", - "thiserror", + "thiserror 1.0.63", "winnow", ] @@ -5932,7 +5934,7 @@ dependencies = [ "gix-object", "gix-worktree-stream", "jiff", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -5948,7 +5950,7 @@ dependencies = [ "gix-trace", "kstring", "smallvec", - "thiserror", + "thiserror 1.0.63", "unicode-bom", ] @@ -5958,7 +5960,7 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a371db66cbd4e13f0ed9dc4c0fea712d7276805fccc877f77e96374d317e87ae" dependencies = [ - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -5967,7 +5969,7 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45c8751169961ba7640b513c3b24af61aa962c967aaf04116734975cd5af0c52" dependencies = [ - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -5993,7 +5995,7 @@ dependencies = [ "gix-features", "gix-hash", "memmap2", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6012,7 +6014,7 @@ dependencies = [ "memchr", "once_cell", "smallvec", - "thiserror", + "thiserror 1.0.63", "unicode-bom", "winnow", ] @@ -6027,7 +6029,7 @@ dependencies = [ "bstr", "gix-path", "libc", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6044,7 +6046,7 @@ dependencies = [ "gix-sec", "gix-trace", "gix-url", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6056,7 +6058,7 @@ dependencies = [ "bstr", "itoa", "jiff", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6076,7 +6078,7 @@ dependencies = [ "gix-trace", "gix-worktree", "imara-diff", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6096,7 +6098,7 @@ dependencies = [ "gix-trace", "gix-utils", "gix-worktree", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6112,7 +6114,7 @@ dependencies = [ "gix-path", "gix-ref", "gix-sec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6134,7 +6136,7 @@ dependencies = [ "parking_lot 0.12.3", "prodash", "sha1_smol", - "thiserror", + "thiserror 1.0.63", "walkdir", ] @@ -6156,7 +6158,7 @@ dependencies = [ "gix-trace", "gix-utils", "smallvec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6189,7 +6191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93d7df7366121b5018f947a04d37f034717e113dcf9ccd85c34b58e57a74d5e" dependencies = [ "faster-hex", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6241,7 +6243,7 @@ dependencies = [ "memmap2", "rustix 0.38.37", "smallvec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6252,7 +6254,7 @@ checksum = "e3bc7fe297f1f4614774989c00ec8b1add59571dc9b024b4c00acb7dedd4e19d" dependencies = [ "gix-tempfile", "gix-utils", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6264,7 +6266,7 @@ dependencies = [ "bstr", "gix-actor", "gix-date", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6280,7 +6282,7 @@ dependencies = [ "gix-object", "gix-revwalk", "smallvec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6298,7 +6300,7 @@ dependencies = [ "gix-validate", "itoa", "smallvec", - "thiserror", + "thiserror 1.0.63", "winnow", ] @@ -6319,7 +6321,7 @@ dependencies = [ "gix-quote", "parking_lot 0.12.3", "tempfile", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6337,7 +6339,7 @@ dependencies = [ "gix-path", "memmap2", "smallvec", - "thiserror", + "thiserror 1.0.63", "uluru", ] @@ -6350,7 +6352,7 @@ dependencies = [ "bstr", "faster-hex", "gix-trace", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6363,7 +6365,7 @@ dependencies = [ "gix-trace", "home", "once_cell", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6378,7 +6380,7 @@ dependencies = [ "gix-config-value", "gix-glob", "gix-path", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6391,7 +6393,7 @@ dependencies = [ "gix-config-value", "parking_lot 0.12.3", "rustix 0.38.37", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6402,7 +6404,7 @@ checksum = "cbff4f9b9ea3fa7a25a70ee62f545143abef624ac6aa5884344e70c8b0a1d9ff" dependencies = [ "bstr", "gix-utils", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6422,7 +6424,7 @@ dependencies = [ "gix-utils", "gix-validate", "memmap2", - "thiserror", + "thiserror 1.0.63", "winnow", ] @@ -6437,7 +6439,7 @@ dependencies = [ "gix-revision", "gix-validate", "smallvec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6453,7 +6455,7 @@ dependencies = [ "gix-object", "gix-revwalk", "gix-trace", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6468,7 +6470,7 @@ dependencies = [ "gix-hashtable", "gix-object", "smallvec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6503,7 +6505,7 @@ dependencies = [ "gix-pathspec", "gix-worktree", "portable-atomic", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6518,7 +6520,7 @@ dependencies = [ "gix-pathspec", "gix-refspec", "gix-url", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6557,7 +6559,7 @@ dependencies = [ "gix-object", "gix-revwalk", "smallvec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6570,7 +6572,7 @@ dependencies = [ "gix-features", "gix-path", "home", - "thiserror", + "thiserror 1.0.63", "url", ] @@ -6592,7 +6594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81f2badbb64e57b404593ee26b752c26991910fd0d81fe6f9a71c1a8309b6c86" dependencies = [ "bstr", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6631,7 +6633,7 @@ dependencies = [ "gix-path", "gix-worktree", "io-close", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6649,7 +6651,7 @@ dependencies = [ "gix-path", "gix-traverse", "parking_lot 0.12.3", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6696,7 +6698,7 @@ dependencies = [ "pin-project", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -6717,7 +6719,7 @@ dependencies = [ "pin-project", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -6799,7 +6801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ebc8013b4426d5b81a4364c419a95ed0b404af2b82e2457de52d9348f0e474" dependencies = [ "combine 3.8.1", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -6917,7 +6919,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -7029,7 +7031,7 @@ dependencies = [ "serde", "serde_json", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "tracing-log 0.1.4", @@ -7075,7 +7077,7 @@ dependencies = [ "once_cell", "rand 0.8.5", "socket2 0.5.7", - "thiserror", + "thiserror 1.0.63", "tinyvec", "tokio", "tracing", @@ -7098,7 +7100,7 @@ dependencies = [ "rand 0.8.5", "resolv-conf", "smallvec", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -7785,7 +7787,7 @@ dependencies = [ "rand 0.8.5", "rtcp", "rtp 0.9.0", - "thiserror", + "thiserror 1.0.63", "tokio", "waitgroup", "webrtc-srtp", @@ -7800,7 +7802,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -7850,7 +7852,7 @@ dependencies = [ "hyper-multipart-rfc7578", "hyper-rustls 0.23.2", "ipfs-api-prelude", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -7870,7 +7872,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-util", "tracing", @@ -8018,7 +8020,7 @@ dependencies = [ "combine 4.6.7", "jni-sys", "log", - "thiserror", + "thiserror 1.0.63", "walkdir", ] @@ -8033,7 +8035,7 @@ dependencies = [ "combine 4.6.7", "jni-sys", "log", - "thiserror", + "thiserror 1.0.63", "walkdir", "windows-sys 0.45.0", ] @@ -8116,7 +8118,7 @@ dependencies = [ "pin-project", "rustls-native-certs 0.6.3", "soketto 0.7.1", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-rustls 0.24.1", "tokio-util", @@ -8139,7 +8141,7 @@ dependencies = [ "rustls-pki-types", "rustls-platform-verifier", "soketto 0.8.0", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-rustls 0.26.0", "tokio-util", @@ -8170,7 +8172,7 @@ dependencies = [ "serde", "serde_json", "soketto 0.7.1", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "wasm-bindgen-futures", @@ -8194,7 +8196,7 @@ dependencies = [ "rustc-hash 2.0.0", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-stream", "tracing", @@ -8214,7 +8216,7 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -8237,7 +8239,7 @@ dependencies = [ "rustls-platform-verifier", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tokio", "tower 0.4.13", "tracing", @@ -8267,7 +8269,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -8302,7 +8304,7 @@ dependencies = [ "beef", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "tracing", ] @@ -8315,7 +8317,7 @@ dependencies = [ "http 1.1.0", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -8442,7 +8444,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -8480,7 +8482,7 @@ dependencies = [ "starknet 0.12.0", "starknet-types-core", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "url", @@ -8492,7 +8494,6 @@ version = "1.0.8" dependencies = [ "anyhow", "arbitrary", - "bitvec", "criterion", "dojo-metrics", "katana-primitives", @@ -8507,9 +8508,8 @@ dependencies = [ "serde_json", "smallvec", "starknet 0.12.0", - "starknet-types-core", "tempfile", - "thiserror", + "thiserror 1.0.63", "tracing", ] @@ -8525,6 +8525,7 @@ dependencies = [ "katana-primitives", "katana-provider", "katana-rpc-types", + "katana-trie", "num-traits 0.2.19", "oneshot", "parking_lot 0.12.3", @@ -8535,7 +8536,7 @@ dependencies = [ "serde_json", "similar-asserts", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -8549,7 +8550,7 @@ dependencies = [ "reqwest 0.11.27", "serde", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "url", @@ -8613,7 +8614,7 @@ dependencies = [ "serde_json", "starknet 0.12.0", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "url", @@ -8628,7 +8629,7 @@ dependencies = [ "katana-primitives", "katana-provider", "katana-stage", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -8644,7 +8645,7 @@ dependencies = [ "katana-provider", "parking_lot 0.12.3", "rand 0.8.5", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -8677,7 +8678,7 @@ dependencies = [ "starknet-types-core", "strum 0.25.0", "strum_macros 0.25.3", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -8702,7 +8703,7 @@ dependencies = [ "starknet 0.12.0", "starknet-types-core", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "url", @@ -8735,6 +8736,7 @@ dependencies = [ "katana-rpc-types", "katana-rpc-types-builder", "katana-tasks", + "katana-trie", "metrics", "num-traits 0.2.19", "rand 0.8.5", @@ -8744,7 +8746,7 @@ dependencies = [ "similar-asserts", "starknet 0.12.0", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tower 0.4.13", "tower-http 0.4.4", @@ -8779,6 +8781,7 @@ dependencies = [ "katana-pool", "katana-primitives", "katana-provider", + "katana-trie", "num-traits 0.2.19", "rstest 0.18.2", "serde", @@ -8787,7 +8790,7 @@ dependencies = [ "serde_with 3.11.0", "similar-asserts", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -8821,7 +8824,7 @@ version = "1.0.8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -8860,7 +8863,7 @@ dependencies = [ "katana-tasks", "num-traits 0.2.19", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -8871,7 +8874,7 @@ version = "1.0.8" dependencies = [ "futures", "rayon", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-metrics", "tokio-util", @@ -8890,7 +8893,7 @@ dependencies = [ "slab", "starknet 0.12.0", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -9032,7 +9035,7 @@ checksum = "ee58dbc414bd23885d7da915e0457618b36d1fc950a6169ef2cb29829d1b1a1d" dependencies = [ "bytes", "lazy_static", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -9099,7 +9102,7 @@ dependencies = [ "multiaddr 0.18.1", "pin-project", "rw-stream-sink", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -9144,7 +9147,7 @@ dependencies = [ "rand 0.8.5", "rw-stream-sink", "smallvec", - "thiserror", + "thiserror 1.0.63", "tracing", "unsigned-varint 0.8.0", "void", @@ -9213,7 +9216,7 @@ dependencies = [ "quick-protobuf", "quick-protobuf-codec", "smallvec", - "thiserror", + "thiserror 1.0.63", "tracing", "void", ] @@ -9231,7 +9234,7 @@ dependencies = [ "quick-protobuf", "rand 0.8.5", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.63", "tracing", "zeroize", ] @@ -9293,7 +9296,7 @@ dependencies = [ "sha2 0.10.8", "snow", "static_assertions", - "thiserror", + "thiserror 1.0.63", "tracing", "x25519-dalek", "zeroize", @@ -9334,7 +9337,7 @@ dependencies = [ "ring 0.17.8", "rustls 0.23.13", "socket2 0.5.7", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -9357,7 +9360,7 @@ dependencies = [ "quick-protobuf-codec", "rand 0.8.5", "static_assertions", - "thiserror", + "thiserror 1.0.63", "tracing", "void", "web-time", @@ -9396,7 +9399,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -9428,7 +9431,7 @@ dependencies = [ "ring 0.17.8", "rustls 0.23.13", "rustls-webpki 0.101.7", - "thiserror", + "thiserror 1.0.63", "x509-parser 0.16.0", "yasna", ] @@ -9468,7 +9471,7 @@ dependencies = [ "rcgen", "serde", "stun 0.6.0", - "thiserror", + "thiserror 1.0.63", "tinytemplate", "tokio", "tokio-util", @@ -9493,7 +9496,7 @@ dependencies = [ "rand 0.8.5", "serde", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.63", "tinytemplate", "tracing", ] @@ -9512,7 +9515,7 @@ dependencies = [ "libp2p-identity", "libp2p-webrtc-utils", "send_wrapper 0.6.0", - "thiserror", + "thiserror 1.0.63", "tracing", "wasm-bindgen", "wasm-bindgen-futures", @@ -9533,7 +9536,7 @@ dependencies = [ "pin-project-lite", "rw-stream-sink", "soketto 0.8.0", - "thiserror", + "thiserror 1.0.63", "tracing", "url", "webpki-roots 0.25.4", @@ -9550,7 +9553,7 @@ dependencies = [ "libp2p-core", "parking_lot 0.12.3", "send_wrapper 0.6.0", - "thiserror", + "thiserror 1.0.63", "tracing", "wasm-bindgen", "web-sys", @@ -9564,7 +9567,7 @@ dependencies = [ "either", "futures", "libp2p-core", - "thiserror", + "thiserror 1.0.63", "tracing", "yamux 0.12.1", "yamux 0.13.3", @@ -9626,7 +9629,7 @@ checksum = "cb26336e6dc7cc76e7927d2c9e7e3bb376d7af65a6f56a0b16c47d18a9b1abc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -9810,7 +9813,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -9829,7 +9832,7 @@ dependencies = [ "metrics", "metrics-util", "quanta", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -10163,7 +10166,7 @@ dependencies = [ "anyhow", "byteorder", "paste", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -10177,7 +10180,7 @@ dependencies = [ "log", "netlink-packet-core", "netlink-sys", - "thiserror", + "thiserror 1.0.63", "tokio", ] @@ -10390,7 +10393,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -10508,7 +10511,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -10609,7 +10612,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -10888,7 +10891,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.63", "ucd-trie", ] @@ -10912,7 +10915,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -10966,7 +10969,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -11010,7 +11013,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -11201,7 +11204,7 @@ dependencies = [ "smallvec", "symbolic-demangle", "tempfile", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -11272,7 +11275,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -11303,7 +11306,7 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ - "thiserror", + "thiserror 1.0.63", "toml 0.5.11", ] @@ -11359,14 +11362,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -11420,7 +11423,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -11443,7 +11446,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -11474,7 +11477,7 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -11514,7 +11517,7 @@ dependencies = [ "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn 2.0.77", + "syn 2.0.90", "tempfile", ] @@ -11535,7 +11538,7 @@ dependencies = [ "prost 0.13.3", "prost-types 0.13.3", "regex", - "syn 2.0.77", + "syn 2.0.90", "tempfile", ] @@ -11549,7 +11552,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -11562,7 +11565,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -11599,7 +11602,7 @@ dependencies = [ "serde", "serde_json", "starknet-types-core", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "url", @@ -11694,7 +11697,7 @@ dependencies = [ "asynchronous-codec", "bytes", "quick-protobuf", - "thiserror", + "thiserror 1.0.63", "unsigned-varint 0.8.0", ] @@ -11721,7 +11724,7 @@ dependencies = [ "rustc-hash 2.0.0", "rustls 0.23.13", "socket2 0.5.7", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -11738,7 +11741,7 @@ dependencies = [ "rustc-hash 2.0.0", "rustls 0.23.13", "slab", - "thiserror", + "thiserror 1.0.63", "tinyvec", "tracing", ] @@ -11888,7 +11891,7 @@ dependencies = [ "rand_chacha", "simd_helpers", "system-deps", - "thiserror", + "thiserror 1.0.63", "v_frame", "wasm-bindgen", ] @@ -12005,7 +12008,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -12209,7 +12212,7 @@ dependencies = [ "libc", "parking_lot 0.12.3", "reth-mdbx-sys", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -12412,7 +12415,7 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.77", + "syn 2.0.90", "unicode-ident", ] @@ -12425,7 +12428,7 @@ dependencies = [ "quote", "rand 0.8.5", "rustc_version 0.4.1", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -12435,7 +12438,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33648a781874466a62d89e265fee9f17e32bc7d05a256e6cca41bf97eadcd8aa" dependencies = [ "bytes", - "thiserror", + "thiserror 1.0.63", "webrtc-util 0.8.1", ] @@ -12450,7 +12453,7 @@ dependencies = [ "netlink-packet-route", "netlink-proto", "nix 0.24.3", - "thiserror", + "thiserror 1.0.63", "tokio", ] @@ -12473,7 +12476,7 @@ dependencies = [ "bytes", "rand 0.8.5", "serde", - "thiserror", + "thiserror 1.0.63", "webrtc-util 0.8.1", ] @@ -12486,7 +12489,7 @@ dependencies = [ "bytes", "rand 0.8.5", "serde", - "thiserror", + "thiserror 1.0.63", "webrtc-util 0.8.1", ] @@ -12547,7 +12550,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -12907,7 +12910,7 @@ dependencies = [ "starknet 0.12.0", "starknet-crypto 0.7.2", "tempdir", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "url", @@ -12938,7 +12941,7 @@ dependencies = [ "serde_json", "serde_with 3.11.0", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", "url", @@ -13010,7 +13013,7 @@ dependencies = [ "smallvec", "smol_str", "tar", - "thiserror", + "thiserror 1.0.63", "tokio", "toml 0.8.19", "toml_edit", @@ -13043,7 +13046,7 @@ dependencies = [ "semver 1.0.23", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -13056,7 +13059,7 @@ dependencies = [ "semver 1.0.23", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -13140,7 +13143,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -13185,7 +13188,7 @@ checksum = "13254db766b17451aced321e7397ebf0a446ef0c8d2942b6e67a95815421093f" dependencies = [ "rand 0.8.5", "substring", - "thiserror", + "thiserror 1.0.63", "url", ] @@ -13353,7 +13356,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -13364,7 +13367,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -13408,7 +13411,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -13475,7 +13478,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -13487,7 +13490,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -13522,7 +13525,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -13737,7 +13740,7 @@ dependencies = [ "serde_json", "starknet 0.12.0", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tower-http 0.5.2", "tracing", @@ -13746,6 +13749,15 @@ dependencies = [ "webbrowser", ] +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -13874,7 +13886,7 @@ dependencies = [ "starknet 0.12.0", "starknet-crypto 0.7.2", "tabled", - "thiserror", + "thiserror 1.0.63", "tokio", "toml 0.8.19", "tracing", @@ -13909,7 +13921,7 @@ dependencies = [ "spinoff", "starknet 0.12.0", "starknet-crypto 0.7.2", - "thiserror", + "thiserror 1.0.63", "tokio", "toml 0.8.19", "tracing", @@ -13950,7 +13962,7 @@ dependencies = [ "serde_json", "sozo-scarbext", "starknet 0.12.0", - "thiserror", + "thiserror 1.0.63", "url", "urlencoding", "walkdir", @@ -14061,7 +14073,7 @@ dependencies = [ "sha2 0.10.8", "smallvec", "sqlformat", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-stream", "tracing", @@ -14079,7 +14091,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -14103,7 +14115,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.77", + "syn 2.0.90", "tempfile", "tokio", "url", @@ -14147,7 +14159,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.63", "tracing", "uuid 1.10.0", "whoami", @@ -14187,7 +14199,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.63", "tracing", "uuid 1.10.0", "whoami", @@ -14267,7 +14279,7 @@ dependencies = [ "starknet-crypto 0.7.2", "starknet-providers 0.11.0", "starknet-signers 0.9.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -14282,7 +14294,7 @@ dependencies = [ "starknet-crypto 0.7.2", "starknet-providers 0.12.0", "starknet-signers 0.10.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -14297,7 +14309,7 @@ dependencies = [ "starknet-accounts 0.10.0", "starknet-core 0.11.1", "starknet-providers 0.11.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -14312,7 +14324,7 @@ dependencies = [ "starknet-accounts 0.11.0", "starknet-core 0.12.0", "starknet-providers 0.12.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -14420,7 +14432,7 @@ checksum = "bbc159a1934c7be9761c237333a57febe060ace2bc9e3b337a59a37af206d19f" dependencies = [ "starknet-curve 0.4.2", "starknet-ff", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -14471,7 +14483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8986a940af916fc0a034f4e42c6ba76d94f1e97216d75447693dfd7aefaf3ef2" dependencies = [ "starknet-core 0.12.0", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -14491,7 +14503,7 @@ dependencies = [ "serde_json", "serde_with 2.3.3", "starknet-core 0.11.1", - "thiserror", + "thiserror 1.0.63", "url", ] @@ -14512,7 +14524,7 @@ dependencies = [ "serde_json", "serde_with 3.11.0", "starknet-core 0.12.0", - "thiserror", + "thiserror 1.0.63", "url", ] @@ -14530,7 +14542,7 @@ dependencies = [ "rand 0.8.5", "starknet-core 0.11.1", "starknet-crypto 0.7.2", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -14547,7 +14559,7 @@ dependencies = [ "rand 0.8.5", "starknet-core 0.12.0", "starknet-crypto 0.7.2", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -14587,7 +14599,7 @@ dependencies = [ "starknet-types-core", "strum 0.24.1", "strum_macros 0.24.3", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -14688,7 +14700,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -14701,7 +14713,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -14717,7 +14729,7 @@ dependencies = [ "rand 0.8.5", "ring 0.17.8", "subtle", - "thiserror", + "thiserror 1.0.63", "tokio", "url", "webrtc-util 0.8.1", @@ -14736,7 +14748,7 @@ dependencies = [ "rand 0.8.5", "ring 0.17.8", "subtle", - "thiserror", + "thiserror 1.0.63", "tokio", "url", "webrtc-util 0.9.0", @@ -14808,9 +14820,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -14826,7 +14838,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -14838,7 +14850,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -14876,7 +14888,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -15031,7 +15043,16 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", ] [[package]] @@ -15042,7 +15063,18 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -15198,7 +15230,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -15426,7 +15458,7 @@ dependencies = [ "proc-macro2", "prost-build 0.12.6", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -15440,7 +15472,7 @@ dependencies = [ "prost-build 0.13.3", "prost-types 0.13.3", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -15492,7 +15524,7 @@ dependencies = [ "httparse", "js-sys", "pin-project", - "thiserror", + "thiserror 1.0.63", "tonic 0.12.3", "tower-service", "wasm-bindgen", @@ -15586,7 +15618,7 @@ dependencies = [ "serde_json", "starknet 0.12.0", "starknet-crypto 0.7.2", - "thiserror", + "thiserror 1.0.63", "tokio", "tonic 0.11.0", "tonic 0.12.3", @@ -15628,7 +15660,7 @@ dependencies = [ "starknet 0.12.0", "starknet-crypto 0.7.2", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-util", "tracing", @@ -15664,7 +15696,7 @@ dependencies = [ "strum 0.25.0", "strum_macros 0.25.3", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-stream", "toml 0.8.19", @@ -15705,7 +15737,7 @@ dependencies = [ "strum 0.25.0", "strum_macros 0.25.3", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-stream", "tonic 0.11.0", @@ -15745,7 +15777,7 @@ dependencies = [ "starknet 0.12.0", "starknet-crypto 0.7.2", "tempfile", - "thiserror", + "thiserror 1.0.63", "tokio", "torii-core", "tracing", @@ -15911,7 +15943,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -15940,7 +15972,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -16066,7 +16098,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -16083,7 +16115,7 @@ dependencies = [ "log", "rand 0.8.5", "sha1", - "thiserror", + "thiserror 1.0.63", "url", "utf-8", ] @@ -16102,7 +16134,7 @@ dependencies = [ "log", "rand 0.8.5", "sha1", - "thiserror", + "thiserror 1.0.63", "url", "utf-8", ] @@ -16121,7 +16153,7 @@ dependencies = [ "rand 0.8.5", "ring 0.17.8", "stun 0.5.1", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-util", "webrtc-util 0.8.1", @@ -16144,7 +16176,7 @@ checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -16212,7 +16244,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815" dependencies = [ - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -16619,7 +16651,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -16653,7 +16685,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -16687,7 +16719,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -16820,7 +16852,7 @@ dependencies = [ "sha2 0.10.8", "smol_str", "stun 0.5.1", - "thiserror", + "thiserror 1.0.63", "time", "tokio", "turn", @@ -16844,7 +16876,7 @@ checksum = "e8c08e648e10572b9edbe741074e0f4d3cb221aa7cdf9a814ee71606de312f33" dependencies = [ "bytes", "log", - "thiserror", + "thiserror 1.0.63", "tokio", "webrtc-sctp", "webrtc-util 0.8.1", @@ -16880,7 +16912,7 @@ dependencies = [ "sha1", "sha2 0.10.8", "subtle", - "thiserror", + "thiserror 1.0.63", "tokio", "webrtc-util 0.8.1", "x25519-dalek", @@ -16901,7 +16933,7 @@ dependencies = [ "serde", "serde_json", "stun 0.5.1", - "thiserror", + "thiserror 1.0.63", "tokio", "turn", "url", @@ -16919,7 +16951,7 @@ checksum = "ce981f93104a8debb3563bb0cedfe4aa2f351fdf6b53f346ab50009424125c08" dependencies = [ "log", "socket2 0.5.7", - "thiserror", + "thiserror 1.0.63", "tokio", "webrtc-util 0.8.1", ] @@ -16934,7 +16966,7 @@ dependencies = [ "bytes", "rand 0.8.5", "rtp 0.10.0", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -16949,7 +16981,7 @@ dependencies = [ "crc", "log", "rand 0.8.5", - "thiserror", + "thiserror 1.0.63", "tokio", "webrtc-util 0.8.1", ] @@ -16972,7 +17004,7 @@ dependencies = [ "rtp 0.9.0", "sha1", "subtle", - "thiserror", + "thiserror 1.0.63", "tokio", "webrtc-util 0.8.1", ] @@ -16992,7 +17024,7 @@ dependencies = [ "log", "nix 0.26.4", "rand 0.8.5", - "thiserror", + "thiserror 1.0.63", "tokio", "winapi", ] @@ -17013,7 +17045,7 @@ dependencies = [ "nix 0.26.4", "portable-atomic", "rand 0.8.5", - "thiserror", + "thiserror 1.0.63", "tokio", "winapi", ] @@ -17154,7 +17186,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -17165,7 +17197,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -17475,7 +17507,7 @@ dependencies = [ "oid-registry 0.6.1", "ring 0.16.20", "rusticata-macros", - "thiserror", + "thiserror 1.0.63", "time", ] @@ -17492,7 +17524,7 @@ dependencies = [ "nom", "oid-registry 0.7.1", "rusticata-macros", - "thiserror", + "thiserror 1.0.63", "time", ] @@ -17630,7 +17662,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -17650,7 +17682,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 544e7d06ee..d4072e4deb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -277,3 +277,8 @@ starknet-crypto = "0.7.1" starknet-types-core = { version = "0.1.7", features = [ "arbitrary", "hash" ] } bitvec = "1.0.1" + +# macro +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "2.0", default-features = false } diff --git a/crates/katana/core/src/backend/mod.rs b/crates/katana/core/src/backend/mod.rs index 47908816bc..92646de084 100644 --- a/crates/katana/core/src/backend/mod.rs +++ b/crates/katana/core/src/backend/mod.rs @@ -13,7 +13,7 @@ use katana_primitives::state::{compute_state_diff_hash, StateUpdates}; use katana_primitives::transaction::{TxHash, TxWithHash}; use katana_primitives::Felt; use katana_provider::traits::block::{BlockHashProvider, BlockWriter}; -use katana_provider::traits::trie::{ClassTrieWriter, ContractTrieWriter}; +use katana_provider::traits::trie::TrieWriter; use katana_trie::compute_merkle_root; use parking_lot::RwLock; use starknet::macros::short_string; @@ -158,21 +158,15 @@ impl Backend { } #[derive(Debug, Clone)] -pub struct UncommittedBlock<'a, P> -where - P: ClassTrieWriter + ContractTrieWriter, -{ +pub struct UncommittedBlock<'a, P: TrieWriter> { header: PartialHeader, transactions: Vec, receipts: &'a [ReceiptWithTxHash], state_updates: &'a StateUpdates, - trie_provider: P, + provider: P, } -impl<'a, P> UncommittedBlock<'a, P> -where - P: ClassTrieWriter + ContractTrieWriter, -{ +impl<'a, P: TrieWriter> UncommittedBlock<'a, P> { pub fn new( header: PartialHeader, transactions: Vec, @@ -180,7 +174,7 @@ where state_updates: &'a StateUpdates, trie_provider: P, ) -> Self { - Self { header, transactions, receipts, state_updates, trie_provider } + Self { header, transactions, receipts, state_updates, provider: trie_provider } } pub fn commit(self) -> SealedBlock { @@ -258,19 +252,16 @@ where // state_commitment = hPos("STARKNET_STATE_V0", contract_trie_root, class_trie_root) fn compute_new_state_root(&self) -> Felt { - let class_trie_root = ClassTrieWriter::insert_updates( - &self.trie_provider, - self.header.number, - &self.state_updates.declared_classes, - ) - .unwrap(); - - let contract_trie_root = ContractTrieWriter::insert_updates( - &self.trie_provider, - self.header.number, - self.state_updates, - ) - .unwrap(); + println!("ohayo im committing now"); + let class_trie_root = self + .provider + .trie_insert_declared_classes(self.header.number, &self.state_updates.declared_classes) + .expect("failed to update class trie"); + + let contract_trie_root = self + .provider + .trie_insert_contract_updates(self.header.number, self.state_updates) + .expect("failed to update contract trie"); hash::Poseidon::hash_array(&[ short_string!("STARKNET_STATE_V0"), diff --git a/crates/katana/core/src/backend/storage.rs b/crates/katana/core/src/backend/storage.rs index b7d9f095c6..22f0f5efad 100644 --- a/crates/katana/core/src/backend/storage.rs +++ b/crates/katana/core/src/backend/storage.rs @@ -15,13 +15,13 @@ use katana_provider::traits::block::{BlockProvider, BlockWriter}; use katana_provider::traits::contract::{ContractClassWriter, ContractClassWriterExt}; use katana_provider::traits::env::BlockEnvProvider; use katana_provider::traits::stage::StageCheckpointProvider; -use katana_provider::traits::state::{StateFactoryProvider, StateRootProvider, StateWriter}; +use katana_provider::traits::state::{StateFactoryProvider, StateWriter}; use katana_provider::traits::state_update::StateUpdateProvider; use katana_provider::traits::transaction::{ ReceiptProvider, TransactionProvider, TransactionStatusProvider, TransactionTraceProvider, TransactionsProviderExt, }; -use katana_provider::traits::trie::{ClassTrieWriter, ContractTrieWriter}; +use katana_provider::traits::trie::TrieWriter; use katana_provider::BlockchainProvider; use num_traits::ToPrimitive; use starknet::core::types::{BlockStatus, MaybePendingBlockWithTxHashes}; @@ -40,14 +40,12 @@ pub trait Database: + TransactionsProviderExt + ReceiptProvider + StateUpdateProvider - + StateRootProvider + StateWriter + ContractClassWriter + ContractClassWriterExt + StateFactoryProvider + BlockEnvProvider - + ClassTrieWriter - + ContractTrieWriter + + TrieWriter + StageCheckpointProvider + 'static + Send @@ -65,14 +63,12 @@ impl Database for T where + TransactionsProviderExt + ReceiptProvider + StateUpdateProvider - + StateRootProvider + StateWriter + ContractClassWriter + ContractClassWriterExt + StateFactoryProvider + BlockEnvProvider - + ClassTrieWriter - + ContractTrieWriter + + TrieWriter + StageCheckpointProvider + 'static + Send diff --git a/crates/katana/executor/Cargo.toml b/crates/katana/executor/Cargo.toml index 0939be3dea..785d18d3d4 100644 --- a/crates/katana/executor/Cargo.toml +++ b/crates/katana/executor/Cargo.toml @@ -9,6 +9,7 @@ version.workspace = true [dependencies] katana-primitives.workspace = true katana-provider.workspace = true +katana-trie.workspace = true parking_lot = { workspace = true, optional = true } starknet = { workspace = true, optional = true } diff --git a/crates/katana/executor/src/abstraction/mod.rs b/crates/katana/executor/src/abstraction/mod.rs index 53afde647d..78483e8ddd 100644 --- a/crates/katana/executor/src/abstraction/mod.rs +++ b/crates/katana/executor/src/abstraction/mod.rs @@ -11,8 +11,9 @@ use katana_primitives::trace::TxExecInfo; use katana_primitives::transaction::TxWithHash; use katana_primitives::Felt; use katana_provider::traits::contract::ContractClassProvider; -use katana_provider::traits::state::StateProvider; +use katana_provider::traits::state::{StateProofProvider, StateProvider, StateRootProvider}; use katana_provider::ProviderResult; +use katana_trie::MultiProof; pub type ExecutorResult = Result; @@ -204,3 +205,35 @@ impl<'a> StateProvider for StateProviderDb<'a> { self.0.storage(address, storage_key) } } + +impl<'a> StateProofProvider for StateProviderDb<'a> { + fn class_multiproof(&self, classes: Vec) -> ProviderResult { + self.0.class_multiproof(classes) + } + + fn contract_multiproof(&self, addresses: Vec) -> ProviderResult { + self.0.contract_multiproof(addresses) + } + + fn storage_multiproof( + &self, + address: ContractAddress, + key: Vec, + ) -> ProviderResult { + self.0.storage_multiproof(address, key) + } +} + +impl<'a> StateRootProvider for StateProviderDb<'a> { + fn classes_root(&self) -> ProviderResult { + self.0.classes_root() + } + + fn contracts_root(&self) -> ProviderResult { + self.0.contracts_root() + } + + fn state_root(&self) -> ProviderResult { + self.0.state_root() + } +} diff --git a/crates/katana/executor/src/implementation/blockifier/state.rs b/crates/katana/executor/src/implementation/blockifier/state.rs index 37563e7d88..cbda7a187f 100644 --- a/crates/katana/executor/src/implementation/blockifier/state.rs +++ b/crates/katana/executor/src/implementation/blockifier/state.rs @@ -10,7 +10,7 @@ use katana_primitives::class::{self, CompiledClass, ContractClass}; use katana_primitives::Felt; use katana_provider::error::ProviderError; use katana_provider::traits::contract::ContractClassProvider; -use katana_provider::traits::state::StateProvider; +use katana_provider::traits::state::{StateProofProvider, StateProvider, StateRootProvider}; use katana_provider::ProviderResult; use parking_lot::Mutex; @@ -238,6 +238,44 @@ impl StateReader for CachedState { } } +impl StateProofProvider for CachedState { + fn class_multiproof( + &self, + classes: Vec, + ) -> ProviderResult { + let _ = classes; + unimplemented!("not supported in executor's state") + } + + fn contract_multiproof( + &self, + addresses: Vec, + ) -> ProviderResult { + let _ = addresses; + unimplemented!("not supported in executor's state") + } + + fn storage_multiproof( + &self, + address: katana_primitives::ContractAddress, + key: Vec, + ) -> ProviderResult { + let _ = address; + let _ = key; + unimplemented!("not supported in executor's state") + } +} + +impl StateRootProvider for CachedState { + fn classes_root(&self) -> ProviderResult { + unimplemented!("not supported in executor's state") + } + + fn contracts_root(&self) -> ProviderResult { + unimplemented!("not supported in executor's state") + } +} + #[cfg(test)] mod tests { diff --git a/crates/katana/executor/src/implementation/noop.rs b/crates/katana/executor/src/implementation/noop.rs index 31a66baf54..09643823dc 100644 --- a/crates/katana/executor/src/implementation/noop.rs +++ b/crates/katana/executor/src/implementation/noop.rs @@ -6,7 +6,7 @@ use katana_primitives::fee::TxFeeInfo; use katana_primitives::transaction::{ExecutableTxWithHash, TxWithHash}; use katana_primitives::Felt; use katana_provider::traits::contract::ContractClassProvider; -use katana_provider::traits::state::StateProvider; +use katana_provider::traits::state::{StateProofProvider, StateProvider, StateRootProvider}; use katana_provider::ProviderResult; use crate::abstraction::{ @@ -170,3 +170,38 @@ impl StateProvider for NoopStateProvider { Ok(None) } } + +impl StateProofProvider for NoopStateProvider { + fn class_multiproof(&self, classes: Vec) -> ProviderResult { + let _ = classes; + Ok(katana_trie::MultiProof(Default::default())) + } + + fn contract_multiproof( + &self, + addresses: Vec, + ) -> ProviderResult { + let _ = addresses; + Ok(katana_trie::MultiProof(Default::default())) + } + + fn storage_multiproof( + &self, + address: ContractAddress, + key: Vec, + ) -> ProviderResult { + let _ = address; + let _ = key; + Ok(katana_trie::MultiProof(Default::default())) + } +} + +impl StateRootProvider for NoopStateProvider { + fn classes_root(&self) -> ProviderResult { + Ok(Felt::ZERO) + } + + fn contracts_root(&self) -> ProviderResult { + Ok(Felt::ZERO) + } +} diff --git a/crates/katana/primitives/src/lib.rs b/crates/katana/primitives/src/lib.rs index be6b106c39..23fddf3707 100644 --- a/crates/katana/primitives/src/lib.rs +++ b/crates/katana/primitives/src/lib.rs @@ -24,3 +24,4 @@ pub mod utils; pub use contract::ContractAddress; pub use starknet::macros::felt; pub use starknet_types_core::felt::{Felt, FromStrError}; +pub use starknet_types_core::hash; diff --git a/crates/katana/rpc/rpc-api/src/starknet.rs b/crates/katana/rpc/rpc-api/src/starknet.rs index bf2b10e410..42a25e8581 100644 --- a/crates/katana/rpc/rpc-api/src/starknet.rs +++ b/crates/katana/rpc/rpc-api/src/starknet.rs @@ -3,8 +3,9 @@ use jsonrpsee::core::RpcResult; use jsonrpsee::proc_macros::rpc; use katana_primitives::block::{BlockIdOrTag, BlockNumber}; +use katana_primitives::class::ClassHash; use katana_primitives::transaction::TxHash; -use katana_primitives::Felt; +use katana_primitives::{ContractAddress, Felt}; use katana_rpc_types::block::{ BlockHashAndNumber, BlockTxCount, MaybePendingBlockWithReceipts, MaybePendingBlockWithTxHashes, MaybePendingBlockWithTxs, @@ -18,6 +19,7 @@ use katana_rpc_types::transaction::{ BroadcastedDeclareTx, BroadcastedDeployAccountTx, BroadcastedInvokeTx, BroadcastedTx, DeclareTxResult, DeployAccountTxResult, InvokeTxResult, Tx, }; +use katana_rpc_types::trie::{ContractStorageKeys, GetStorageProofResponse}; use katana_rpc_types::{ FeeEstimate, FeltAsHex, FunctionCall, SimulationFlag, SimulationFlagForEstimateFee, SyncingStatus, @@ -183,6 +185,18 @@ pub trait StarknetApi { block_id: BlockIdOrTag, contract_address: Felt, ) -> RpcResult; + + /// Get merkle paths in one of the state tries: global state, classes, individual contract. A + /// single request can query for any mix of the three types of storage proofs (classes, + /// contracts, and storage). + #[method(name = "getStorageProof")] + async fn get_storage_proof( + &self, + block_id: BlockIdOrTag, + class_hashes: Option>, + contract_addresses: Option>, + contracts_storage_keys: Option>, + ) -> RpcResult; } /// Write API. diff --git a/crates/katana/rpc/rpc-types-builder/src/state_update.rs b/crates/katana/rpc/rpc-types-builder/src/state_update.rs index 8d898485d0..ba9fa920fb 100644 --- a/crates/katana/rpc/rpc-types-builder/src/state_update.rs +++ b/crates/katana/rpc/rpc-types-builder/src/state_update.rs @@ -1,7 +1,7 @@ use katana_primitives::block::BlockHashOrNumber; use katana_primitives::Felt; use katana_provider::traits::block::{BlockHashProvider, BlockNumberProvider}; -use katana_provider::traits::state::StateRootProvider; +use katana_provider::traits::state::{StateFactoryProvider, StateRootProvider}; use katana_provider::traits::state_update::StateUpdateProvider; use katana_provider::ProviderResult; use katana_rpc_types::state_update::{StateDiff, StateUpdate}; @@ -21,7 +21,7 @@ impl

StateUpdateBuilder

{ impl

StateUpdateBuilder

where - P: BlockHashProvider + BlockNumberProvider + StateRootProvider + StateUpdateProvider, + P: BlockHashProvider + BlockNumberProvider + StateFactoryProvider + StateUpdateProvider, { /// Builds a state update for the given block. pub fn build(self) -> ProviderResult> { @@ -30,16 +30,23 @@ where return Ok(None); }; - let new_root = StateRootProvider::state_root(&self.provider, self.block_id)? - .expect("should exist if block exists"); + let new_root = self + .provider + .historical(self.block_id)? + .expect("should exist if block exists") + .state_root()?; + let old_root = { let block_num = BlockNumberProvider::block_number_by_hash(&self.provider, block_hash)? .expect("should exist if block exists"); match block_num { 0 => Felt::ZERO, - _ => StateRootProvider::state_root(&self.provider, (block_num - 1).into())? - .expect("should exist if not genesis"), + _ => self + .provider + .historical((block_num - 1).into())? + .expect("should exist if block exists") + .state_root()?, } }; diff --git a/crates/katana/rpc/rpc-types/Cargo.toml b/crates/katana/rpc/rpc-types/Cargo.toml index 7df238583b..8cdfe76dc3 100644 --- a/crates/katana/rpc/rpc-types/Cargo.toml +++ b/crates/katana/rpc/rpc-types/Cargo.toml @@ -13,6 +13,7 @@ katana-executor.workspace = true katana-pool.workspace = true katana-primitives.workspace = true katana-provider.workspace = true +katana-trie.workspace = true anyhow.workspace = true derive_more.workspace = true diff --git a/crates/katana/rpc/rpc-types/src/lib.rs b/crates/katana/rpc/rpc-types/src/lib.rs index d67db902a4..3ee07c1a19 100644 --- a/crates/katana/rpc/rpc-types/src/lib.rs +++ b/crates/katana/rpc/rpc-types/src/lib.rs @@ -13,6 +13,7 @@ pub mod receipt; pub mod state_update; pub mod trace; pub mod transaction; +pub mod trie; mod utils; use std::ops::Deref; diff --git a/crates/katana/rpc/rpc-types/src/trie.rs b/crates/katana/rpc/rpc-types/src/trie.rs new file mode 100644 index 0000000000..993534537a --- /dev/null +++ b/crates/katana/rpc/rpc-types/src/trie.rs @@ -0,0 +1,193 @@ +use std::collections::HashMap; + +use katana_primitives::contract::StorageKey; +use katana_primitives::{ContractAddress, Felt}; +use katana_trie::bonsai::BitSlice; +use katana_trie::{MultiProof, Path, ProofNode}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct ContractStorageKeys { + #[serde(rename = "contract_address")] + pub address: ContractAddress, + #[serde(rename = "storage_keys")] + pub keys: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct GlobalRoots { + /// The associated block hash (needed in case the caller used a block tag for the block_id + /// parameter). + pub block_hash: Felt, + pub classes_tree_root: Felt, + pub contracts_tree_root: Felt, +} + +/// Node in the Merkle-Patricia trie. +#[derive(Debug, Serialize, Deserialize)] +pub enum MerkleNode { + /// Represents a path to the highest non-zero descendant node. + Edge { + /// An integer whose binary representation represents the path from the current node to its + /// highest non-zero descendant (bounded by 2^251) + path: Felt, + /// The length of the path (bounded by 251). + length: u8, + /// The hash of the unique non-zero maximal-height descendant node. + child: Felt, + }, + + /// An internal node whose both children are non-zero. + Binary { + /// The hash of the left child. + left: Felt, + /// The hash of the right child. + right: Felt, + }, +} + +/// The response type for `starknet_getStorageProof` method. +/// +/// The requested storage proofs. Note that if a requested leaf has the default value, the path to +/// it may end in an edge node whose path is not a prefix of the requested leaf, thus effectively +/// proving non-membership +#[derive(Debug, Serialize, Deserialize)] +pub struct GetStorageProofResponse { + pub global_roots: GlobalRoots, + pub classes_proof: ClassesProof, + pub contracts_proof: ContractsProof, + pub contracts_storage_proofs: ContractStorageProofs, +} + +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct ClassesProof { + pub nodes: Nodes, +} + +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct ContractsProof { + /// The nodes in the union of the paths from the contracts tree root to the requested leaves. + pub nodes: Nodes, + /// The nonce and class hash for each requested contract address, in the order in which they + /// appear in the request. These values are needed to construct the associated leaf node. + pub contract_leaves_data: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ContractLeafData { + pub nonce: Felt, + pub class_hash: Felt, +} + +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct ContractStorageProofs { + pub nodes: Vec, +} + +#[derive(Debug, Default)] +pub struct Nodes(pub HashMap); + +impl Serialize for Nodes { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeSeq; + + #[derive(Debug, Serialize)] + struct NodeEntry<'a> { + node_hash: &'a Felt, + node: &'a MerkleNode, + } + + let mut seq = serializer.serialize_seq(Some(self.0.len()))?; + for (node_hash, node) in &self.0 { + seq.serialize_element(&NodeEntry { node_hash, node })?; + } + seq.end() + } +} + +impl<'de> Deserialize<'de> for Nodes { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Debug, Deserialize)] + struct NodeEntry { + node_hash: Felt, + node: MerkleNode, + } + + let entries: Vec = Vec::deserialize(deserializer)?; + let map = entries.into_iter().map(|entry| (entry.node_hash, entry.node)).collect(); + Ok(Nodes(map)) + } +} + +// --- Conversion from/to internal types for convenience + +impl From for Nodes { + fn from(value: MultiProof) -> Self { + Self(value.0.into_iter().map(|(hash, node)| (hash, MerkleNode::from(node))).collect()) + } +} + +impl From for MultiProof { + fn from(value: Nodes) -> Self { + Self(value.0.into_iter().map(|(hash, node)| (hash, ProofNode::from(node))).collect()) + } +} + +impl From for MerkleNode { + fn from(value: ProofNode) -> Self { + match value { + ProofNode::Binary { left, right } => MerkleNode::Binary { left, right }, + ProofNode::Edge { child, path } => { + MerkleNode::Edge { child, length: path.len() as u8, path: path_to_felt(path) } + } + } + } +} + +impl From for ProofNode { + fn from(value: MerkleNode) -> Self { + match value { + MerkleNode::Binary { left, right } => Self::Binary { left, right }, + MerkleNode::Edge { path, child, .. } => Self::Edge { child, path: felt_to_path(path) }, + } + } +} + +fn felt_to_path(felt: Felt) -> Path { + Path(BitSlice::from_slice(&felt.to_bytes_be())[5..].to_bitvec()) +} + +fn path_to_felt(path: Path) -> Felt { + let mut arr = [0u8; 32]; + let slice = &mut BitSlice::from_slice_mut(&mut arr)[5..]; + slice[..path.len()].copy_from_bitslice(&path); + Felt::from_bytes_be(&arr) +} + +#[cfg(test)] +mod tests { + use katana_primitives::felt; + + use super::*; + + // This test is assuming that the `path` field in `MerkleNode::Edge` is already a valid trie + // path value. + #[rstest::rstest] + #[case(felt!("0x1234567890abcdef"))] + #[case(felt!("0xdeadbeef"))] + #[case(Felt::MAX)] + #[case(Felt::ZERO)] + fn test_path_felt_roundtrip(#[case] path_in_felt: Felt) { + let initial_path = felt_to_path(path_in_felt); + + let converted_felt = path_to_felt(initial_path.clone()); + let path = felt_to_path(converted_felt); + assert_eq!(initial_path, path); + } +} diff --git a/crates/katana/rpc/rpc/Cargo.toml b/crates/katana/rpc/rpc/Cargo.toml index 0c09844955..3910bbd2c9 100644 --- a/crates/katana/rpc/rpc/Cargo.toml +++ b/crates/katana/rpc/rpc/Cargo.toml @@ -43,6 +43,7 @@ jsonrpsee = { workspace = true, features = [ "client" ] } katana-cairo.workspace = true katana-node.workspace = true katana-rpc-api = { workspace = true, features = [ "client" ] } +katana-trie.workspace = true num-traits.workspace = true rand.workspace = true rstest.workspace = true diff --git a/crates/katana/rpc/rpc/src/starknet/mod.rs b/crates/katana/rpc/rpc/src/starknet/mod.rs index c0d4256987..ee4945a596 100644 --- a/crates/katana/rpc/rpc/src/starknet/mod.rs +++ b/crates/katana/rpc/rpc/src/starknet/mod.rs @@ -20,7 +20,7 @@ use katana_primitives::Felt; use katana_provider::traits::block::{BlockHashProvider, BlockIdReader, BlockNumberProvider}; use katana_provider::traits::contract::ContractClassProvider; use katana_provider::traits::env::BlockEnvProvider; -use katana_provider::traits::state::{StateFactoryProvider, StateProvider}; +use katana_provider::traits::state::{StateFactoryProvider, StateProvider, StateRootProvider}; use katana_provider::traits::transaction::{ ReceiptProvider, TransactionProvider, TransactionStatusProvider, }; @@ -34,6 +34,10 @@ use katana_rpc_types::event::{EventFilterWithPage, EventsPage}; use katana_rpc_types::receipt::{ReceiptBlock, TxReceiptWithBlockInfo}; use katana_rpc_types::state_update::MaybePendingStateUpdate; use katana_rpc_types::transaction::Tx; +use katana_rpc_types::trie::{ + ClassesProof, ContractLeafData, ContractStorageKeys, ContractStorageProofs, ContractsProof, + GetStorageProofResponse, GlobalRoots, Nodes, +}; use katana_rpc_types::FeeEstimate; use katana_rpc_types_builder::ReceiptBuilder; use katana_tasks::{BlockingTaskPool, TokioTaskSpawner}; @@ -1124,6 +1128,75 @@ where Ok(id) } + + async fn get_proofs( + &self, + block_id: BlockIdOrTag, + class_hashes: Option>, + contract_addresses: Option>, + contracts_storage_keys: Option>, + ) -> StarknetApiResult { + self.on_io_blocking_task(move |this| { + let provider = this.inner.backend.blockchain.provider(); + + let state = this.state(&block_id)?; + let block_hash = provider.latest_hash()?; + + // --- Get classes proof (if any) + + let classes_proof = if let Some(classes) = class_hashes { + let proofs = state.class_multiproof(classes)?; + ClassesProof { nodes: proofs.into() } + } else { + ClassesProof::default() + }; + + // --- Get contracts proof (if any) + + let contracts_proof = if let Some(addresses) = contract_addresses { + let proofs = state.contract_multiproof(addresses.clone())?; + let mut contract_leaves_data = Vec::new(); + + for address in addresses { + let nonce = state.nonce(address)?.unwrap_or_default(); + let class_hash = state.class_hash_of_contract(address)?.unwrap_or_default(); + contract_leaves_data.push(ContractLeafData { class_hash, nonce }); + } + + ContractsProof { nodes: proofs.into(), contract_leaves_data } + } else { + ContractsProof::default() + }; + + // --- Get contracts storage proof (if any) + + let contracts_storage_proofs = if let Some(contract_storage) = contracts_storage_keys { + let mut nodes: Vec = Vec::new(); + + for ContractStorageKeys { address, keys } in contract_storage { + let proofs = state.storage_multiproof(address, keys)?; + nodes.push(proofs.into()); + } + + ContractStorageProofs { nodes } + } else { + ContractStorageProofs::default() + }; + + let classes_tree_root = state.classes_root()?; + let contracts_tree_root = state.contracts_root()?; + + let global_roots = GlobalRoots { block_hash, classes_tree_root, contracts_tree_root }; + + Ok(GetStorageProofResponse { + global_roots, + classes_proof, + contracts_proof, + contracts_storage_proofs, + }) + }) + .await + } } impl Clone for StarknetApi diff --git a/crates/katana/rpc/rpc/src/starknet/read.rs b/crates/katana/rpc/rpc/src/starknet/read.rs index 89322967ba..a48cfe5f5b 100644 --- a/crates/katana/rpc/rpc/src/starknet/read.rs +++ b/crates/katana/rpc/rpc/src/starknet/read.rs @@ -1,8 +1,9 @@ use jsonrpsee::core::{async_trait, Error, RpcResult}; use katana_executor::{EntryPointCall, ExecutorFactory}; use katana_primitives::block::BlockIdOrTag; +use katana_primitives::class::ClassHash; use katana_primitives::transaction::{ExecutableTx, ExecutableTxWithHash, TxHash}; -use katana_primitives::Felt; +use katana_primitives::{ContractAddress, Felt}; use katana_rpc_api::starknet::StarknetApiServer; use katana_rpc_types::block::{ BlockHashAndNumber, MaybePendingBlockWithReceipts, MaybePendingBlockWithTxHashes, @@ -15,6 +16,7 @@ use katana_rpc_types::message::MsgFromL1; use katana_rpc_types::receipt::TxReceiptWithBlockInfo; use katana_rpc_types::state_update::MaybePendingStateUpdate; use katana_rpc_types::transaction::{BroadcastedTx, Tx}; +use katana_rpc_types::trie::{ContractStorageKeys, GetStorageProofResponse}; use katana_rpc_types::{FeeEstimate, FeltAsHex, FunctionCall, SimulationFlagForEstimateFee}; use starknet::core::types::TransactionStatus; @@ -265,4 +267,17 @@ impl StarknetApiServer for StarknetApi { ) -> RpcResult { Ok(self.transaction_status(transaction_hash).await?) } + + async fn get_storage_proof( + &self, + block_id: BlockIdOrTag, + class_hashes: Option>, + contract_addresses: Option>, + contracts_storage_keys: Option>, + ) -> RpcResult { + let proofs = self + .get_proofs(block_id, class_hashes, contract_addresses, contracts_storage_keys) + .await?; + Ok(proofs) + } } diff --git a/crates/katana/rpc/rpc/tests/proofs.rs b/crates/katana/rpc/rpc/tests/proofs.rs new file mode 100644 index 0000000000..b7c24592b2 --- /dev/null +++ b/crates/katana/rpc/rpc/tests/proofs.rs @@ -0,0 +1,64 @@ +use std::path::PathBuf; + +use dojo_test_utils::sequencer::{get_default_test_config, TestSequencer}; +use jsonrpsee::http_client::HttpClientBuilder; +use katana_node::config::SequencingConfig; +use katana_primitives::block::BlockIdOrTag; +use katana_primitives::hash; +use katana_primitives::hash::StarkHash; +use katana_rpc_api::starknet::StarknetApiClient; +use katana_rpc_types::trie::GetStorageProofResponse; +use katana_trie::bitvec::view::AsBits; +use katana_trie::bonsai::BitVec; +use katana_trie::MultiProof; +use starknet::accounts::Account; +use starknet::core::types::BlockTag; +use starknet::macros::short_string; + +mod common; + +#[tokio::test] +async fn classes_proofs() { + let sequencer = + TestSequencer::start(get_default_test_config(SequencingConfig::default())).await; + + let provider = sequencer.provider(); + let account = sequencer.account(); + + let path: PathBuf = PathBuf::from("tests/test_data/cairo1_contract.json"); + let (contract, compiled_class_hash) = common::prepare_contract_declaration_params(&path) + .expect("failed to prepare class declaration params"); + + let class_hash = contract.class_hash(); + let res = account + .declare_v2(contract.into(), compiled_class_hash) + .send() + .await + .expect("failed to send declare tx"); + + dojo_utils::TransactionWaiter::new(res.transaction_hash, &provider) + .await + .expect("failed to wait on tx"); + + // We need to use the jsonrpsee client because `starknet-rs` doesn't yet support RPC 0.8.0 + let client = HttpClientBuilder::default().build(sequencer.url()).unwrap(); + + let GetStorageProofResponse { global_roots, classes_proof, .. } = client + .get_storage_proof(BlockIdOrTag::Tag(BlockTag::Latest), Some(vec![class_hash]), None, None) + .await + .expect("failed to get storage proof"); + + let key: BitVec = class_hash.to_bytes_be().as_bits()[5..].to_owned(); + let value = + hash::Poseidon::hash(&short_string!("CONTRACT_CLASS_LEAF_V0"), &compiled_class_hash); + + let classes_proof = MultiProof::from(classes_proof.nodes); + + // the returned data is the list of values corresponds to the [key] + let results = classes_proof + .verify_proof::(global_roots.classes_tree_root, [key], 251) + .collect::, _>>() + .expect("failed to verify proofs"); + + assert_eq!(vec![value], results); +} diff --git a/crates/katana/runner/macro/Cargo.toml b/crates/katana/runner/macro/Cargo.toml index a8e2689be0..7c270bd298 100644 --- a/crates/katana/runner/macro/Cargo.toml +++ b/crates/katana/runner/macro/Cargo.toml @@ -8,6 +8,6 @@ version.workspace = true proc-macro = true [dependencies] -proc-macro2 = "1.0.86" -quote = "1.0" -syn = { version = "2.0", features = [ "fold", "full" ] } +proc-macro2.workspace = true +quote.workspace = true +syn = { workspace = true, features = [ "fold", "full" ] } diff --git a/crates/katana/storage/codecs/derive/Cargo.toml b/crates/katana/storage/codecs/derive/Cargo.toml index 986d362e50..f4e0cc5248 100644 --- a/crates/katana/storage/codecs/derive/Cargo.toml +++ b/crates/katana/storage/codecs/derive/Cargo.toml @@ -9,10 +9,10 @@ version.workspace = true proc-macro = true [dependencies] -proc-macro2 = "1.0.70" -quote = "1.0.33" +proc-macro2.workspace = true +quote.workspace = true serde.workspace = true -syn = { version = "2.0.41", features = [ "extra-traits", "full" ] } +syn = { workspace = true, features = [ "extra-traits", "full" ] } [package.metadata.cargo-udeps.ignore] normal = [ "serde" ] diff --git a/crates/katana/storage/db/Cargo.toml b/crates/katana/storage/db/Cargo.toml index b92f75b51a..8385b2dffe 100644 --- a/crates/katana/storage/db/Cargo.toml +++ b/crates/katana/storage/db/Cargo.toml @@ -18,14 +18,11 @@ parking_lot.workspace = true roaring = { version = "0.10.3", features = [ "serde" ] } serde.workspace = true serde_json.workspace = true -starknet.workspace = true -starknet-types-core.workspace = true tempfile.workspace = true thiserror.workspace = true tracing.workspace = true # codecs -bitvec.workspace = true postcard = { workspace = true, optional = true } smallvec = "1.13.2" @@ -37,6 +34,7 @@ rev = "b34b0d3" [dev-dependencies] arbitrary.workspace = true criterion.workspace = true +starknet.workspace = true [features] default = [ "postcard" ] diff --git a/crates/katana/storage/db/src/abstraction/transaction.rs b/crates/katana/storage/db/src/abstraction/transaction.rs index 9f62a7a572..0280e65c07 100644 --- a/crates/katana/storage/db/src/abstraction/transaction.rs +++ b/crates/katana/storage/db/src/abstraction/transaction.rs @@ -66,3 +66,115 @@ pub trait DbTxMut: DbTx { /// Clears all entries in the given database. This will empty the database. fn clear(&self) -> Result<(), DatabaseError>; } + +// --- Reference variations + +pub trait DbTxRef<'a>: Clone { + /// The cursor type. + type Cursor: DbCursor; + + /// The cursor type for dupsort table. + // TODO: ideally we should only define the cursor type once, + // find a way to not have to define both cursor types in both traits + type DupCursor: DbDupSortCursor; + + /// Creates a cursor to iterate over a table items. + fn cursor(&self) -> Result, DatabaseError>; + + /// Creates a cursor to iterate over a dupsort table items. + fn cursor_dup(&self) -> Result, DatabaseError>; + + /// Gets a value from a table using the given key. + fn get(&self, key: T::Key) -> Result, DatabaseError>; + + /// Returns number of entries in the table. + fn entries(&self) -> Result; +} + +pub trait DbTxMutRef<'a>: DbTxRef<'a> { + /// The mutable cursor type. + type Cursor: DbCursorMut; + + /// The mutable cursor type for dupsort table. + // TODO: find a way to not have to define both cursor types in both traits + type DupCursor: DbDupSortCursorMut; + + /// Creates a cursor to mutably iterate over a table items. + fn cursor_mut(&self) -> Result<>::Cursor, DatabaseError>; + + /// Creates a cursor to iterate over a dupsort table items. + fn cursor_dup_mut( + &self, + ) -> Result<>::DupCursor, DatabaseError>; + + /// Inserts an item into a database. + /// + /// This function stores key/data pairs in the database. The default behavior is to enter the + /// new key/data pair, replacing any previously existing key if duplicates are disallowed, or + /// adding a duplicate data item if duplicates are allowed (DatabaseFlags::DUP_SORT). + fn put(&self, key: T::Key, value: T::Value) -> Result<(), DatabaseError>; + + /// Delete items from a database, removing the key/data pair if it exists. + /// + /// If the data parameter is [Some] only the matching data item will be deleted. Otherwise, if + /// data parameter is [None], any/all value(s) for specified key will be deleted. + /// + /// Returns `true` if the key/value pair was present. + fn delete(&self, key: T::Key, value: Option) + -> Result; + + /// Clears all entries in the given database. This will empty the database. + fn clear(&self) -> Result<(), DatabaseError>; +} + +impl<'a, Tx: DbTx> DbTxRef<'a> for &'a Tx { + type Cursor = ::Cursor; + type DupCursor = ::DupCursor; + + fn cursor(&self) -> Result, DatabaseError> { + ::cursor::(self) + } + + fn cursor_dup(&self) -> Result, DatabaseError> { + ::cursor_dup::(self) + } + + fn entries(&self) -> Result { + ::entries::(self) + } + + fn get(&self, key: T::Key) -> Result, DatabaseError> { + ::get::(self, key) + } +} + +impl<'a, Tx: DbTxMut> DbTxMutRef<'a> for &'a Tx { + type Cursor = ::Cursor; + type DupCursor = ::DupCursor; + + fn cursor_mut(&self) -> Result<>::Cursor, DatabaseError> { + ::cursor_mut::(self) + } + + fn cursor_dup_mut( + &self, + ) -> Result<>::DupCursor, DatabaseError> { + ::cursor_dup_mut::(self) + } + + fn put(&self, key: T::Key, value: T::Value) -> Result<(), DatabaseError> { + ::put::(self, key, value) + } + + fn delete( + &self, + key: T::Key, + value: Option, + ) -> Result { + ::delete::(self, key, value) + } + + fn clear(&self) -> Result<(), DatabaseError> { + ::clear::(self) + } +} diff --git a/crates/katana/storage/db/src/mdbx/tx.rs b/crates/katana/storage/db/src/mdbx/tx.rs index da902c9f5d..a1d6529045 100644 --- a/crates/katana/storage/db/src/mdbx/tx.rs +++ b/crates/katana/storage/db/src/mdbx/tx.rs @@ -33,7 +33,7 @@ pub struct Tx { impl Tx { /// Creates new `Tx` object with a `RO` or `RW` transaction. pub fn new(inner: libmdbx::Transaction) -> Self { - Self { inner, db_handles: Default::default() } + Self { inner, db_handles: RwLock::new([None; NUM_TABLES]) } } pub fn get_dbi(&self) -> Result { diff --git a/crates/katana/storage/db/src/models/trie.rs b/crates/katana/storage/db/src/models/trie.rs index b10456af57..3e3628919c 100644 --- a/crates/katana/storage/db/src/models/trie.rs +++ b/crates/katana/storage/db/src/models/trie.rs @@ -1,18 +1,48 @@ use katana_trie::bonsai::ByteVec; use serde::{Deserialize, Serialize}; -use crate::codecs::{Decode, Encode}; +use crate::codecs::{Compress, Decode, Decompress, Encode}; use crate::error::CodecError; +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct TrieHistoryEntry { + pub key: TrieDatabaseKey, + pub value: TrieDatabaseValue, +} + +impl Compress for TrieHistoryEntry { + type Compressed = Vec; + + fn compress(self) -> Self::Compressed { + let mut buf = Vec::new(); + buf.extend(self.key.encode()); + buf.extend(self.value.compress()); + buf + } +} + +impl Decompress for TrieHistoryEntry { + fn decompress>(bytes: B) -> Result { + let bytes = bytes.as_ref(); + + let key = TrieDatabaseKey::decode(bytes)?; + // first byte is the key type, second byte is the actual key length + let key_bytes_length = 1 + 1 + key.key.len(); + let value = TrieDatabaseValue::decompress(&bytes[key_bytes_length..])?; + + Ok(Self { key, value }) + } +} + #[repr(u8)] -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] pub enum TrieDatabaseKeyType { Trie = 0, Flat, TrieLog, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct TrieDatabaseKey { pub r#type: TrieDatabaseKeyType, pub key: Vec, @@ -26,6 +56,7 @@ impl Encode for TrieDatabaseKey { fn encode(self) -> Self::Encoded { let mut encoded = Vec::new(); encoded.push(self.r#type as u8); + encoded.push(self.key.len() as u8); // Encode key length encoded.extend(self.key); encoded } @@ -34,7 +65,9 @@ impl Encode for TrieDatabaseKey { impl Decode for TrieDatabaseKey { fn decode>(bytes: B) -> Result { let bytes = bytes.as_ref(); - if bytes.is_empty() { + + if bytes.len() < 2 { + // Need at least type and length bytes panic!("emptyy buffer") } @@ -45,7 +78,13 @@ impl Decode for TrieDatabaseKey { _ => panic!("Invalid trie database key type"), }; - let key = bytes[1..].to_vec(); + let key_len = bytes[1] as usize; + + if bytes.len() < 2 + key_len { + panic!("Buffer too short for key length"); + } + + let key = bytes[2..2 + key_len].to_vec(); Ok(TrieDatabaseKey { r#type, key }) } diff --git a/crates/katana/storage/db/src/tables.rs b/crates/katana/storage/db/src/tables.rs index e4223d38cf..c3ca7c5f44 100644 --- a/crates/katana/storage/db/src/tables.rs +++ b/crates/katana/storage/db/src/tables.rs @@ -11,7 +11,7 @@ use crate::models::contract::{ContractClassChange, ContractInfoChangeList, Contr use crate::models::list::BlockList; use crate::models::stage::{StageCheckpoint, StageId}; use crate::models::storage::{ContractStorageEntry, ContractStorageKey, StorageEntry}; -use crate::models::trie::{TrieDatabaseKey, TrieDatabaseValue}; +use crate::models::trie::{TrieDatabaseKey, TrieDatabaseValue, TrieHistoryEntry}; pub trait Key: Encode + Decode + Clone + std::fmt::Debug {} pub trait Value: Compress + Decompress + std::fmt::Debug {} @@ -20,7 +20,7 @@ impl Key for T where T: Encode + Decode + Clone + std::fmt::Debug {} impl Value for T where T: Compress + Decompress + std::fmt::Debug {} /// An asbtraction for a table. -pub trait Table { +pub trait Table: 'static { /// The name of the table. const NAME: &'static str; /// The key type of the table. @@ -37,7 +37,12 @@ pub trait DupSort: Table { type SubKey: Key; } -pub trait Trie: Table {} +pub trait Trie: Table { + /// Table for storing the trie entries according to the block its was committed. + type History: DupSort; + /// Table for storing the trie change set. + type Changeset: Table; +} /// Enum for the types of tables present in libmdbx. #[derive(Debug, PartialEq, Copy, Clone)] @@ -48,7 +53,7 @@ pub enum TableType { DupSort, } -pub const NUM_TABLES: usize = 27; +pub const NUM_TABLES: usize = 33; /// Macro to declare `libmdbx` tables. #[macro_export] @@ -172,10 +177,16 @@ define_tables_enum! {[ (ClassChangeHistory, TableType::DupSort), (StorageChangeHistory, TableType::DupSort), (StorageChangeSet, TableType::Table), - (ClassTrie, TableType::Table), - (ContractTrie, TableType::Table), - (ContractStorageTrie, TableType::Table), - (StageCheckpoints, TableType::Table) + (StageCheckpoints, TableType::Table), + (ClassesTrie, TableType::Table), + (ContractsTrie, TableType::Table), + (StoragesTrie, TableType::Table), + (ClassesTrieHistory, TableType::DupSort), + (ContractsTrieHistory, TableType::DupSort), + (StoragesTrieHistory, TableType::DupSort), + (ClassesTrieChangeSet, TableType::Table), + (ContractsTrieChangeSet, TableType::Table), + (StoragesTrieChangeSet, TableType::Table) ]} tables! { @@ -236,16 +247,41 @@ tables! { StorageChangeHistory: (BlockNumber, ContractStorageKey) => ContractStorageEntry, /// Class trie - ClassTrie: (TrieDatabaseKey) => TrieDatabaseValue, + ClassesTrie: (TrieDatabaseKey) => TrieDatabaseValue, /// Contract trie - ContractTrie: (TrieDatabaseKey) => TrieDatabaseValue, + ContractsTrie: (TrieDatabaseKey) => TrieDatabaseValue, /// Contract storage trie - ContractStorageTrie: (TrieDatabaseKey) => TrieDatabaseValue + StoragesTrie: (TrieDatabaseKey) => TrieDatabaseValue, + + /// Class trie history + ClassesTrieHistory: (BlockNumber, TrieDatabaseKey) => TrieHistoryEntry, + /// Contract trie history + ContractsTrieHistory: (BlockNumber, TrieDatabaseKey) => TrieHistoryEntry, + /// Contract storage trie history + StoragesTrieHistory: (BlockNumber, TrieDatabaseKey) => TrieHistoryEntry, + + /// Class trie change set + ClassesTrieChangeSet: (TrieDatabaseKey) => BlockList, + /// contract trie change set + ContractsTrieChangeSet: (TrieDatabaseKey) => BlockList, + /// contract storage trie change set + StoragesTrieChangeSet: (TrieDatabaseKey) => BlockList +} + +impl Trie for ClassesTrie { + type History = ClassesTrieHistory; + type Changeset = ClassesTrieChangeSet; } -impl Trie for ClassTrie {} -impl Trie for ContractTrie {} -impl Trie for ContractStorageTrie {} +impl Trie for ContractsTrie { + type History = ContractsTrieHistory; + type Changeset = ContractsTrieChangeSet; +} + +impl Trie for StoragesTrie { + type History = StoragesTrieHistory; + type Changeset = StoragesTrieChangeSet; +} #[cfg(test)] mod tests { @@ -278,10 +314,16 @@ mod tests { assert_eq!(Tables::ALL[20].name(), ClassChangeHistory::NAME); assert_eq!(Tables::ALL[21].name(), StorageChangeHistory::NAME); assert_eq!(Tables::ALL[22].name(), StorageChangeSet::NAME); - assert_eq!(Tables::ALL[23].name(), ClassTrie::NAME); - assert_eq!(Tables::ALL[24].name(), ContractTrie::NAME); - assert_eq!(Tables::ALL[25].name(), ContractStorageTrie::NAME); - assert_eq!(Tables::ALL[26].name(), StageCheckpoints::NAME); + assert_eq!(Tables::ALL[23].name(), StageCheckpoints::NAME); + assert_eq!(Tables::ALL[24].name(), ClassesTrie::NAME); + assert_eq!(Tables::ALL[25].name(), ContractsTrie::NAME); + assert_eq!(Tables::ALL[26].name(), StoragesTrie::NAME); + assert_eq!(Tables::ALL[27].name(), ClassesTrieHistory::NAME); + assert_eq!(Tables::ALL[28].name(), ContractsTrieHistory::NAME); + assert_eq!(Tables::ALL[29].name(), StoragesTrieHistory::NAME); + assert_eq!(Tables::ALL[30].name(), ClassesTrieChangeSet::NAME); + assert_eq!(Tables::ALL[31].name(), ContractsTrieChangeSet::NAME); + assert_eq!(Tables::ALL[32].name(), StoragesTrieChangeSet::NAME); assert_eq!(Tables::Headers.table_type(), TableType::Table); assert_eq!(Tables::BlockHashes.table_type(), TableType::Table); @@ -306,10 +348,16 @@ mod tests { assert_eq!(Tables::ClassChangeHistory.table_type(), TableType::DupSort); assert_eq!(Tables::StorageChangeHistory.table_type(), TableType::DupSort); assert_eq!(Tables::StorageChangeSet.table_type(), TableType::Table); - assert_eq!(Tables::ClassTrie.table_type(), TableType::Table); - assert_eq!(Tables::ContractTrie.table_type(), TableType::Table); - assert_eq!(Tables::ContractStorageTrie.table_type(), TableType::Table); assert_eq!(Tables::StageCheckpoints.table_type(), TableType::Table); + assert_eq!(Tables::ClassesTrie.table_type(), TableType::Table); + assert_eq!(Tables::ContractsTrie.table_type(), TableType::Table); + assert_eq!(Tables::StoragesTrie.table_type(), TableType::Table); + assert_eq!(Tables::ClassesTrieHistory.table_type(), TableType::DupSort); + assert_eq!(Tables::ContractsTrieHistory.table_type(), TableType::DupSort); + assert_eq!(Tables::StoragesTrieHistory.table_type(), TableType::DupSort); + assert_eq!(Tables::ClassesTrieChangeSet.table_type(), TableType::Table); + assert_eq!(Tables::ContractsTrieChangeSet.table_type(), TableType::Table); + assert_eq!(Tables::StoragesTrieChangeSet.table_type(), TableType::Table); } use katana_primitives::address; @@ -329,6 +377,9 @@ mod tests { }; use crate::models::list::BlockList; use crate::models::storage::{ContractStorageEntry, ContractStorageKey, StorageEntry}; + use crate::models::trie::{ + TrieDatabaseKey, TrieDatabaseKeyType, TrieDatabaseValue, TrieHistoryEntry, + }; macro_rules! assert_key_encode_decode { { $( ($name:ty, $key:expr) ),* } => { @@ -396,12 +447,17 @@ mod tests { (BlockList, BlockList::default()), (ContractStorageEntry, ContractStorageEntry::default()), (Receipt, Receipt::Invoke(InvokeTxReceipt { - revert_error: None, - events: Vec::new(), - messages_sent: Vec::new(), - execution_resources: Default::default(), - fee: TxFeeInfo { gas_consumed: 0, gas_price: 0, overall_fee: 0, unit: PriceUnit::Wei }, - })) + revert_error: None, + events: Vec::new(), + messages_sent: Vec::new(), + execution_resources: Default::default(), + fee: TxFeeInfo { gas_consumed: 0, gas_price: 0, overall_fee: 0, unit: PriceUnit::Wei }, + })), + (TrieDatabaseValue, TrieDatabaseValue::default()), + (TrieHistoryEntry, TrieHistoryEntry { + value: TrieDatabaseValue::default(), + key: TrieDatabaseKey { key: Vec::default(), r#type: TrieDatabaseKeyType::Flat }, + }) } } } diff --git a/crates/katana/storage/db/src/trie/class.rs b/crates/katana/storage/db/src/trie/class.rs deleted file mode 100644 index 4e853829e9..0000000000 --- a/crates/katana/storage/db/src/trie/class.rs +++ /dev/null @@ -1,55 +0,0 @@ -use bitvec::order::Msb0; -use bitvec::vec::BitVec; -use bitvec::view::AsBits; -use katana_primitives::block::BlockNumber; -use katana_primitives::class::{ClassHash, CompiledClassHash}; -use katana_primitives::Felt; -use katana_trie::bonsai::id::BasicId; -use katana_trie::bonsai::{BonsaiStorage, BonsaiStorageConfig}; -use starknet::macros::short_string; -use starknet_types_core::hash::{Poseidon, StarkHash}; - -use crate::abstraction::DbTxMut; -use crate::tables; -use crate::trie::TrieDb; - -// https://docs.starknet.io/architecture-and-concepts/network-architecture/starknet-state/#classes_trie -const CONTRACT_CLASS_LEAF_V0: Felt = short_string!("CONTRACT_CLASS_LEAF_V0"); - -#[derive(Debug)] -pub struct ClassTrie { - inner: BonsaiStorage, Poseidon>, -} - -impl ClassTrie { - pub fn new(tx: Tx) -> Self { - let config = BonsaiStorageConfig { - max_saved_trie_logs: Some(0), - max_saved_snapshots: Some(0), - snapshot_interval: u64::MAX, - }; - - let db = TrieDb::::new(tx); - let inner = BonsaiStorage::new(db, config).unwrap(); - - Self { inner } - } - - pub fn insert(&mut self, hash: ClassHash, compiled_hash: CompiledClassHash) { - let value = Poseidon::hash(&CONTRACT_CLASS_LEAF_V0, &compiled_hash); - let key: BitVec = hash.to_bytes_be().as_bits()[5..].to_owned(); - self.inner.insert(self.bonsai_identifier(), &key, &value).unwrap(); - } - - pub fn commit(&mut self, block_number: BlockNumber) { - self.inner.commit(BasicId::new(block_number)).unwrap(); - } - - pub fn root(&self) -> Felt { - self.inner.root_hash(self.bonsai_identifier()).unwrap() - } - - fn bonsai_identifier(&self) -> &'static [u8] { - b"1" - } -} diff --git a/crates/katana/storage/db/src/trie/contract.rs b/crates/katana/storage/db/src/trie/contract.rs deleted file mode 100644 index 5e460739aa..0000000000 --- a/crates/katana/storage/db/src/trie/contract.rs +++ /dev/null @@ -1,83 +0,0 @@ -use bitvec::order::Msb0; -use bitvec::vec::BitVec; -use bitvec::view::AsBits; -use katana_primitives::block::BlockNumber; -use katana_primitives::contract::{StorageKey, StorageValue}; -use katana_primitives::{ContractAddress, Felt}; -use katana_trie::bonsai::id::BasicId; -use katana_trie::bonsai::{BonsaiStorage, BonsaiStorageConfig}; -use starknet_types_core::hash::Poseidon; - -use crate::abstraction::DbTxMut; -use crate::tables; -use crate::trie::TrieDb; - -#[derive(Debug)] -pub struct StorageTrie { - inner: BonsaiStorage, Poseidon>, -} - -impl StorageTrie { - pub fn new(tx: Tx) -> Self { - let config = BonsaiStorageConfig { - max_saved_trie_logs: Some(0), - max_saved_snapshots: Some(0), - snapshot_interval: u64::MAX, - }; - - let db = TrieDb::::new(tx); - let inner = BonsaiStorage::new(db, config).unwrap(); - - Self { inner } - } - - pub fn insert(&mut self, address: ContractAddress, key: StorageKey, value: StorageValue) { - let key: BitVec = key.to_bytes_be().as_bits()[5..].to_owned(); - self.inner.insert(&address.to_bytes_be(), &key, &value).unwrap(); - } - - pub fn commit(&mut self, block_number: BlockNumber) { - self.inner.commit(BasicId::new(block_number)).unwrap(); - } - - pub fn root(&self, address: &ContractAddress) -> Felt { - self.inner.root_hash(&address.to_bytes_be()).unwrap() - } -} - -#[derive(Debug)] -pub struct ContractTrie { - inner: BonsaiStorage, Poseidon>, -} - -impl ContractTrie { - pub fn new(tx: Tx) -> Self { - let config = BonsaiStorageConfig { - max_saved_trie_logs: Some(0), - max_saved_snapshots: Some(0), - snapshot_interval: u64::MAX, - }; - - let db = TrieDb::::new(tx); - let inner = BonsaiStorage::new(db, config).unwrap(); - - Self { inner } - } - - pub fn insert(&mut self, address: ContractAddress, state_hash: Felt) { - let key: BitVec = address.to_bytes_be().as_bits()[5..].to_owned(); - self.inner.insert(self.bonsai_identifier(), &key, &state_hash).unwrap(); - } - - pub fn commit(&mut self, block_number: BlockNumber) { - self.inner.commit(BasicId::new(block_number)).unwrap(); - } - - pub fn root(&self) -> Felt { - self.inner.root_hash(self.bonsai_identifier()).unwrap() - } - - fn bonsai_identifier(&self) -> &'static [u8] { - b"1" - } -} diff --git a/crates/katana/storage/db/src/trie/mod.rs b/crates/katana/storage/db/src/trie/mod.rs index 823c9f28ba..de5aaa12e2 100644 --- a/crates/katana/storage/db/src/trie/mod.rs +++ b/crates/katana/storage/db/src/trie/mod.rs @@ -1,20 +1,22 @@ +use core::fmt; +use std::collections::HashMap; +use std::fmt::Debug; use std::marker::PhantomData; use anyhow::Result; -use katana_trie::bonsai::id::BasicId; -use katana_trie::bonsai::{self, ByteVec, DatabaseKey}; +use katana_primitives::block::BlockNumber; +use katana_trie::bonsai::{BonsaiDatabase, BonsaiPersistentDatabase, ByteVec, DatabaseKey}; +use katana_trie::CommitId; use smallvec::ToSmallVec; -use crate::abstraction::{DbCursor, DbTxMut}; -use crate::models::trie::{TrieDatabaseKey, TrieDatabaseKeyType}; +use crate::abstraction::{DbCursor, DbTxMutRef, DbTxRef}; +use crate::models::trie::{TrieDatabaseKey, TrieDatabaseKeyType, TrieHistoryEntry}; use crate::models::{self}; -use crate::tables; +use crate::tables::{self, Trie}; -mod class; -mod contract; +mod snapshot; -pub use class::ClassTrie; -pub use contract::{ContractTrie, StorageTrie}; +pub use snapshot::SnapshotTrieDb; #[derive(Debug, thiserror::Error)] #[error(transparent)] @@ -23,25 +25,218 @@ pub struct Error(#[from] crate::error::DatabaseError); impl katana_trie::bonsai::DBError for Error {} #[derive(Debug)] -pub struct TrieDb { +pub struct TrieDbFactory<'a, Tx: DbTxRef<'a>> { tx: Tx, - _table: PhantomData, + _phantom: &'a PhantomData<()>, } -impl TrieDb +impl<'a, Tx: DbTxRef<'a>> TrieDbFactory<'a, Tx> { + pub fn new(tx: Tx) -> Self { + Self { tx, _phantom: &PhantomData } + } + + pub fn latest(&self) -> GlobalTrie<'a, Tx> { + GlobalTrie { tx: self.tx.clone(), _phantom: &PhantomData } + } + + // TODO: check that the snapshot for the block number is available + pub fn historical(&self, block: BlockNumber) -> Option> { + Some(HistoricalGlobalTrie { tx: self.tx.clone(), block, _phantom: &PhantomData }) + } +} + +/// Provides access to the latest tries. +#[derive(Debug)] +pub struct GlobalTrie<'a, Tx: DbTxRef<'a>> { + tx: Tx, + _phantom: &'a PhantomData<()>, +} + +impl<'a, Tx> GlobalTrie<'a, Tx> +where + Tx: DbTxRef<'a> + Debug, +{ + /// Returns the contracts trie. + pub fn contracts_trie( + &self, + ) -> katana_trie::ContractsTrie> { + katana_trie::ContractsTrie::new(TrieDb::new(self.tx.clone())) + } + + /// Returns the classes trie. + pub fn classes_trie(&self) -> katana_trie::ClassesTrie> { + katana_trie::ClassesTrie::new(TrieDb::new(self.tx.clone())) + } + + /// Returns the storages trie. + pub fn storages_trie(&self) -> katana_trie::StoragesTrie> { + katana_trie::StoragesTrie::new(TrieDb::new(self.tx.clone())) + } +} + +/// Historical tries, allowing access to the state tries at each block. +#[derive(Debug)] +pub struct HistoricalGlobalTrie<'a, Tx: DbTxRef<'a>> { + /// The database transaction. + tx: Tx, + /// The block number at which the trie was constructed. + block: BlockNumber, + _phantom: &'a PhantomData<()>, +} + +impl<'a, Tx> HistoricalGlobalTrie<'a, Tx> +where + Tx: DbTxRef<'a> + Debug, +{ + /// Returns the historical contracts trie. + pub fn contracts_trie( + &self, + ) -> katana_trie::ContractsTrie> { + let commit = CommitId::new(self.block); + katana_trie::ContractsTrie::new(SnapshotTrieDb::new(self.tx.clone(), commit)) + } + + /// Returns the historical classes trie. + pub fn classes_trie( + &self, + ) -> katana_trie::ClassesTrie> { + let commit = CommitId::new(self.block); + katana_trie::ClassesTrie::new(SnapshotTrieDb::new(self.tx.clone(), commit)) + } + + /// Returns the historical storages trie. + pub fn storages_trie( + &self, + ) -> katana_trie::StoragesTrie> { + let commit = CommitId::new(self.block); + katana_trie::StoragesTrie::new(SnapshotTrieDb::new(self.tx.clone(), commit)) + } +} + +// --- Trie's database implementations. These are implemented based on the Bonsai Trie +// functionalities and abstractions. + +pub struct TrieDb<'a, Tb, Tx> +where + Tb: Trie, + Tx: DbTxRef<'a>, +{ + tx: Tx, + _phantom: &'a PhantomData, +} + +impl<'a, Tb, Tx> fmt::Debug for TrieDb<'a, Tb, Tx> +where + Tb: Trie, + Tx: DbTxRef<'a> + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TrieDbMut").field("tx", &self.tx).finish() + } +} + +impl<'a, Tb, Tx> TrieDb<'a, Tb, Tx> +where + Tb: Trie, + Tx: DbTxRef<'a>, +{ + pub(crate) fn new(tx: Tx) -> Self { + Self { tx, _phantom: &PhantomData } + } +} + +impl<'a, Tb, Tx> BonsaiDatabase for TrieDb<'a, Tb, Tx> +where + Tb: Trie, + Tx: DbTxRef<'a> + fmt::Debug, +{ + type Batch = (); + type DatabaseError = Error; + + fn create_batch(&self) -> Self::Batch {} + + fn remove_by_prefix(&mut self, _: &DatabaseKey<'_>) -> Result<(), Self::DatabaseError> { + Ok(()) + } + + fn get(&self, key: &DatabaseKey<'_>) -> Result, Self::DatabaseError> { + let value = self.tx.get::(to_db_key(key))?; + Ok(value) + } + + fn get_by_prefix( + &self, + _: &DatabaseKey<'_>, + ) -> Result, Self::DatabaseError> { + todo!() + } + + fn insert( + &mut self, + _: &DatabaseKey<'_>, + _: &[u8], + _: Option<&mut Self::Batch>, + ) -> Result, Self::DatabaseError> { + unimplemented!("not supported in read-only transaction") + } + + fn remove( + &mut self, + _: &DatabaseKey<'_>, + _: Option<&mut Self::Batch>, + ) -> Result, Self::DatabaseError> { + unimplemented!("not supported in read-only transaction") + } + + fn contains(&self, key: &DatabaseKey<'_>) -> Result { + let key = to_db_key(key); + let value = self.tx.get::(key)?; + Ok(value.is_some()) + } + + fn write_batch(&mut self, _: Self::Batch) -> Result<(), Self::DatabaseError> { + unimplemented!("not supported in read-only transaction") + } +} + +pub struct TrieDbMut<'tx, Tb, Tx> +where + Tb: Trie, + Tx: DbTxMutRef<'tx>, +{ + tx: Tx, + /// List of key-value pairs that has been added throughout the duration of the trie + /// transaction. + /// + /// This will be used to create the trie snapshot. + write_cache: HashMap, + _phantom: &'tx PhantomData, +} + +impl<'tx, Tb, Tx> fmt::Debug for TrieDbMut<'tx, Tb, Tx> +where + Tb: Trie, + Tx: DbTxMutRef<'tx> + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TrieDbMut").field("tx", &self.tx).finish() + } +} + +impl<'tx, Tb, Tx> TrieDbMut<'tx, Tb, Tx> where - Tb: tables::Trie, - Tx: DbTxMut, + Tb: Trie, + Tx: DbTxMutRef<'tx>, { pub fn new(tx: Tx) -> Self { - Self { tx, _table: PhantomData } + Self { tx, write_cache: HashMap::new(), _phantom: &PhantomData } } } -impl bonsai::BonsaiDatabase for TrieDb +impl<'tx, Tb, Tx> BonsaiDatabase for TrieDbMut<'tx, Tb, Tx> where - Tb: tables::Trie, - Tx: DbTxMut, + Tb: Trie, + Tx: DbTxMutRef<'tx> + fmt::Debug, { type Batch = (); type DatabaseError = Error; @@ -85,23 +280,30 @@ where &mut self, key: &DatabaseKey<'_>, value: &[u8], - _batch: Option<&mut Self::Batch>, + batch: Option<&mut Self::Batch>, ) -> Result, Self::DatabaseError> { + let _ = batch; let key = to_db_key(key); let value: ByteVec = value.to_smallvec(); + let old_value = self.tx.get::(key.clone())?; - self.tx.put::(key, value)?; + self.tx.put::(key.clone(), value.clone())?; + + self.write_cache.insert(key, value); Ok(old_value) } fn remove( &mut self, key: &DatabaseKey<'_>, - _batch: Option<&mut Self::Batch>, + batch: Option<&mut Self::Batch>, ) -> Result, Self::DatabaseError> { + let _ = batch; let key = to_db_key(key); + let old_value = self.tx.get::(key.clone())?; self.tx.delete::(key, None)?; + Ok(old_value) } @@ -111,27 +313,56 @@ where Ok(value.is_some()) } - fn write_batch(&mut self, _batch: Self::Batch) -> Result<(), Self::DatabaseError> { + fn write_batch(&mut self, _: Self::Batch) -> Result<(), Self::DatabaseError> { Ok(()) } } -impl bonsai::BonsaiPersistentDatabase for TrieDb +impl<'tx, Tb, Tx> BonsaiPersistentDatabase for TrieDbMut<'tx, Tb, Tx> where - Tb: tables::Trie, - Tx: DbTxMut, + Tb: Trie, + Tx: DbTxMutRef<'tx> + fmt::Debug + 'tx, { type DatabaseError = Error; - type Transaction = TrieDb; + type Transaction<'a> = SnapshotTrieDb<'tx, Tb, Tx> where Self: 'a; + + fn snapshot(&mut self, id: CommitId) { + let block_number: BlockNumber = id.into(); + + let entries = std::mem::take(&mut self.write_cache); + let entries = entries.into_iter().map(|(key, value)| TrieHistoryEntry { key, value }); + + for entry in entries { + let mut set = self + .tx + .get::(entry.key.clone()) + .expect("failed to get trie change set") + .unwrap_or_default(); + set.insert(block_number); - fn snapshot(&mut self, _: BasicId) {} + self.tx + .put::(entry.key.clone(), set) + .expect("failed to put trie change set"); + + self.tx + .put::(block_number, entry) + .expect("failed to put trie history entry"); + } + } - fn merge(&mut self, _: Self::Transaction) -> Result<(), Self::DatabaseError> { - todo!(); + // merging should recompute the trie again + fn merge<'a>(&mut self, transaction: Self::Transaction<'a>) -> Result<(), Self::DatabaseError> + where + Self: 'a, + { + let _ = transaction; + unimplemented!(); } - fn transaction(&self, _: BasicId) -> Option { - todo!(); + // TODO: check if the snapshot exist + fn transaction(&self, id: CommitId) -> Option<(CommitId, Self::Transaction<'_>)> { + dbg!("getting snapshot", id); + Some((id, SnapshotTrieDb::new(self.tx.clone(), id))) } } diff --git a/crates/katana/storage/db/src/trie/snapshot.rs b/crates/katana/storage/db/src/trie/snapshot.rs new file mode 100644 index 0000000000..b9adff8d72 --- /dev/null +++ b/crates/katana/storage/db/src/trie/snapshot.rs @@ -0,0 +1,123 @@ +use core::fmt; +use std::marker::PhantomData; + +use anyhow::Result; +use katana_primitives::block::BlockNumber; +use katana_trie::bonsai::{BonsaiDatabase, ByteVec, DatabaseKey}; +use katana_trie::CommitId; + +use super::Error; +use crate::abstraction::{DbDupSortCursor, DbTxRef}; +use crate::models::list::BlockList; +use crate::tables::Trie; +use crate::trie::to_db_key; + +pub struct SnapshotTrieDb<'tx, Tb, Tx> +where + Tb: Trie, + Tx: DbTxRef<'tx>, +{ + tx: Tx, + snapshot_id: CommitId, + _table: &'tx PhantomData, +} + +impl<'a, Tb, Tx> fmt::Debug for SnapshotTrieDb<'a, Tb, Tx> +where + Tb: Trie, + Tx: DbTxRef<'a> + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SnapshotTrieDb").field("tx", &self.tx).finish() + } +} + +/// This is a helper function for getting the block number of the most +/// recent change that occurred relative to the given block number. +/// +/// ## Arguments +/// +/// * `block_list`: A list of block numbers where a change in value occur. +fn recent_change_from_block(target: BlockNumber, block_list: &BlockList) -> Option { + // if the rank is 0, then it's either; + // 1. the list is empty + // 2. there are no prior changes occured before/at `block_number` + let rank = block_list.rank(target); + if rank == 0 { None } else { block_list.select(rank - 1) } +} + +impl<'tx, Tb, Tx> SnapshotTrieDb<'tx, Tb, Tx> +where + Tb: Trie, + Tx: DbTxRef<'tx>, +{ + pub(crate) fn new(tx: Tx, id: CommitId) -> Self { + Self { tx, snapshot_id: id, _table: &PhantomData } + } +} + +impl<'tx, Tb, Tx> BonsaiDatabase for SnapshotTrieDb<'tx, Tb, Tx> +where + Tb: Trie, + Tx: DbTxRef<'tx> + fmt::Debug, +{ + type Batch = (); + type DatabaseError = Error; + + fn create_batch(&self) -> Self::Batch {} + + fn remove_by_prefix(&mut self, _: &DatabaseKey<'_>) -> Result<(), Self::DatabaseError> { + unimplemented!("modifying trie snapshot is not supported") + } + + fn get(&self, key: &DatabaseKey<'_>) -> Result, Self::DatabaseError> { + let key = to_db_key(key); + let block_number = self.snapshot_id.into(); + + let change_set = self.tx.get::(key.clone())?; + if let Some(num) = change_set.and_then(|set| recent_change_from_block(block_number, &set)) { + let mut cursor = self.tx.cursor_dup::()?; + let entry = cursor + .seek_by_key_subkey(num, key.clone())? + .expect("entry should exist if in change set"); + + if entry.key == key { + return Ok(Some(entry.value)); + } + } + + Ok(None) + } + + fn get_by_prefix( + &self, + _: &DatabaseKey<'_>, + ) -> Result, Self::DatabaseError> { + todo!() + } + + fn insert( + &mut self, + _: &DatabaseKey<'_>, + _: &[u8], + _: Option<&mut Self::Batch>, + ) -> Result, Self::DatabaseError> { + unimplemented!("modifying trie snapshot is not supported") + } + + fn remove( + &mut self, + _: &DatabaseKey<'_>, + _: Option<&mut Self::Batch>, + ) -> Result, Self::DatabaseError> { + unimplemented!("modifying trie snapshot is not supported") + } + + fn contains(&self, _: &DatabaseKey<'_>) -> Result { + todo!() + } + + fn write_batch(&mut self, _: Self::Batch) -> Result<(), Self::DatabaseError> { + unimplemented!("modifying trie snapshot is not supported") + } +} diff --git a/crates/katana/storage/db/src/version.rs b/crates/katana/storage/db/src/version.rs index c8542f5b26..c7797c39b7 100644 --- a/crates/katana/storage/db/src/version.rs +++ b/crates/katana/storage/db/src/version.rs @@ -5,7 +5,7 @@ use std::mem; use std::path::{Path, PathBuf}; /// Current version of the database. -pub const CURRENT_DB_VERSION: u32 = 5; +pub const CURRENT_DB_VERSION: u32 = 6; /// Name of the version file. const DB_VERSION_FILE_NAME: &str = "db.version"; @@ -81,6 +81,6 @@ mod tests { #[test] fn test_current_version() { use super::CURRENT_DB_VERSION; - assert_eq!(CURRENT_DB_VERSION, 5, "Invalid current database version") + assert_eq!(CURRENT_DB_VERSION, 6, "Invalid current database version") } } diff --git a/crates/katana/storage/provider/src/lib.rs b/crates/katana/storage/provider/src/lib.rs index dc156ce7f6..191af03cdd 100644 --- a/crates/katana/storage/provider/src/lib.rs +++ b/crates/katana/storage/provider/src/lib.rs @@ -16,12 +16,12 @@ use katana_primitives::trace::TxExecInfo; use katana_primitives::transaction::{TxHash, TxNumber, TxWithHash}; use katana_primitives::Felt; use traits::block::{BlockIdReader, BlockStatusProvider, BlockWriter}; -use traits::contract::{ContractClassProvider, ContractClassWriter, ContractClassWriterExt}; +use traits::contract::{ContractClassWriter, ContractClassWriterExt}; use traits::env::BlockEnvProvider; use traits::stage::StageCheckpointProvider; -use traits::state::{StateRootProvider, StateWriter}; +use traits::state::StateWriter; use traits::transaction::{TransactionStatusProvider, TransactionTraceProvider}; -use traits::trie::{ClassTrieWriter, ContractTrieWriter}; +use traits::trie::TrieWriter; pub mod error; pub mod providers; @@ -246,53 +246,6 @@ where } } -impl StateProvider for BlockchainProvider -where - Db: StateProvider, -{ - fn nonce( - &self, - address: ContractAddress, - ) -> ProviderResult> { - self.provider.nonce(address) - } - - fn storage( - &self, - address: ContractAddress, - storage_key: StorageKey, - ) -> ProviderResult> { - self.provider.storage(address, storage_key) - } - - fn class_hash_of_contract( - &self, - address: ContractAddress, - ) -> ProviderResult> { - self.provider.class_hash_of_contract(address) - } -} - -impl ContractClassProvider for BlockchainProvider -where - Db: ContractClassProvider, -{ - fn class(&self, hash: ClassHash) -> ProviderResult> { - self.provider.class(hash) - } - - fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { - self.provider.compiled_class(hash) - } - - fn compiled_class_hash_of_class_hash( - &self, - hash: ClassHash, - ) -> ProviderResult> { - self.provider.compiled_class_hash_of_class_hash(hash) - } -} - impl StateFactoryProvider for BlockchainProvider where Db: StateFactoryProvider, @@ -332,15 +285,6 @@ where } } -impl StateRootProvider for BlockchainProvider -where - Db: StateRootProvider, -{ - fn state_root(&self, block_id: BlockHashOrNumber) -> ProviderResult> { - self.provider.state_root(block_id) - } -} - impl ContractClassWriter for BlockchainProvider where Db: ContractClassWriter, @@ -406,29 +350,24 @@ where } } -impl ClassTrieWriter for BlockchainProvider +impl TrieWriter for BlockchainProvider where - Db: ClassTrieWriter, + Db: TrieWriter, { - fn insert_updates( + fn trie_insert_declared_classes( &self, block_number: BlockNumber, updates: &BTreeMap, ) -> ProviderResult { - self.provider.insert_updates(block_number, updates) + self.provider.trie_insert_declared_classes(block_number, updates) } -} -impl ContractTrieWriter for BlockchainProvider -where - Db: ContractTrieWriter, -{ - fn insert_updates( + fn trie_insert_contract_updates( &self, block_number: BlockNumber, state_updates: &StateUpdates, ) -> ProviderResult { - self.provider.insert_updates(block_number, state_updates) + self.provider.trie_insert_contract_updates(block_number, state_updates) } } diff --git a/crates/katana/storage/provider/src/providers/db/mod.rs b/crates/katana/storage/provider/src/providers/db/mod.rs index f880dfc73c..17b028669b 100644 --- a/crates/katana/storage/provider/src/providers/db/mod.rs +++ b/crates/katana/storage/provider/src/providers/db/mod.rs @@ -31,7 +31,6 @@ use katana_primitives::receipt::Receipt; use katana_primitives::state::{StateUpdates, StateUpdatesWithClasses}; use katana_primitives::trace::TxExecInfo; use katana_primitives::transaction::{TxHash, TxNumber, TxWithHash}; -use katana_primitives::Felt; use crate::error::ProviderError; use crate::traits::block::{ @@ -40,7 +39,7 @@ use crate::traits::block::{ }; use crate::traits::env::BlockEnvProvider; use crate::traits::stage::StageCheckpointProvider; -use crate::traits::state::{StateFactoryProvider, StateProvider, StateRootProvider}; +use crate::traits::state::{StateFactoryProvider, StateProvider}; use crate::traits::state_update::StateUpdateProvider; use crate::traits::transaction::{ ReceiptProvider, TransactionProvider, TransactionStatusProvider, TransactionTraceProvider, @@ -256,25 +255,6 @@ impl BlockStatusProvider for DbProvider { } } -impl StateRootProvider for DbProvider { - fn state_root(&self, block_id: BlockHashOrNumber) -> ProviderResult> { - let db_tx = self.0.tx()?; - - let block_num = match block_id { - BlockHashOrNumber::Num(num) => Some(num), - BlockHashOrNumber::Hash(hash) => db_tx.get::(hash)?, - }; - - if let Some(block_num) = block_num { - let header = db_tx.get::(block_num)?; - db_tx.commit()?; - Ok(header.map(|h| h.state_root)) - } else { - Ok(None) - } - } -} - // A helper function that iterates over all entries in a dupsort table and collects the // results into `V`. If `key` is not found, `V::default()` is returned. fn dup_entries( diff --git a/crates/katana/storage/provider/src/providers/db/state.rs b/crates/katana/storage/provider/src/providers/db/state.rs index 0589ac5d30..fbf2498898 100644 --- a/crates/katana/storage/provider/src/providers/db/state.rs +++ b/crates/katana/storage/provider/src/providers/db/state.rs @@ -5,6 +5,7 @@ use katana_db::models::contract::ContractInfoChangeList; use katana_db::models::list::BlockList; use katana_db::models::storage::{ContractStorageKey, StorageEntry}; use katana_db::tables; +use katana_db::trie::TrieDbFactory; use katana_primitives::block::BlockNumber; use katana_primitives::class::{ClassHash, CompiledClass, CompiledClassHash, ContractClass}; use katana_primitives::contract::{ @@ -14,7 +15,7 @@ use katana_primitives::contract::{ use super::DbProvider; use crate::error::ProviderError; use crate::traits::contract::{ContractClassProvider, ContractClassWriter, ContractClassWriterExt}; -use crate::traits::state::{StateProvider, StateWriter}; +use crate::traits::state::{StateProofProvider, StateProvider, StateRootProvider, StateWriter}; use crate::ProviderResult; impl StateWriter for DbProvider { @@ -160,6 +161,51 @@ where } } +impl StateProofProvider for LatestStateProvider +where + Tx: DbTx + fmt::Debug + Send + Sync, +{ + fn class_multiproof(&self, classes: Vec) -> ProviderResult { + let mut trie = TrieDbFactory::new(&self.0).latest().classes_trie(); + let proofs = trie.multiproof(classes); + Ok(proofs) + } + + fn contract_multiproof( + &self, + addresses: Vec, + ) -> ProviderResult { + let mut trie = TrieDbFactory::new(&self.0).latest().contracts_trie(); + let proofs = trie.multiproof(addresses); + Ok(proofs) + } + + fn storage_multiproof( + &self, + address: ContractAddress, + storage_keys: Vec, + ) -> ProviderResult { + let mut trie = TrieDbFactory::new(&self.0).latest().storages_trie(); + let proofs = trie.multiproof(address, storage_keys); + Ok(proofs) + } +} + +impl StateRootProvider for LatestStateProvider +where + Tx: DbTx + fmt::Debug + Send + Sync, +{ + fn classes_root(&self) -> ProviderResult { + let trie = TrieDbFactory::new(&self.0).latest().classes_trie(); + Ok(trie.root()) + } + + fn contracts_root(&self) -> ProviderResult { + let trie = TrieDbFactory::new(&self.0).latest().contracts_trie(); + Ok(trie.root()) + } +} + /// A historical state provider. #[derive(Debug)] pub(super) struct HistoricalStateProvider { @@ -293,8 +339,79 @@ where } } +impl StateProofProvider for HistoricalStateProvider +where + Tx: DbTx + fmt::Debug + Send + Sync, +{ + fn class_multiproof(&self, classes: Vec) -> ProviderResult { + let proofs = TrieDbFactory::new(&self.tx) + .historical(self.block_number) + .expect("should exist") + .classes_trie() + .multiproof(classes); + Ok(proofs) + } + + fn contract_multiproof( + &self, + addresses: Vec, + ) -> ProviderResult { + let proofs = TrieDbFactory::new(&self.tx) + .historical(self.block_number) + .expect("should exist") + .contracts_trie() + .multiproof(addresses); + Ok(proofs) + } + + fn storage_multiproof( + &self, + address: ContractAddress, + storage_keys: Vec, + ) -> ProviderResult { + let proofs = TrieDbFactory::new(&self.tx) + .historical(self.block_number) + .expect("should exist") + .storages_trie() + .multiproof(address, storage_keys); + Ok(proofs) + } +} + +impl StateRootProvider for HistoricalStateProvider +where + Tx: DbTx + fmt::Debug + Send + Sync, +{ + fn classes_root(&self) -> ProviderResult { + let root = TrieDbFactory::new(&self.tx) + .historical(self.block_number) + .expect("should exist") + .classes_trie() + .root(); + Ok(root) + } + + fn contracts_root(&self) -> ProviderResult { + let root = TrieDbFactory::new(&self.tx) + .historical(self.block_number) + .expect("should exist") + .contracts_trie() + .root(); + Ok(root) + } + + fn state_root(&self) -> ProviderResult { + let header = self.tx.get::(self.block_number)?.expect("should exist"); + Ok(header.state_root) + } +} + /// This is a helper function for getting the block number of the most /// recent change that occurred relative to the given block number. +/// +/// ## Arguments +/// +/// * `block_list`: A list of block numbers where a change in value occur. fn recent_change_from_block( block_number: BlockNumber, block_list: &BlockList, diff --git a/crates/katana/storage/provider/src/providers/db/trie.rs b/crates/katana/storage/provider/src/providers/db/trie.rs index 65b5b96984..cb98362d4c 100644 --- a/crates/katana/storage/provider/src/providers/db/trie.rs +++ b/crates/katana/storage/provider/src/providers/db/trie.rs @@ -2,17 +2,18 @@ use std::collections::{BTreeMap, HashMap}; use std::fmt::Debug; use katana_db::abstraction::Database; -use katana_db::trie; -use katana_db::trie::{ContractTrie, StorageTrie}; +use katana_db::tables; +use katana_db::trie::TrieDbMut; use katana_primitives::block::BlockNumber; use katana_primitives::class::{ClassHash, CompiledClassHash}; use katana_primitives::state::StateUpdates; use katana_primitives::{ContractAddress, Felt}; -use katana_trie::compute_contract_state_hash; +use katana_trie::{compute_contract_state_hash, ClassesTrie, ContractsTrie, StoragesTrie}; use crate::providers::db::DbProvider; use crate::traits::state::{StateFactoryProvider, StateProvider}; -use crate::traits::trie::{ClassTrieWriter, ContractTrieWriter}; +use crate::traits::trie::TrieWriter; +use crate::ProviderResult; #[derive(Debug, Default)] struct ContractLeaf { @@ -21,80 +22,84 @@ struct ContractLeaf { pub nonce: Option, } -impl ClassTrieWriter for DbProvider { - fn insert_updates( +impl TrieWriter for DbProvider { + fn trie_insert_declared_classes( &self, block_number: BlockNumber, updates: &BTreeMap, - ) -> crate::ProviderResult { - let mut trie = trie::ClassTrie::new(self.0.tx_mut()?); + ) -> ProviderResult { + self.0.update(|tx| { + let mut trie = ClassesTrie::new(TrieDbMut::::new(tx)); - for (class_hash, compiled_hash) in updates { - trie.insert(*class_hash, *compiled_hash); - } + for (class_hash, compiled_hash) in updates { + trie.insert(*class_hash, *compiled_hash); + } - trie.commit(block_number); - Ok(trie.root()) + trie.commit(block_number); + Ok(trie.root()) + })? } -} -impl ContractTrieWriter for DbProvider { - fn insert_updates( + fn trie_insert_contract_updates( &self, block_number: BlockNumber, state_updates: &StateUpdates, - ) -> crate::ProviderResult { - let mut contract_leafs: HashMap = HashMap::new(); - - let leaf_hashes: Vec<_> = { - let mut storage_trie_db = StorageTrie::new(self.0.tx_mut()?); - - // First we insert the contract storage changes - for (address, storage_entries) in &state_updates.storage_updates { - for (key, value) in storage_entries { - storage_trie_db.insert(*address, *key, *value); + ) -> ProviderResult { + self.0.update(|tx| { + let mut contract_trie_db = + ContractsTrie::new(TrieDbMut::::new(tx)); + let mut storage_trie_db = + StoragesTrie::new(TrieDbMut::::new(tx)); + + let mut contract_leafs: HashMap = HashMap::new(); + + let leaf_hashes: Vec<_> = { + // First we insert the contract storage changes + for (address, storage_entries) in &state_updates.storage_updates { + for (key, value) in storage_entries { + storage_trie_db.insert(*address, *key, *value); + } + // insert the contract address in the contract_leafs to put the storage root + // later + contract_leafs.insert(*address, Default::default()); } - // insert the contract address in the contract_leafs to put the storage root later - contract_leafs.insert(*address, Default::default()); - } - - // Then we commit them - storage_trie_db.commit(block_number); - for (address, nonce) in &state_updates.nonce_updates { - contract_leafs.entry(*address).or_default().nonce = Some(*nonce); - } + // Then we commit them + storage_trie_db.commit(block_number); - for (address, class_hash) in &state_updates.deployed_contracts { - contract_leafs.entry(*address).or_default().class_hash = Some(*class_hash); - } + for (address, nonce) in &state_updates.nonce_updates { + contract_leafs.entry(*address).or_default().nonce = Some(*nonce); + } - for (address, class_hash) in &state_updates.replaced_classes { - contract_leafs.entry(*address).or_default().class_hash = Some(*class_hash); - } + for (address, class_hash) in &state_updates.deployed_contracts { + contract_leafs.entry(*address).or_default().class_hash = Some(*class_hash); + } - contract_leafs - .into_iter() - .map(|(address, mut leaf)| { - let storage_root = storage_trie_db.root(&address); - leaf.storage_root = Some(storage_root); + for (address, class_hash) in &state_updates.replaced_classes { + contract_leafs.entry(*address).or_default().class_hash = Some(*class_hash); + } - let latest_state = self.latest().unwrap(); - let leaf_hash = contract_state_leaf_hash(latest_state, &address, &leaf); + contract_leafs + .into_iter() + .map(|(address, mut leaf)| { + let storage_root = storage_trie_db.root(address); + leaf.storage_root = Some(storage_root); - (address, leaf_hash) - }) - .collect::>() - }; + let latest_state = self.latest().unwrap(); + let leaf_hash = contract_state_leaf_hash(latest_state, &address, &leaf); - let mut contract_trie_db = ContractTrie::new(self.0.tx_mut()?); + (address, leaf_hash) + }) + .collect::>() + }; - for (k, v) in leaf_hashes { - contract_trie_db.insert(k, v); - } + for (k, v) in leaf_hashes { + contract_trie_db.insert(k, v); + } - contract_trie_db.commit(block_number); - Ok(contract_trie_db.root()) + contract_trie_db.commit(block_number); + Ok(contract_trie_db.root()) + })? } } diff --git a/crates/katana/storage/provider/src/providers/fork/backend.rs b/crates/katana/storage/provider/src/providers/fork/backend.rs index 2abb7f5a88..6e0c588869 100644 --- a/crates/katana/storage/provider/src/providers/fork/backend.rs +++ b/crates/katana/storage/provider/src/providers/fork/backend.rs @@ -28,7 +28,7 @@ use tracing::{error, trace}; use crate::error::ProviderError; use crate::providers::in_memory::cache::CacheStateDb; use crate::traits::contract::ContractClassProvider; -use crate::traits::state::StateProvider; +use crate::traits::state::{StateProofProvider, StateProvider, StateRootProvider}; use crate::ProviderResult; const LOG_TARGET: &str = "forking::backend"; @@ -662,6 +662,41 @@ impl ContractClassProvider for SharedStateProvider { } } +impl StateProofProvider for SharedStateProvider { + fn class_multiproof(&self, classes: Vec) -> ProviderResult { + let _ = classes; + unimplemented!("not supported in forked mode") + } + + fn contract_multiproof( + &self, + addresses: Vec, + ) -> ProviderResult { + let _ = addresses; + unimplemented!("not supported in forked mode") + } + + fn storage_multiproof( + &self, + address: ContractAddress, + key: Vec, + ) -> ProviderResult { + let _ = address; + let _ = key; + unimplemented!("not supported in forked mode") + } +} + +impl StateRootProvider for SharedStateProvider { + fn classes_root(&self) -> ProviderResult { + unimplemented!("not supported in forked mode") + } + + fn contracts_root(&self) -> ProviderResult { + unimplemented!("not supported in forked mode") + } +} + /// A helper function to convert a contract/class not found error returned by the RPC provider into /// a `Option::None`. /// diff --git a/crates/katana/storage/provider/src/providers/fork/mod.rs b/crates/katana/storage/provider/src/providers/fork/mod.rs index 922b1ada91..09342cc860 100644 --- a/crates/katana/storage/provider/src/providers/fork/mod.rs +++ b/crates/katana/storage/provider/src/providers/fork/mod.rs @@ -33,13 +33,13 @@ use crate::traits::block::{ use crate::traits::contract::{ContractClassWriter, ContractClassWriterExt}; use crate::traits::env::BlockEnvProvider; use crate::traits::stage::StageCheckpointProvider; -use crate::traits::state::{StateFactoryProvider, StateProvider, StateRootProvider, StateWriter}; +use crate::traits::state::{StateFactoryProvider, StateProvider, StateWriter}; use crate::traits::state_update::StateUpdateProvider; use crate::traits::transaction::{ ReceiptProvider, TransactionProvider, TransactionStatusProvider, TransactionTraceProvider, TransactionsProviderExt, }; -use crate::traits::trie::{ClassTrieWriter, ContractTrieWriter}; +use crate::traits::trie::TrieWriter; use crate::ProviderResult; #[derive(Debug)] @@ -410,17 +410,17 @@ impl ReceiptProvider for ForkedProvider { } } -impl StateRootProvider for ForkedProvider { - fn state_root( - &self, - block_id: BlockHashOrNumber, - ) -> ProviderResult> { - let state_root = self.block_number_by_id(block_id)?.and_then(|num| { - self.storage.read().block_headers.get(&num).map(|header| header.state_root) - }); - Ok(state_root) - } -} +// impl StateRootProvider for ForkedProvider { +// fn state_root( +// &self, +// block_id: BlockHashOrNumber, +// ) -> ProviderResult> { +// let state_root = self.block_number_by_id(block_id)?.and_then(|num| { +// self.storage.read().block_headers.get(&num).map(|header| header.state_root) +// }); +// Ok(state_root) +// } +// } impl StateUpdateProvider for ForkedProvider { fn state_update(&self, block_id: BlockHashOrNumber) -> ProviderResult> { @@ -599,8 +599,8 @@ impl BlockEnvProvider for ForkedProvider { } } -impl ClassTrieWriter for ForkedProvider { - fn insert_updates( +impl TrieWriter for ForkedProvider { + fn trie_insert_declared_classes( &self, block_number: BlockNumber, updates: &BTreeMap, @@ -609,10 +609,8 @@ impl ClassTrieWriter for ForkedProvider { let _ = updates; Ok(Felt::ZERO) } -} -impl ContractTrieWriter for ForkedProvider { - fn insert_updates( + fn trie_insert_contract_updates( &self, block_number: BlockNumber, state_updates: &StateUpdates, diff --git a/crates/katana/storage/provider/src/providers/fork/state.rs b/crates/katana/storage/provider/src/providers/fork/state.rs index b1377743cc..3c6fe5debd 100644 --- a/crates/katana/storage/provider/src/providers/fork/state.rs +++ b/crates/katana/storage/provider/src/providers/fork/state.rs @@ -7,7 +7,7 @@ use super::backend::SharedStateProvider; use crate::providers::in_memory::cache::CacheStateDb; use crate::providers::in_memory::state::StateSnapshot; use crate::traits::contract::ContractClassProvider; -use crate::traits::state::StateProvider; +use crate::traits::state::{StateProofProvider, StateProvider, StateRootProvider}; use crate::ProviderResult; pub type ForkedStateDb = CacheStateDb; @@ -76,7 +76,7 @@ impl StateProvider for ForkedStateDb { } } -impl ContractClassProvider for CacheStateDb { +impl ContractClassProvider for ForkedStateDb { fn class(&self, hash: ClassHash) -> ProviderResult> { if let class @ Some(_) = self.shared_contract_classes.classes.read().get(&hash) { return Ok(class.cloned()); @@ -102,6 +102,37 @@ impl ContractClassProvider for CacheStateDb { } } +impl StateProofProvider for ForkedStateDb { + fn class_multiproof(&self, _: Vec) -> ProviderResult { + Ok(katana_trie::MultiProof(Default::default())) + } + + fn contract_multiproof( + &self, + _: Vec, + ) -> ProviderResult { + Ok(katana_trie::MultiProof(Default::default())) + } + + fn storage_multiproof( + &self, + _: ContractAddress, + _: Vec, + ) -> ProviderResult { + Ok(katana_trie::MultiProof(Default::default())) + } +} + +impl StateRootProvider for ForkedStateDb { + fn classes_root(&self) -> ProviderResult { + Ok(katana_primitives::Felt::ZERO) + } + + fn contracts_root(&self) -> ProviderResult { + Ok(katana_primitives::Felt::ZERO) + } +} + #[derive(Debug)] pub(super) struct LatestStateProvider(pub(super) Arc); @@ -143,6 +174,37 @@ impl ContractClassProvider for LatestStateProvider { } } +impl StateProofProvider for LatestStateProvider { + fn class_multiproof(&self, _: Vec) -> ProviderResult { + Ok(katana_trie::MultiProof(Default::default())) + } + + fn contract_multiproof( + &self, + _: Vec, + ) -> ProviderResult { + Ok(katana_trie::MultiProof(Default::default())) + } + + fn storage_multiproof( + &self, + _: ContractAddress, + _: Vec, + ) -> ProviderResult { + Ok(katana_trie::MultiProof(Default::default())) + } +} + +impl StateRootProvider for LatestStateProvider { + fn classes_root(&self) -> ProviderResult { + Ok(katana_primitives::Felt::ZERO) + } + + fn contracts_root(&self) -> ProviderResult { + Ok(katana_primitives::Felt::ZERO) + } +} + impl StateProvider for ForkedSnapshot { fn nonce(&self, address: ContractAddress) -> ProviderResult> { if let nonce @ Some(_) = self @@ -215,6 +277,37 @@ impl ContractClassProvider for ForkedSnapshot { } } +impl StateProofProvider for ForkedSnapshot { + fn class_multiproof(&self, _: Vec) -> ProviderResult { + Ok(katana_trie::MultiProof(Default::default())) + } + + fn contract_multiproof( + &self, + _: Vec, + ) -> ProviderResult { + Ok(katana_trie::MultiProof(Default::default())) + } + + fn storage_multiproof( + &self, + _: ContractAddress, + _: Vec, + ) -> ProviderResult { + Ok(katana_trie::MultiProof(Default::default())) + } +} + +impl StateRootProvider for ForkedSnapshot { + fn classes_root(&self) -> ProviderResult { + Ok(katana_primitives::Felt::ZERO) + } + + fn contracts_root(&self) -> ProviderResult { + Ok(katana_primitives::Felt::ZERO) + } +} + #[cfg(test)] mod tests { use std::collections::BTreeMap; diff --git a/crates/katana/storage/provider/src/traits/state.rs b/crates/katana/storage/provider/src/traits/state.rs index 38046c2b5b..2059933bf4 100644 --- a/crates/katana/storage/provider/src/traits/state.rs +++ b/crates/katana/storage/provider/src/traits/state.rs @@ -2,18 +2,35 @@ use katana_primitives::block::BlockHashOrNumber; use katana_primitives::class::ClassHash; use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageValue}; use katana_primitives::Felt; +use katana_trie::MultiProof; +use starknet::macros::short_string; +use starknet_types_core::hash::StarkHash; use super::contract::ContractClassProvider; use crate::ProviderResult; #[auto_impl::auto_impl(&, Box, Arc)] pub trait StateRootProvider: Send + Sync { - /// Retrieves the state root of a block. - fn state_root(&self, block_id: BlockHashOrNumber) -> ProviderResult>; + /// Retrieves the root of the global state trie. + fn state_root(&self) -> ProviderResult { + Ok(starknet_types_core::hash::Poseidon::hash_array(&[ + short_string!("STARKNET_STATE_V0"), + self.contracts_root()?, + self.classes_root()?, + ])) + } + + /// Retrieves the root of the classes trie. + fn classes_root(&self) -> ProviderResult; + + /// Retrieves the root of the contracts trie. + fn contracts_root(&self) -> ProviderResult; } #[auto_impl::auto_impl(&, Box, Arc)] -pub trait StateProvider: ContractClassProvider + Send + Sync + std::fmt::Debug { +pub trait StateProvider: + ContractClassProvider + StateProofProvider + StateRootProvider + Send + Sync + std::fmt::Debug +{ /// Returns the nonce of a contract. fn nonce(&self, address: ContractAddress) -> ProviderResult>; @@ -63,3 +80,16 @@ pub trait StateWriter: Send + Sync { class_hash: ClassHash, ) -> ProviderResult<()>; } + +#[auto_impl::auto_impl(&, Box, Arc)] +pub trait StateProofProvider: Send + Sync { + fn storage_multiproof( + &self, + address: ContractAddress, + key: Vec, + ) -> ProviderResult; + + fn contract_multiproof(&self, addresses: Vec) -> ProviderResult; + + fn class_multiproof(&self, classes: Vec) -> ProviderResult; +} diff --git a/crates/katana/storage/provider/src/traits/trie.rs b/crates/katana/storage/provider/src/traits/trie.rs index 8570a30b88..6a63ec3c40 100644 --- a/crates/katana/storage/provider/src/traits/trie.rs +++ b/crates/katana/storage/provider/src/traits/trie.rs @@ -8,17 +8,14 @@ use katana_primitives::Felt; use crate::ProviderResult; #[auto_impl::auto_impl(&, Box, Arc)] -pub trait ClassTrieWriter: Send + Sync { - fn insert_updates( +pub trait TrieWriter: Send + Sync { + fn trie_insert_declared_classes( &self, block_number: BlockNumber, updates: &BTreeMap, ) -> ProviderResult; -} -#[auto_impl::auto_impl(&, Box, Arc)] -pub trait ContractTrieWriter: Send + Sync { - fn insert_updates( + fn trie_insert_contract_updates( &self, block_number: BlockNumber, state_updates: &StateUpdates, diff --git a/crates/katana/storage/provider/tests/block.rs b/crates/katana/storage/provider/tests/block.rs index 3b6f686804..1be22a5e92 100644 --- a/crates/katana/storage/provider/tests/block.rs +++ b/crates/katana/storage/provider/tests/block.rs @@ -11,7 +11,7 @@ use katana_provider::traits::block::{ BlockHashProvider, BlockNumberProvider, BlockProvider, BlockStatusProvider, BlockWriter, }; use katana_provider::traits::env::BlockEnvProvider; -use katana_provider::traits::state::StateRootProvider; +use katana_provider::traits::state::{StateFactoryProvider, StateRootProvider}; use katana_provider::traits::state_update::StateUpdateProvider; use katana_provider::traits::transaction::{ ReceiptProvider, TransactionProvider, TransactionStatusProvider, TransactionTraceProvider, @@ -29,6 +29,7 @@ use fixtures::{ use katana_primitives::Felt; #[apply(insert_block_cases)] +#[ignore = "trie computation not supported yet for forked mode yet"] fn insert_block_with_fork_provider( #[from(fork_provider)] provider: BlockchainProvider, #[case] block_count: u64, @@ -45,6 +46,7 @@ fn insert_block_with_db_provider( } #[apply(insert_block_cases)] +#[ignore = "trie computation not supported yet for forked mode yet"] fn insert_block_empty_with_fork_provider( #[from(fork_provider)] provider: BlockchainProvider, #[case] block_count: u64, @@ -65,7 +67,7 @@ where Db: BlockProvider + BlockWriter + ReceiptProvider - + StateRootProvider + + StateFactoryProvider + TransactionStatusProvider + TransactionTraceProvider + BlockEnvProvider, @@ -119,7 +121,8 @@ where let actual_block = provider.block(block_id)?; let actual_block_txs = provider.transactions_by_block(block_id)?; let actual_status = provider.block_status(block_id)?; - let actual_state_root = provider.state_root(block_id)?; + let actual_state_root = + provider.historical(block_id)?.map(|s| s.state_root()).transpose()?; let actual_block_tx_count = provider.transaction_count_by_block(block_id)?; let actual_receipts = provider.receipts_by_block(block_id)?; @@ -174,7 +177,7 @@ where Db: BlockProvider + BlockWriter + ReceiptProvider - + StateRootProvider + + StateFactoryProvider + TransactionStatusProvider + TransactionTraceProvider + BlockEnvProvider, @@ -225,7 +228,8 @@ where let actual_block = provider.block(block_id)?; let actual_block_txs = provider.transactions_by_block(block_id)?; let actual_status = provider.block_status(block_id)?; - let actual_state_root = provider.state_root(block_id)?; + let actual_state_root = + provider.historical(block_id)?.map(|s| s.state_root()).transpose()?; let actual_block_tx_count = provider.transaction_count_by_block(block_id)?; let actual_receipts = provider.receipts_by_block(block_id)?; diff --git a/crates/katana/trie/Cargo.toml b/crates/katana/trie/Cargo.toml index 759dab5347..871009cf7f 100644 --- a/crates/katana/trie/Cargo.toml +++ b/crates/katana/trie/Cargo.toml @@ -17,7 +17,7 @@ starknet-types-core.workspace = true thiserror.workspace = true [dependencies.bonsai-trie] +branch = "kariy/public-path" default-features = false features = [ "std" ] -git = "https://github.com/madara-alliance/bonsai-trie/" -rev = "56d7d62" +git = "https://github.com/dojoengine/bonsai-trie/" diff --git a/crates/katana/trie/src/classes.rs b/crates/katana/trie/src/classes.rs new file mode 100644 index 0000000000..f996445ed0 --- /dev/null +++ b/crates/katana/trie/src/classes.rs @@ -0,0 +1,45 @@ +use bonsai_trie::{BonsaiDatabase, BonsaiPersistentDatabase, MultiProof}; +use katana_primitives::block::BlockNumber; +use katana_primitives::class::{ClassHash, CompiledClassHash}; +use katana_primitives::Felt; +use starknet::macros::short_string; +use starknet_types_core::hash::{Poseidon, StarkHash}; + +use crate::id::CommitId; + +#[derive(Debug)] +pub struct ClassesTrie { + trie: crate::BonsaiTrie, +} + +impl ClassesTrie { + const BONSAI_IDENTIFIER: &'static [u8] = b"classes"; + + pub fn new(db: DB) -> Self { + Self { trie: crate::BonsaiTrie::new(db) } + } + + pub fn root(&self) -> Felt { + self.trie.root(Self::BONSAI_IDENTIFIER) + } + + pub fn multiproof(&mut self, class_hashes: Vec) -> MultiProof { + self.trie.multiproof(Self::BONSAI_IDENTIFIER, class_hashes) + } +} + +impl ClassesTrie +where + DB: BonsaiDatabase + BonsaiPersistentDatabase, +{ + pub fn insert(&mut self, hash: ClassHash, compiled_hash: CompiledClassHash) { + // https://docs.starknet.io/architecture-and-concepts/network-architecture/starknet-state/#classes_trie + const CONTRACT_CLASS_LEAF_V0: Felt = short_string!("CONTRACT_CLASS_LEAF_V0"); + let value = Poseidon::hash(&CONTRACT_CLASS_LEAF_V0, &compiled_hash); + self.trie.insert(Self::BONSAI_IDENTIFIER, hash, value) + } + + pub fn commit(&mut self, block: BlockNumber) { + self.trie.commit(block.into()) + } +} diff --git a/crates/katana/trie/src/contracts.rs b/crates/katana/trie/src/contracts.rs new file mode 100644 index 0000000000..3576a89916 --- /dev/null +++ b/crates/katana/trie/src/contracts.rs @@ -0,0 +1,43 @@ +use bonsai_trie::{BonsaiDatabase, BonsaiPersistentDatabase, MultiProof}; +use katana_primitives::block::BlockNumber; +use katana_primitives::{ContractAddress, Felt}; + +use crate::id::CommitId; + +#[derive(Debug)] +pub struct ContractsTrie { + trie: crate::BonsaiTrie, +} + +impl ContractsTrie { + /// NOTE: The identifier value is only relevant if the underlying [`BonsaiDatabase`] + /// implementation is shared across other tries. + const BONSAI_IDENTIFIER: &'static [u8] = b"contracts"; + + pub fn new(db: DB) -> Self { + Self { trie: crate::BonsaiTrie::new(db) } + } + + pub fn root(&self) -> Felt { + self.trie.root(Self::BONSAI_IDENTIFIER) + } + + pub fn multiproof(&mut self, addresses: Vec) -> MultiProof { + let keys = addresses.into_iter().map(Felt::from).collect::>(); + dbg!(&keys); + self.trie.multiproof(Self::BONSAI_IDENTIFIER, keys) + } +} + +impl ContractsTrie +where + DB: BonsaiDatabase + BonsaiPersistentDatabase, +{ + pub fn insert(&mut self, address: ContractAddress, state_hash: Felt) { + self.trie.insert(Self::BONSAI_IDENTIFIER, *address, state_hash) + } + + pub fn commit(&mut self, block: BlockNumber) { + self.trie.commit(block.into()) + } +} diff --git a/crates/katana/trie/src/id.rs b/crates/katana/trie/src/id.rs new file mode 100644 index 0000000000..d7a81aa8c1 --- /dev/null +++ b/crates/katana/trie/src/id.rs @@ -0,0 +1,38 @@ +use bonsai_trie::id::Id; +use bonsai_trie::ByteVec; +use katana_primitives::block::BlockNumber; + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Default)] +pub struct CommitId(BlockNumber); + +impl CommitId { + pub fn new(block_number: BlockNumber) -> Self { + Self(block_number) + } +} + +impl Id for CommitId { + fn as_u64(self) -> u64 { + self.0 + } + + fn from_u64(v: u64) -> Self { + Self(v) + } + + fn to_bytes(&self) -> ByteVec { + ByteVec::from(&self.0.to_be_bytes() as &[_]) + } +} + +impl From for CommitId { + fn from(value: BlockNumber) -> Self { + Self(value) + } +} + +impl From for BlockNumber { + fn from(value: CommitId) -> Self { + value.0 + } +} diff --git a/crates/katana/trie/src/lib.rs b/crates/katana/trie/src/lib.rs index 1a3e858bd6..49e34ef26d 100644 --- a/crates/katana/trie/src/lib.rs +++ b/crates/katana/trie/src/lib.rs @@ -1,19 +1,79 @@ -use anyhow::Result; +use bitvec::array::BitArray; +use bitvec::order::Msb0; use bitvec::vec::BitVec; -pub use bonsai_trie as bonsai; -use bonsai_trie::id::BasicId; -use bonsai_trie::{BonsaiDatabase, BonsaiPersistentDatabase}; +use bitvec::view::AsBits; +pub use bonsai::{MultiProof, Path, ProofNode}; +pub use bonsai_trie::{BonsaiDatabase, BonsaiPersistentDatabase}; +use bonsai_trie::{BonsaiStorage, BonsaiStorageConfig}; use katana_primitives::class::ClassHash; use katana_primitives::Felt; -use starknet_types_core::hash::{Pedersen, StarkHash}; +use starknet_types_core::hash::{Pedersen, Poseidon, StarkHash}; +pub use {bitvec, bonsai_trie as bonsai}; -/// A helper trait to define a database that can be used as a Bonsai Trie. +mod classes; +mod contracts; +mod id; +mod storages; + +pub use classes::ClassesTrie; +pub use contracts::ContractsTrie; +pub use id::CommitId; +pub use storages::StoragesTrie; + +/// A lightweight shim for [`BonsaiStorage`]. /// -/// Basically a short hand for `BonsaiDatabase + BonsaiPersistentDatabase`. -pub trait BonsaiTrieDb: BonsaiDatabase + BonsaiPersistentDatabase {} -impl BonsaiTrieDb for T where T: BonsaiDatabase + BonsaiPersistentDatabase {} +/// This abstract the Bonsai Trie operations - providing a simplified interface without +/// having to handle how to transform the keys into the internal keys used by the trie. +/// This struct is not meant to be used directly, and instead use the specific tries that have +/// been derived from it, [`ClassesTrie`], [`ContractsTrie`], or [`StoragesTrie`]. +#[derive(Debug)] +pub(crate) struct BonsaiTrie { + storage: BonsaiStorage, +} + +impl BonsaiTrie { + pub fn new(db: DB) -> Self { + let config = BonsaiStorageConfig { + max_saved_trie_logs: Some(usize::MAX), + max_saved_snapshots: Some(usize::MAX), + snapshot_interval: 1, + }; + + Self { storage: BonsaiStorage::new(db, config, 251) } + } +} + +impl BonsaiTrie { + pub fn root(&self, id: &[u8]) -> Felt { + self.storage.root_hash(id).expect("failed to get trie root") + } + + pub fn multiproof(&mut self, id: &[u8], mut keys: Vec) -> MultiProof { + keys.sort(); + let keys = keys + .into_iter() + .map(|key| BitArray::new(key.to_bytes_be())) + .map(|hash| hash.as_bitslice()[5..].to_owned()); + + self.storage.get_multi_proof(id, keys).expect("failed to get multiproof") + } +} + +impl BonsaiTrie +where + DB: BonsaiDatabase + BonsaiPersistentDatabase, +{ + pub fn insert(&mut self, id: &[u8], key: Felt, value: Felt) { + let key: BitVec = key.to_bytes_be().as_bits()[5..].to_owned(); + self.storage.insert(id, &key, &value).unwrap(); + } + + pub fn commit(&mut self, id: CommitId) { + self.storage.commit(id).expect("failed to commit trie"); + } +} -pub fn compute_merkle_root(values: &[Felt]) -> Result +pub fn compute_merkle_root(values: &[Felt]) -> anyhow::Result where H: StarkHash + Send + Sync, { @@ -25,7 +85,7 @@ where let config = BonsaiStorageConfig::default(); let bonsai_db = databases::HashMapDb::::default(); - let mut bs = BonsaiStorage::<_, _, H>::new(bonsai_db, config).unwrap(); + let mut bs = BonsaiStorage::<_, _, H>::new(bonsai_db, config, 64); for (id, value) in values.iter().enumerate() { let key = BitVec::from_iter(id.to_be_bytes()); diff --git a/crates/katana/trie/src/storages.rs b/crates/katana/trie/src/storages.rs new file mode 100644 index 0000000000..20231ba369 --- /dev/null +++ b/crates/katana/trie/src/storages.rs @@ -0,0 +1,47 @@ +use bonsai_trie::{BonsaiDatabase, BonsaiPersistentDatabase, MultiProof}; +use katana_primitives::block::BlockNumber; +use katana_primitives::contract::{StorageKey, StorageValue}; +use katana_primitives::{ContractAddress, Felt}; + +use crate::id::CommitId; + +#[derive(Debug)] +pub struct StoragesTrie { + trie: crate::BonsaiTrie, +} + +impl StoragesTrie { + pub fn new(db: DB) -> Self { + Self { trie: crate::BonsaiTrie::new(db) } + } + + pub fn root(&self, address: ContractAddress) -> Felt { + self.trie.root(&address.to_bytes_be()) + } + + pub fn multiproof( + &mut self, + address: ContractAddress, + storage_keys: Vec, + ) -> MultiProof { + self.trie.multiproof(&address.to_bytes_be(), storage_keys) + } +} + +impl StoragesTrie +where + DB: BonsaiDatabase + BonsaiPersistentDatabase, +{ + pub fn insert( + &mut self, + address: ContractAddress, + storage_key: StorageKey, + storage_value: StorageValue, + ) { + self.trie.insert(&address.to_bytes_be(), storage_key, storage_value) + } + + pub fn commit(&mut self, block: BlockNumber) { + self.trie.commit(block.into()) + } +} diff --git a/crates/saya/core/src/blockchain/mod.rs b/crates/saya/core/src/blockchain/mod.rs index f019b4b617..206f8c181c 100644 --- a/crates/saya/core/src/blockchain/mod.rs +++ b/crates/saya/core/src/blockchain/mod.rs @@ -6,9 +6,7 @@ use katana_provider::providers::db::DbProvider; use katana_provider::traits::block::{BlockProvider, BlockWriter}; use katana_provider::traits::contract::ContractClassWriter; use katana_provider::traits::env::BlockEnvProvider; -use katana_provider::traits::state::{ - StateFactoryProvider, StateProvider, StateRootProvider, StateWriter, -}; +use katana_provider::traits::state::{StateFactoryProvider, StateProvider, StateWriter}; use katana_provider::traits::state_update::StateUpdateProvider; use katana_provider::traits::transaction::{ ReceiptProvider, TransactionProvider, TransactionStatusProvider, TransactionsProviderExt, @@ -25,7 +23,6 @@ pub trait Database: + TransactionsProviderExt + ReceiptProvider + StateUpdateProvider - + StateRootProvider + StateWriter + ContractClassWriter + StateFactoryProvider @@ -44,7 +41,6 @@ impl Database for T where + TransactionsProviderExt + ReceiptProvider + StateUpdateProvider - + StateRootProvider + StateWriter + ContractClassWriter + StateFactoryProvider diff --git a/spawn-and-move-db.tar.gz b/spawn-and-move-db.tar.gz index e8c46c8aa704ffd73b855735d7677de2f2eb64e0..8f02dc10d4dd83ff544705d45153a610d62e556b 100644 GIT binary patch literal 1791575 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