diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index db18bf8e5bc..ca65b27701c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,4 +5,7 @@ crates/fuel-gas-price-algorithm @xgreenx @Dentosal @MitchTurner @rafal-ch # Code owners for the transaction pool -crates/services/txpool_v2 @xgreenx @Dentosal @MitchTurner @AurelienFT \ No newline at end of file +crates/services/txpool_v2 @xgreenx @Dentosal @MitchTurner @AurelienFT + +# Code owners for the gas price service (v0 & v1) +crates/services/gas_price_service @xgreenx @Dentosal @MitchTurner @rymnc \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4daab0418df..a4630e6e4a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,9 +27,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - [2361](https://github.com/FuelLabs/fuel-core/pull/2361): Add caches to the sync service to not reask for data it already fetched from the network. - [2327](https://github.com/FuelLabs/fuel-core/pull/2327): Add more services tests and more checks of the pool. Also add an high level documentation for users of the pool and contributors. - [2416](https://github.com/FuelLabs/fuel-core/issues/2416): Define the `GasPriceServiceV1` task. +- [1922](https://github.com/FuelLabs/fuel-core/pull/1922): Added support for posting blocks to the shared sequencer. - [2033](https://github.com/FuelLabs/fuel-core/pull/2033): Remove `Option` in favor of `BlockHeightQuery` where applicable. -- [2150](https://github.com/FuelLabs/fuel-core/pull/2150): Upgraded `libp2p` to `0.54.1` and introduced `max_pending_incoming_connections` , `max_established_connections` and `max_pending_outgoing_connections` to limit the number of pending connections. +- [2439](https://github.com/FuelLabs/fuel-core/pull/2439): Add gas costs for the two new zk opcodes `ecop` and `eadd` and the benches that allow to calibrate them. - [2472](https://github.com/FuelLabs/fuel-core/pull/2472): Added the `amountU128` field to the `Balance` GraphQL schema, providing the total balance as a `U128`. The existing `amount` field clamps any balance exceeding `U64` to `u64::MAX`. +- [2526](https://github.com/FuelLabs/fuel-core/pull/2526): Add possibility to not have any cache set for RocksDB. Add an option to either load the RocksDB columns families on creation of the database or when the column is used. +- [2532](https://github.com/FuelLabs/fuel-core/pull/2532): Getters for inner rocksdb database handles. +- [2150](https://github.com/FuelLabs/fuel-core/pull/2150): Upgraded `libp2p` to `0.54.1` and introduced `ConnectionLimiter` to limit pending incoming/outgoing connections. ### Fixed - [2365](https://github.com/FuelLabs/fuel-core/pull/2365): Fixed the error during dry run in the case of race condition. @@ -37,20 +41,41 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - [2369](https://github.com/FuelLabs/fuel-core/pull/2369): The `transaction_insertion_time_in_thread_pool_milliseconds` metric is properly collected. - [2413](https://github.com/FuelLabs/fuel-core/issues/2413): block production immediately errors if unable to lock the mutex. - [2389](https://github.com/FuelLabs/fuel-core/pull/2389): Fix construction of reverse iterator in RocksDB. +- [2479](https://github.com/FuelLabs/fuel-core/pull/2479): Fix an error on the last iteration of the read and write sequential opcodes on contract storage. +- [2478](https://github.com/FuelLabs/fuel-core/pull/2478): Fix proof created by `message_receipts_proof` function by ignoring the receipts from failed transactions to match `message_outbox_root`. - [2485](https://github.com/FuelLabs/fuel-core/pull/2485): Hardcode the timestamp of the genesis block and version of `tai64` to avoid breaking changes for us. +- [2511](https://github.com/FuelLabs/fuel-core/pull/2511): Fix backward compatibility of V0Metadata in gas price db. ### Changed +- [2501](https://github.com/FuelLabs/fuel-core/pull/2501): Use gas price from block for estimating future gas prices +- [2468](https://github.com/FuelLabs/fuel-core/pull/2468): Abstract unrecorded blocks concept for V1 algorithm, create new storage impl. Introduce `TransactionableStorage` trait to allow atomic changes to the storage. - [2295](https://github.com/FuelLabs/fuel-core/pull/2295): `CombinedDb::from_config` now respects `state_rewind_policy` with tmp RocksDB. - [2378](https://github.com/FuelLabs/fuel-core/pull/2378): Use cached hash of the topic instead of calculating it on each publishing gossip message. +- [2438](https://github.com/FuelLabs/fuel-core/pull/2438): Refactored service to use new implementation of `StorageRead::read` that takes an offset in input. - [2429](https://github.com/FuelLabs/fuel-core/pull/2429): Introduce custom enum for representing result of running service tasks - [2377](https://github.com/FuelLabs/fuel-core/pull/2377): Add more errors that can be returned as responses when using protocol `/fuel/req_res/0.0.2`. The errors supported are `ProtocolV1EmptyResponse` (status code `0`) for converting empty responses sent via protocol `/fuel/req_res/0.0.1`, `RequestedRangeTooLarge`(status code `1`) if the client requests a range of objects such as sealed block headers or transactions too large, `Timeout` (status code `2`) if the remote peer takes too long to fulfill a request, or `SyncProcessorOutOfCapacity` if the remote peer is fulfilling too many requests concurrently. - [2233](https://github.com/FuelLabs/fuel-core/pull/2233): Introduce a new column `modification_history_v2` for storing the modification history in the historical rocksDB. Keys in this column are stored in big endian order. Changed the behaviour of the historical rocksDB to write changes for new block heights to the new column, and to perform lookup of values from the `modification_history_v2` table first, and then from the `modification_history` table, performing a migration upon access if necessary. #### Breaking +- [2438](https://github.com/FuelLabs/fuel-core/pull/2438): The `fuel-core-client` can only work with new version of the `fuel-core`. The `0.40` and all older versions are not supported. +- [2438](https://github.com/FuelLabs/fuel-core/pull/2438): Updated `fuel-vm` to `0.59.1` release. Check [release notes](https://github.com/FuelLabs/fuel-vm/releases/tag/v0.59.0) for more details. - [2389](https://github.com/FuelLabs/fuel-core/pull/2258): Updated the `messageProof` GraphQL schema to return a non-nullable `MessageProof`. - [2154](https://github.com/FuelLabs/fuel-core/pull/2154): Transaction graphql endpoints use `TransactionType` instead of `fuel_tx::Transaction`. - [2446](https://github.com/FuelLabs/fuel-core/pull/2446): Use graphiql instead of graphql-playground due to known vulnerability and stale development. - [2379](https://github.com/FuelLabs/fuel-core/issues/2379): Change `kv_store::Value` to be `Arc<[u8]>` instead of `Arc>`. +- [2526](https://github.com/FuelLabs/fuel-core/pull/2526): By default the cache of RocksDB is now disabled instead of being `1024 * 1024 * 1024`. + +## [Version 0.40.2] + +### Fixed + +- [2476](https://github.com/FuelLabs/fuel-core/pull/2476): Hardcode the timestamp of the genesis block. + +## [Version 0.40.1] + +### Added + +- [2450](https://github.com/FuelLabs/fuel-core/pull/2450): Added support for posting blocks to the shared sequencer. ## [Version 0.40.0] @@ -466,7 +491,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). The change has many minor improvements in different areas related to the state transition bytecode: - The state transition bytecode lies in its own file(`state_transition_bytecode.wasm`) along with the chain config file. The `ChainConfig` loads it automatically when `ChainConfig::load` is called and pushes it back when `ChainConfig::write` is called. - The `fuel-core` release bundle also contains the `fuel-core-wasm-executor.wasm` file of the corresponding executor version. - - The regenesis process now considers the last block produced by the previous network. When we create a (re)genesis block of a new network, it has the `height = last_block_of_old_netowkr + 1`. It continues the old network and doesn't overlap blocks(before, we had `old_block.height == new_genesis_block.hegiht`). + - The regenesis process now considers the last block produced by the previous network. When we create a (re)genesis block of a new network, it has the `height = last_block_of_old_network + 1`. It continues the old network and doesn't overlap blocks(before, we had `old_block.height == new_genesis_block.height`). - Along with the new block height, the regenesis process also increases the state transition bytecode and consensus parameters versions. It guarantees that a new network doesn't use values from the previous network and allows us not to migrate `StateTransitionBytecodeVersions` and `ConsensusParametersVersions` tables. - Added a new CLI argument, `native-executor-version,` that allows overriding of the default version of the native executor. It can be useful for side rollups that have their own history of executor upgrades. - Replaced: diff --git a/Cargo.lock b/Cargo.lock index 4beb51cc0af..0c1c22c785e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,9 +99,9 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" @@ -230,7 +230,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", "synstructure", ] @@ -242,7 +242,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -261,7 +261,7 @@ dependencies = [ "bstr", "doc-comment", "libc", - "predicates 3.1.2", + "predicates 3.1.3", "predicates-core", "predicates-tree", "wait-timeout", @@ -304,7 +304,7 @@ checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.2.0", + "fastrand 2.3.0", "futures-lite 2.5.0", "slab", ] @@ -337,9 +337,9 @@ dependencies = [ [[package]] name = "async-graphql" -version = "7.0.11" +version = "7.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba6d24703c5adc5ba9116901b92ee4e4c0643c01a56c4fd303f3818638d7449" +checksum = "59fd6bd734afb8b6e4d0f84a3e77305ce0a7ccc60d70f6001cb5e1c3f38d8ff1" dependencies = [ "async-graphql-derive", "async-graphql-parser", @@ -352,12 +352,11 @@ dependencies = [ "futures-timer", "futures-util", "handlebars", - "http 1.1.0", - "indexmap 2.6.0", + "http 1.2.0", + "indexmap 2.7.0", "mime", "multer", "num-traits", - "once_cell", "pin-project-lite", "regex", "serde", @@ -371,9 +370,9 @@ dependencies = [ [[package]] name = "async-graphql-derive" -version = "7.0.11" +version = "7.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a94c2d176893486bd37cd1b6defadd999f7357bf5804e92f510c08bcf16c538f" +checksum = "ac38b4dd452d529d6c0248b51df23603f0a875770352e26ae8c346ce6c149b3e" dependencies = [ "Inflector", "async-graphql-parser", @@ -382,15 +381,15 @@ dependencies = [ "proc-macro2", "quote", "strum 0.26.3", - "syn 2.0.90", + "syn 2.0.94", "thiserror 1.0.69", ] [[package]] name = "async-graphql-parser" -version = "7.0.11" +version = "7.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79272bdbf26af97866e149f05b2b546edb5c00e51b5f916289931ed233e208ad" +checksum = "42d271ddda2f55b13970928abbcbc3423cfc18187c60e8769b48f21a93b7adaa" dependencies = [ "async-graphql-value", "pest", @@ -400,12 +399,12 @@ dependencies = [ [[package]] name = "async-graphql-value" -version = "7.0.11" +version = "7.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef5ec94176a12a8cbe985cd73f2e54dc9c702c88c766bdef12f1f3a67cedbee1" +checksum = "aefe909173a037eaf3281b046dc22580b59a38b765d7b8d5116f2ffef098048d" dependencies = [ "bytes", - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_json", ] @@ -443,7 +442,7 @@ dependencies = [ "futures-lite 2.5.0", "parking", "polling 3.7.4", - "rustix 0.38.41", + "rustix 0.38.42", "slab", "tracing", "windows-sys 0.59.0", @@ -495,7 +494,7 @@ dependencies = [ "cfg-if", "event-listener 5.3.1", "futures-lite 2.5.0", - "rustix 0.38.41", + "rustix 0.38.42", "tracing", ] @@ -511,7 +510,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.41", + "rustix 0.38.42", "signal-hook-registry", "slab", "windows-sys 0.59.0", @@ -562,7 +561,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -573,13 +572,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "1b1244b10dcd56c92219da4e14caa97e312079e185f04ba3eea25061561dc0a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -664,7 +663,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -675,9 +674,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.10" +version = "1.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b49afaa341e8dd8577e1a2200468f98956d6eda50bcf4a53246cc00174ba924" +checksum = "c03a50b30228d3af8865ce83376b4e99e1ffa34728220fe2860e4df0bb5278d6" dependencies = [ "aws-credential-types", "aws-runtime", @@ -692,7 +691,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.2.0", + "fastrand 2.3.0", "hex", "http 0.2.12", "ring 0.17.8", @@ -717,9 +716,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.3" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a10d5c055aa540164d9561a0e2e74ad30f0dcf7393c3a92f6733ddf9c5762468" +checksum = "b16d1aa50accc11a4b4d5c50f7fb81cc0cf60328259c587d0e6b0f11385bde46" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -730,7 +729,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.2.0", + "fastrand 2.3.0", "http 0.2.12", "http-body 0.4.6", "once_cell", @@ -742,9 +741,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.50.0" +version = "1.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd059dacda4dfd5b57f2bd453fc6555f9acb496cb77508d517da24cf5d73167" +checksum = "a6cf16c0e5853312995505557b876dd3f9fb9941e96d031383528ccef14ace57" dependencies = [ "aws-credential-types", "aws-runtime", @@ -764,9 +763,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.49.0" +version = "1.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09677244a9da92172c8dc60109b4a9658597d4d298b188dd0018b6a66b410ca4" +checksum = "1605dc0bf9f0a4b05b451441a17fcb0bda229db384f23bf5cead3adbab0664ac" dependencies = [ "aws-credential-types", "aws-runtime", @@ -786,9 +785,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.50.0" +version = "1.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fea2f3a8bb3bd10932ae7ad59cc59f65f270fc9183a7e91f501dc5efbef7ee" +checksum = "59f3f73466ff24f6ad109095e0f3f2c830bfb4cd6c8b12f744c8e61ebf4d3ba1" dependencies = [ "aws-credential-types", "aws-runtime", @@ -808,9 +807,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.50.0" +version = "1.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ada54e5f26ac246dc79727def52f7f8ed38915cb47781e2a72213957dc3a7d5" +checksum = "249b2acaa8e02fd4718705a9494e3eb633637139aa4bb09d70965b0448e865db" dependencies = [ "aws-credential-types", "aws-runtime", @@ -831,9 +830,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.5" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5619742a0d8f253be760bfbb8e8e8368c69e3587e4637af5754e488a611499b1" +checksum = "7d3820e0c08d0737872ff3c7c1f21ebbb6693d832312d6152bf18ef50a5471c2" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -844,7 +843,7 @@ dependencies = [ "hex", "hmac 0.12.1", "http 0.2.12", - "http 1.1.0", + "http 1.2.0", "once_cell", "percent-encoding", "sha2 0.10.8", @@ -854,9 +853,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +checksum = "427cb637d15d63d6f9aae26358e1c9a9c09d5aa490d64b09354c8217cfef0f28" dependencies = [ "futures-util", "pin-project-lite", @@ -885,9 +884,9 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.60.7" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +checksum = "ee4e69cc50921eb913c6b662f8d909131bb3e6ad6cb6090d3a39b66fc5c52095" dependencies = [ "aws-smithy-types", ] @@ -904,22 +903,22 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.3" +version = "1.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be28bd063fa91fd871d131fc8b68d7cd4c5fa0869bea68daca50dcb1cbd76be2" +checksum = "a05dd41a70fc74051758ee75b5c4db2c0ca070ed9229c3df50e9475cda1cb985" dependencies = [ "aws-smithy-async", "aws-smithy-http", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", - "fastrand 2.2.0", - "h2", + "fastrand 2.3.0", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "http-body 1.0.1", "httparse", - "hyper 0.14.31", + "hyper 0.14.32", "hyper-rustls 0.24.2", "once_cell", "pin-project-lite", @@ -939,7 +938,7 @@ dependencies = [ "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.1.0", + "http 1.2.0", "pin-project-lite", "tokio", "tracing", @@ -948,16 +947,16 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.9" +version = "1.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fbd94a32b3a7d55d3806fe27d98d3ad393050439dd05eb53ece36ec5e3d3510" +checksum = "38ddc9bd6c28aeb303477170ddd183760a956a03e083b3902a990238a7e3792d" dependencies = [ "base64-simd", "bytes", "bytes-utils", "futures-core", "http 0.2.12", - "http 1.1.0", + "http 1.2.0", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -1002,15 +1001,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.2.9", "bitflags 1.3.2", "bytes", "futures-util", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", + "hyper 0.14.32", "itoa", - "matchit", + "matchit 0.5.0", "memchr", "mime", "percent-encoding", @@ -1020,12 +1019,67 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 0.1.2", "tokio", - "tower", + "tower 0.4.13", "tower-http 0.3.5", "tower-layer", "tower-service", ] +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core 0.3.4", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "itoa", + "matchit 0.7.3", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 0.1.2", + "tower 0.4.13", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core 0.4.5", + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "itoa", + "matchit 0.7.3", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 1.0.2", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-core" version = "0.2.9" @@ -1042,6 +1096,43 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -1134,7 +1225,23 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.90", + "syn 2.0.94", +] + +[[package]] +name = "bip32" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa13fae8b6255872fd86f7faf4b41168661d7d78609f7bfe6771b85c6739a15b" +dependencies = [ + "bs58", + "hmac 0.12.1", + "k256", + "rand_core", + "ripemd", + "sha2 0.10.8", + "subtle", + "zeroize", ] [[package]] @@ -1143,7 +1250,16 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", ] [[package]] @@ -1152,6 +1268,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -1231,9 +1353,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.0" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -1338,9 +1460,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.2" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" +checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" dependencies = [ "jobserver", "libc", @@ -1394,9 +1516,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1456,56 +1578,26 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "atty", - "bitflags 1.3.2", - "clap_derive 3.2.25", - "clap_lex 0.2.4", - "indexmap 1.9.3", - "once_cell", - "strsim 0.10.0", - "termcolor", - "textwrap", -] - -[[package]] -name = "clap" -version = "4.5.21" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", - "clap_derive 4.5.18", + "clap_derive", ] [[package]] name = "clap_builder" -version = "4.5.21" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstream", "anstyle", - "clap_lex 0.7.3", + "clap_lex", "strsim 0.11.1", ] -[[package]] -name = "clap_derive" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" -dependencies = [ - "heck 0.4.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "clap_derive" version = "4.5.18" @@ -1515,23 +1607,14 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", + "syn 2.0.94", ] [[package]] name = "clap_lex" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cobs" @@ -1608,15 +1691,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.8" +version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" dependencies = [ "encode_unicode", - "lazy_static", "libc", - "unicode-width 0.1.14", - "windows-sys 0.52.0", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", ] [[package]] @@ -1660,18 +1743,18 @@ dependencies = [ [[package]] name = "const_format" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c655d81ff1114fb0dcdea9225ea9f0cc712a6f8d189378e82bdf62a473a64b" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ "proc-macro2", "quote", @@ -1709,7 +1792,7 @@ checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" dependencies = [ "cookie", "document-features", - "idna 1.0.3", + "idna", "log", "publicsuffix", "serde", @@ -1744,6 +1827,37 @@ dependencies = [ "memchr", ] +[[package]] +name = "cosmos-sdk-proto" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "462e1f6a8e005acc8835d32d60cbd7973ed65ea2a8d8473830e675f050956427" +dependencies = [ + "prost 0.13.4", + "tendermint-proto 0.40.1", + "tonic 0.12.3", +] + +[[package]] +name = "cosmrs" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "210fbe6f98594963b46cc980f126a9ede5db9a3848ca65b71303bebdb01afcd9" +dependencies = [ + "bip32", + "cosmos-sdk-proto", + "ecdsa", + "eyre", + "k256", + "rand_core", + "serde", + "serde_json", + "signature", + "subtle-encoding", + "tendermint 0.40.1", + "thiserror 1.0.69", +] + [[package]] name = "cpp_demangle" version = "0.4.4" @@ -1902,7 +2016,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.21", + "clap", "criterion-plot", "futures", "is-terminal", @@ -1939,18 +2053,18 @@ checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1967,9 +2081,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" @@ -2088,18 +2202,31 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", +] + +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core", + "subtle-ng", + "zeroize", ] [[package]] name = "cynic" -version = "3.9.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a41762e03383d7bf4213b9b4e5fb15e232853400b0b738b764d1d2df9649400" +checksum = "17334136b717249d58b05739a825e2c83f53b0beee0e1f714c332cf48eee7023" dependencies = [ "cynic-proc-macros", "ref-cast", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_json", "static_assertions", @@ -2108,9 +2235,9 @@ dependencies = [ [[package]] name = "cynic-codegen" -version = "3.9.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9e35b6ceec97e397c422585aa7d8c057158c63d2727f15d4ddc47563862798" +checksum = "5aa0d13b24fe724349a8363f04183d4ee0dc2ab5e40c574f8f8ee2edbc0d344d" dependencies = [ "cynic-parser", "darling", @@ -2119,31 +2246,31 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.90", + "syn 2.0.94", "thiserror 1.0.69", ] [[package]] name = "cynic-parser" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b33b82bcdeebc7a3111be52b1a6a582206ff18d2811dd8575be9809cf6d4c88e" +checksum = "86a21da88ae46f2be6a622880a72f968d05c50b5a797e525332d0c988f693f70" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "lalrpop-util 0.22.0", "logos", ] [[package]] name = "cynic-proc-macros" -version = "3.9.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb9f1756c97b93a7e2faf9c5d58cc21655de6a32984dd8aa7aa076c7e246601" +checksum = "046126da74af834da9b8b6e9c2a8e56bf85bc4f2b871550586008237b64ff6bb" dependencies = [ "cynic-codegen", "darling", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -2167,7 +2294,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -2178,7 +2305,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -2271,7 +2398,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -2291,7 +2418,8 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", + "unicode-xid", ] [[package]] @@ -2407,7 +2535,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -2473,6 +2601,19 @@ dependencies = [ "signature", ] +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core", + "sha2 0.9.9", + "zeroize", +] + [[package]] name = "ed25519-dalek" version = "2.1.1" @@ -2488,6 +2629,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.94", +] + [[package]] name = "either" version = "1.13.0" @@ -2536,9 +2689,9 @@ dependencies = [ [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" @@ -2576,7 +2729,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -2596,7 +2749,27 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", +] + +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.94", ] [[package]] @@ -2751,7 +2924,7 @@ dependencies = [ "reqwest 0.11.27", "serde", "serde_json", - "syn 2.0.90", + "syn 2.0.94", "toml 0.8.19", "walkdir", ] @@ -2769,7 +2942,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -2795,7 +2968,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.3", - "syn 2.0.90", + "syn 2.0.94", "tempfile", "thiserror 1.0.69", "tiny-keccak", @@ -2974,9 +3147,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43ddc25e1ad2cc0106d5e2d967397b4fb2068a66677ee9b0eea4600e5cfe8fb4" dependencies = [ "futures", - "hyper 0.14.31", + "hyper 0.14.32", "hyper-rustls 0.24.2", - "hyper-timeout", + "hyper-timeout 0.4.1", "log", "pin-project", "rand", @@ -3010,9 +3183,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "ff" @@ -3070,6 +3243,16 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flex-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" +dependencies = [ + "eyre", + "paste", +] + [[package]] name = "float-cmp" version = "0.9.0" @@ -3087,9 +3270,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "form_urlencoded" @@ -3130,38 +3313,39 @@ dependencies = [ [[package]] name = "fuel-asm" -version = "0.58.2" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f325971bf9047ec70004f80a989e03456316bc19cbef3ff3a39a38b192ab56e" +checksum = "885617a606218680114122f4e1107ed5c9424e42dec05de84843e4a3a99e2cd7" dependencies = [ "bitflags 2.6.0", - "fuel-types 0.58.2", + "fuel-types 0.59.1", "serde", "strum 0.24.1", ] [[package]] name = "fuel-compression" -version = "0.58.2" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e42841f56f76ed759b3f516e5188d5c42de47015bee951651660c13b6dfa6c" +checksum = "6269052edeef79b4fe02ee1bb518e926098dd0ef81d88e3a617b218c05cef233" dependencies = [ - "fuel-derive 0.58.2", - "fuel-types 0.58.2", + "fuel-derive 0.59.1", + "fuel-types 0.59.1", "serde", ] [[package]] name = "fuel-core" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "assert_matches", "async-graphql", "async-graphql-value", "async-trait", - "axum", - "clap 4.5.21", + "axum 0.5.17", + "clap", + "cosmrs", "derive_more 0.99.18", "enum-iterator", "fuel-core", @@ -3178,15 +3362,16 @@ dependencies = [ "fuel-core-producer", "fuel-core-relayer", "fuel-core-services", + "fuel-core-shared-sequencer", "fuel-core-storage", "fuel-core-sync", "fuel-core-trace", "fuel-core-txpool", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "fuel-core-upgradable-executor", "futures", "hex", - "hyper 0.14.31", + "hyper 0.14.32", "indicatif", "itertools 0.12.1", "mockall", @@ -3208,7 +3393,7 @@ dependencies = [ "tokio-rayon", "tokio-stream", "tokio-util", - "tower", + "tower 0.4.13", "tower-http 0.4.4", "tracing", "uuid 1.11.0", @@ -3220,7 +3405,7 @@ version = "0.0.0" dependencies = [ "anyhow", "async-trait", - "clap 4.5.21", + "clap", "criterion", "ctrlc", "ed25519-dalek", @@ -3232,8 +3417,9 @@ dependencies = [ "fuel-core-services", "fuel-core-storage", "fuel-core-sync", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "futures", + "hex", "itertools 0.12.1", "num_enum", "p256", @@ -3253,16 +3439,16 @@ dependencies = [ [[package]] name = "fuel-core-bft" -version = "0.40.0" +version = "0.40.2" [[package]] name = "fuel-core-bin" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "aws-config", "aws-sdk-kms", - "clap 4.5.21", + "clap", "const_format", "dirs 4.0.0", "dotenvy", @@ -3270,9 +3456,9 @@ dependencies = [ "fuel-core-chain-config", "fuel-core-compression", "fuel-core-metrics", - "fuel-core-poa", + "fuel-core-shared-sequencer", "fuel-core-storage", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "hex", "humantime", "itertools 0.12.1", @@ -3295,7 +3481,7 @@ dependencies = [ [[package]] name = "fuel-core-chain-config" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "bech32", @@ -3303,7 +3489,7 @@ dependencies = [ "derivative", "fuel-core-chain-config", "fuel-core-storage", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "insta", "itertools 0.12.1", "parquet", @@ -3321,20 +3507,20 @@ dependencies = [ [[package]] name = "fuel-core-client" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "base64 0.22.1", "cynic", "derive_more 0.99.18", "eventsource-client", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "futures", "hex", "hyper-rustls 0.24.2", "insta", "itertools 0.12.1", - "reqwest 0.12.9", + "reqwest 0.12.12", "schemafy_lib", "serde", "serde_json", @@ -3345,22 +3531,22 @@ dependencies = [ [[package]] name = "fuel-core-client-bin" -version = "0.40.0" +version = "0.40.2" dependencies = [ - "clap 4.5.21", + "clap", "fuel-core-client", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "serde_json", "tokio", ] [[package]] name = "fuel-core-compression" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "fuel-core-compression", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "paste", "postcard", "proptest", @@ -3373,30 +3559,30 @@ dependencies = [ [[package]] name = "fuel-core-consensus-module" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "fuel-core-chain-config", "fuel-core-poa", "fuel-core-storage", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "test-case", ] [[package]] name = "fuel-core-database" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "derive_more 0.99.18", "fuel-core-storage", "fuel-core-trace", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", ] [[package]] name = "fuel-core-e2e-client" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "assert_cmd", @@ -3404,7 +3590,7 @@ dependencies = [ "fuel-core-chain-config", "fuel-core-client", "fuel-core-trace", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "futures", "hex", "humantime-serde", @@ -3421,12 +3607,12 @@ dependencies = [ [[package]] name = "fuel-core-executor" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "fuel-core-storage", "fuel-core-trace", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "hex", "parking_lot", "serde", @@ -3435,19 +3621,19 @@ dependencies = [ [[package]] name = "fuel-core-gas-price-service" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "async-trait", "enum-iterator", "fuel-core-services", "fuel-core-storage", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "fuel-gas-price-algorithm", "futures", "num_enum", "parking_lot", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "strum 0.25.0", "strum_macros 0.25.3", @@ -3459,14 +3645,14 @@ dependencies = [ [[package]] name = "fuel-core-importer" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "derive_more 0.99.18", "fuel-core-metrics", "fuel-core-storage", "fuel-core-trace", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "mockall", "parking_lot", "rayon", @@ -3477,22 +3663,22 @@ dependencies = [ [[package]] name = "fuel-core-keygen" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", - "clap 4.5.21", - "fuel-core-types 0.40.0", + "clap", + "fuel-core-types 0.40.2", "libp2p-identity", "serde", ] [[package]] name = "fuel-core-keygen-bin" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "atty", - "clap 4.5.21", + "clap", "crossterm", "fuel-core-keygen", "serde_json", @@ -3501,7 +3687,7 @@ dependencies = [ [[package]] name = "fuel-core-metrics" -version = "0.40.0" +version = "0.40.2" dependencies = [ "once_cell", "parking_lot", @@ -3516,7 +3702,7 @@ dependencies = [ [[package]] name = "fuel-core-p2p" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "async-trait", @@ -3527,7 +3713,7 @@ dependencies = [ "fuel-core-services", "fuel-core-storage", "fuel-core-trace", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "futures", "hex", "hickory-resolver", @@ -3557,19 +3743,16 @@ dependencies = [ [[package]] name = "fuel-core-poa" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "async-trait", - "aws-config", - "aws-sdk-kms", "fuel-core-chain-config", "fuel-core-poa", "fuel-core-services", "fuel-core-storage", "fuel-core-trace", - "fuel-core-types 0.40.0", - "k256", + "fuel-core-types 0.40.2", "mockall", "rand", "serde", @@ -3582,7 +3765,7 @@ dependencies = [ [[package]] name = "fuel-core-producer" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "async-trait", @@ -3590,7 +3773,7 @@ dependencies = [ "fuel-core-producer", "fuel-core-storage", "fuel-core-trace", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "mockall", "proptest", "rand", @@ -3601,7 +3784,7 @@ dependencies = [ [[package]] name = "fuel-core-relayer" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "async-trait", @@ -3615,7 +3798,7 @@ dependencies = [ "fuel-core-services", "fuel-core-storage", "fuel-core-trace", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "futures", "mockall", "once_cell", @@ -3634,7 +3817,7 @@ dependencies = [ [[package]] name = "fuel-core-services" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "async-trait", @@ -3649,16 +3832,39 @@ dependencies = [ "tracing", ] +[[package]] +name = "fuel-core-shared-sequencer" +version = "0.40.2" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.22.1", + "cosmos-sdk-proto", + "cosmrs", + "fuel-core-services", + "fuel-core-types 0.40.2", + "fuel-sequencer-proto", + "futures", + "postcard", + "prost 0.12.6", + "reqwest 0.12.12", + "serde", + "serde_json", + "tendermint-rpc", + "tokio", + "tracing", +] + [[package]] name = "fuel-core-storage" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "derive_more 0.99.18", "enum-iterator", "fuel-core-storage", - "fuel-core-types 0.40.0", - "fuel-vm 0.58.2", + "fuel-core-types 0.40.2", + "fuel-vm 0.59.1", "impl-tools", "itertools 0.12.1", "mockall", @@ -3675,13 +3881,13 @@ dependencies = [ [[package]] name = "fuel-core-sync" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "async-trait", "fuel-core-services", "fuel-core-trace", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "futures", "mockall", "rand", @@ -3700,7 +3906,7 @@ dependencies = [ "async-trait", "aws-config", "aws-sdk-kms", - "clap 4.5.21", + "clap", "cynic", "ethers", "fuel-core", @@ -3716,11 +3922,11 @@ dependencies = [ "fuel-core-storage", "fuel-core-trace", "fuel-core-txpool", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "fuel-core-upgradable-executor", "futures", "hex", - "hyper 0.14.31", + "hyper 0.14.32", "insta", "itertools 0.12.1", "k256", @@ -3729,7 +3935,7 @@ dependencies = [ "primitive-types", "proptest", "rand", - "reqwest 0.12.9", + "reqwest 0.12.12", "rstest", "serde_json", "spki", @@ -3742,7 +3948,7 @@ dependencies = [ [[package]] name = "fuel-core-trace" -version = "0.40.0" +version = "0.40.2" dependencies = [ "ctor", "tracing", @@ -3752,7 +3958,7 @@ dependencies = [ [[package]] name = "fuel-core-txpool" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "async-trait", @@ -3762,7 +3968,7 @@ dependencies = [ "fuel-core-storage", "fuel-core-trace", "fuel-core-txpool", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "futures", "mockall", "num-rational", @@ -3794,29 +4000,33 @@ dependencies = [ [[package]] name = "fuel-core-types" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", + "aws-config", + "aws-sdk-kms", "bs58", "derivative", "derive_more 0.99.18", - "fuel-vm 0.58.2", + "fuel-vm 0.59.1", + "k256", "rand", "secrecy", "serde", "tai64", + "tokio", "zeroize", ] [[package]] name = "fuel-core-upgradable-executor" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "derive_more 0.99.18", "fuel-core-executor", "fuel-core-storage", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "fuel-core-wasm-executor", "parking_lot", "postcard", @@ -3826,13 +4036,13 @@ dependencies = [ [[package]] name = "fuel-core-wasm-executor" -version = "0.40.0" +version = "0.40.2" dependencies = [ "anyhow", "fuel-core-executor", "fuel-core-storage", "fuel-core-types 0.35.0", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "postcard", "proptest", "serde", @@ -3857,15 +4067,15 @@ dependencies = [ [[package]] name = "fuel-crypto" -version = "0.58.2" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e318850ca64890ff123a99b6b866954ef49da94ab9bc6827cf6ee045568585" +checksum = "8e5643d6c281369825854bbc96d5d1648fe49ddcc3443961c633270f65e6c4b6" dependencies = [ "coins-bip32", "coins-bip39", "ecdsa", "ed25519-dalek", - "fuel-types 0.58.2", + "fuel-types 0.59.1", "k256", "lazy_static", "p256", @@ -3884,25 +4094,25 @@ checksum = "3f49fdbfc1615d88d2849650afc2b0ac2fecd69661ebadd31a073d8416747764" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", "synstructure", ] [[package]] name = "fuel-derive" -version = "0.58.2" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab0bc46a3552964bae5169e79b383761a54bd115ea66951a1a7a229edcefa55a" +checksum = "8703ee10001e6a52ad9a0d8411ca5a92098de978ccfbdddd0ba185f3a7405b4c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", "synstructure", ] [[package]] name = "fuel-gas-price-algorithm" -version = "0.40.0" +version = "0.40.2" dependencies = [ "proptest", "rand", @@ -3928,19 +4138,31 @@ dependencies = [ [[package]] name = "fuel-merkle" -version = "0.58.2" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c79eca6a452311c70978a5df796c0f99f27e474b69719e0db4c1d82e68800d07" +checksum = "7446f9cc1885ede3af88cf567931f96fd1feda3146e9052c09b1b260fbd56173" dependencies = [ "derive_more 0.99.18", "digest 0.10.7", - "fuel-storage 0.58.2", + "fuel-storage 0.59.1", "hashbrown 0.13.2", "hex", "serde", "sha2 0.10.8", ] +[[package]] +name = "fuel-sequencer-proto" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbdd607c9c70921cc016becde659e5062ae460b7bb3f525a1dd65f8209c0083" +dependencies = [ + "prost 0.12.6", + "prost-types", + "regex", + "tonic 0.11.0", +] + [[package]] name = "fuel-storage" version = "0.56.0" @@ -3949,9 +4171,9 @@ checksum = "4c1b711f28553ddc5f3546711bd220e144ce4c1af7d9e9a1f70b2f20d9f5b791" [[package]] name = "fuel-storage" -version = "0.58.2" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0c46b5d76b3e11197bd31e036cd8b1cb46c4d822cacc48836638080c6d2b76" +checksum = "01f810b544f0930fc68e1311ea078fad9af6912d5215a9ae9febda67206e34e5" [[package]] name = "fuel-tx" @@ -3977,18 +4199,18 @@ dependencies = [ [[package]] name = "fuel-tx" -version = "0.58.2" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6723bb8710ba2b70516ac94d34459593225870c937670fb3afaf82e0354667ac" +checksum = "806498d953bc989995425f1bb7c17890f5538a3664c6ec3b5d8a77c63d617421" dependencies = [ "bitflags 2.6.0", - "derivative", - "derive_more 0.99.18", - "fuel-asm 0.58.2", + "derive_more 1.0.0", + "educe", + "fuel-asm 0.59.1", "fuel-compression", - "fuel-crypto 0.58.2", - "fuel-merkle 0.58.2", - "fuel-types 0.58.2", + "fuel-crypto 0.59.1", + "fuel-merkle 0.59.1", + "fuel-types 0.59.1", "hashbrown 0.14.5", "itertools 0.10.5", "postcard", @@ -4011,11 +4233,11 @@ dependencies = [ [[package]] name = "fuel-types" -version = "0.58.2" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982265415a99b5bd6277bc24194a233bb2e18764df11c937b3dbb11a02c9e545" +checksum = "d0d46d41db59c131195e6d842cc1e50f0b8adeadf2a840519e8b57b78e57a0bf" dependencies = [ - "fuel-derive 0.58.2", + "fuel-derive 0.59.1", "hex", "rand", "serde", @@ -4054,24 +4276,24 @@ dependencies = [ [[package]] name = "fuel-vm" -version = "0.58.2" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b5362d7d072c72eec20581f67fc5400090c356a7f3ae77c79880b3b177b667" +checksum = "9bcf74f636cc01a1ab035458628f925945bb4df9da653058c6ab2b26fa25f087" dependencies = [ "anyhow", "async-trait", "backtrace", "bitflags 2.6.0", - "derivative", "derive_more 0.99.18", + "educe", "ethnum", - "fuel-asm 0.58.2", + "fuel-asm 0.59.1", "fuel-compression", - "fuel-crypto 0.58.2", - "fuel-merkle 0.58.2", - "fuel-storage 0.58.2", - "fuel-tx 0.58.2", - "fuel-types 0.58.2", + "fuel-crypto 0.59.1", + "fuel-merkle 0.59.1", + "fuel-storage 0.59.1", + "fuel-tx 0.59.1", + "fuel-types 0.59.1", "hashbrown 0.14.5", "itertools 0.10.5", "libm", @@ -4084,6 +4306,7 @@ dependencies = [ "sha3", "static_assertions", "strum 0.24.1", + "substrate-bn", "tai64", ] @@ -4173,7 +4396,7 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ - "fastrand 2.2.0", + "fastrand 2.3.0", "futures-core", "futures-io", "parking", @@ -4198,7 +4421,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -4208,7 +4431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" dependencies = [ "futures-io", - "rustls 0.23.19", + "rustls 0.23.20", "rustls-pki-types", ] @@ -4313,7 +4536,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" dependencies = [ "fallible-iterator", - "indexmap 2.6.0", + "indexmap 2.7.0", "stable_deref_trait", ] @@ -4325,9 +4548,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "gloo-timers" @@ -4376,7 +4599,26 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.6.0", + "indexmap 2.7.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.2.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -4526,9 +4768,9 @@ checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" [[package]] name = "hickory-proto" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" dependencies = [ "async-trait", "cfg-if", @@ -4537,7 +4779,7 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna 0.4.0", + "idna", "ipnet", "once_cell", "rand", @@ -4551,9 +4793,9 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" dependencies = [ "cfg-if", "futures-util", @@ -4611,11 +4853,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4642,9 +4884,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -4669,7 +4911,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.2.0", ] [[package]] @@ -4680,7 +4922,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "pin-project-lite", ] @@ -4721,15 +4963,15 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.31" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", @@ -4745,16 +4987,18 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "h2 0.4.7", + "http 1.2.0", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -4770,7 +5014,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.32", "log", "rustls 0.21.12", "rustls-native-certs", @@ -4781,18 +5025,18 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.5.1", + "http 1.2.0", + "hyper 1.5.2", "hyper-util", - "rustls 0.23.19", + "rustls 0.23.20", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tower-service", "webpki-roots 0.26.7", ] @@ -4803,12 +5047,25 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.31", + "hyper 0.14.32", "pin-project-lite", "tokio", "tokio-io-timeout", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper 1.5.2", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.10" @@ -4818,9 +5075,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.1", + "hyper 1.5.2", "pin-project-lite", "socket2 0.5.8", "tokio", @@ -4966,7 +5223,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -4981,16 +5238,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "1.0.3" @@ -5057,7 +5304,7 @@ dependencies = [ "bytes", "futures", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.32", "log", "rand", "tokio", @@ -5094,26 +5341,26 @@ dependencies = [ [[package]] name = "impl-tools" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a84bc8d2baf8da56e93b4247067d918e1a44829bbbe3e4b875aaf8d7d3c7bc9" +checksum = "0ae95c9095c2f1126d7db785955c73cdc5fc33e7c3fa911bd4a42931672029a7" dependencies = [ "autocfg", "impl-tools-lib", "proc-macro-error2", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] name = "impl-tools-lib" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a795a1e201125947a063b967c79de6ae152143ab522f481d4f493c44835ba37a" +checksum = "2a391adcea096a89a593317881fb61ef4e68d3e7d9de9e2338e6e1557be29e10" dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -5124,7 +5371,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -5146,9 +5393,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -5164,7 +5411,7 @@ dependencies = [ "console", "number_prefix", "portable-atomic", - "unicode-width 0.2.0", + "unicode-width", "web-time", ] @@ -5300,20 +5547,14 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ "once_cell", "wasm-bindgen", ] -[[package]] -name = "json" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" - [[package]] name = "jsonwebtoken" version = "8.3.0" @@ -5367,7 +5608,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", - "bit-set", + "bit-set 0.5.3", "ena", "itertools 0.11.0", "lalrpop-util 0.20.2", @@ -5404,6 +5645,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin 0.9.8", +] [[package]] name = "lazycell" @@ -5419,9 +5663,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.167" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libflate" @@ -5810,7 +6054,7 @@ dependencies = [ "quinn", "rand", "ring 0.17.8", - "rustls 0.23.19", + "rustls 0.23.20", "socket2 0.5.8", "thiserror 1.0.69", "tokio", @@ -5871,7 +6115,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -5923,7 +6167,7 @@ dependencies = [ "libp2p-identity", "rcgen", "ring 0.17.8", - "rustls 0.23.19", + "rustls 0.23.20", "rustls-webpki 0.101.7", "thiserror 1.0.69", "x509-parser", @@ -6073,7 +6317,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d8de370f98a6cb8a4606618e53e802f93b094ddec0f96988eaec2c27e6e9ce7" dependencies = [ - "clap 4.5.21", + "clap", "termcolor", "threadpool", ] @@ -6140,18 +6384,18 @@ dependencies = [ [[package]] name = "logos" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6b6e02facda28ca5fb8dbe4b152496ba3b1bd5a4b40bb2b1b2d8ad74e0f39b" +checksum = "7251356ef8cb7aec833ddf598c6cb24d17b689d20b993f9d11a3d764e34e6458" dependencies = [ "logos-derive", ] [[package]] name = "logos-codegen" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32eb6b5f26efacd015b000bfc562186472cd9b34bdba3f6b264e2a052676d10" +checksum = "59f80069600c0d66734f5ff52cc42f2dabd6b29d205f333d61fd7832e9e9963f" dependencies = [ "beef", "fnv", @@ -6159,14 +6403,14 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.8.5", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] name = "logos-derive" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5d0c5463c911ef55624739fc353238b4e310f0144be1f875dc42fec6bfd5ec" +checksum = "24fb722b06a9dc12adb0963ed585f19fc61dc5413e6a9be9422ef92c091e731d" dependencies = [ "logos-codegen", ] @@ -6229,6 +6473,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "md-5" version = "0.10.6" @@ -6251,7 +6501,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.41", + "rustix 0.38.42", ] [[package]] @@ -6277,9 +6527,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", ] @@ -6343,7 +6593,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.1.0", + "http 1.2.0", "httparse", "memchr", "mime", @@ -6383,9 +6633,9 @@ dependencies = [ [[package]] name = "multihash" -version = "0.19.2" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc41f430805af9d1cf4adae4ed2149c759b877b01d909a1f40256188d09345d2" +checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" dependencies = [ "core2", "unsigned-varint 0.8.0", @@ -6411,7 +6661,6 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" dependencies = [ - "clap 3.2.25", "rand", ] @@ -6469,11 +6718,11 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" +checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" dependencies = [ - "async-io 1.13.0", + "async-io 2.4.0", "bytes", "futures", "libc", @@ -6498,6 +6747,17 @@ dependencies = [ "libc", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "libc", +] + [[package]] name = "nix" version = "0.29.0" @@ -6618,7 +6878,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -6649,7 +6908,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -6666,13 +6925,13 @@ checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", "hashbrown 0.15.2", - "indexmap 2.6.0", + "indexmap 2.7.0", "memchr", ] @@ -6749,12 +7008,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" - [[package]] name = "ouroboros" version = "0.18.4" @@ -6777,7 +7030,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -6806,29 +7059,28 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.7.0" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be4817d39f3272f69c59fe05d0535ae6456c2dc2fa1ba02910296c7e0a5c590" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ "arrayvec", "bitvec", "byte-slice-cast", "impl-trait-for-tuples", "parity-scale-codec-derive", - "rustversion", "serde", ] [[package]] name = "parity-scale-codec-derive" -version = "3.7.0" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8781a75c6205af67215f382092b6e0a4ff3734798523e69073d4bcd294ec767b" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 1.0.109", ] [[package]] @@ -6855,7 +7107,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "smallvec", "windows-targets 0.52.6", ] @@ -6930,6 +7182,33 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "peg" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" + [[package]] name = "pem" version = "1.1.1" @@ -6957,20 +7236,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 1.0.69", + "thiserror 2.0.9", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" dependencies = [ "pest", "pest_generator", @@ -6978,22 +7257,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] name = "pest_meta" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" dependencies = [ "once_cell", "pest", @@ -7007,7 +7286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.6.0", + "indexmap 2.7.0", ] [[package]] @@ -7050,7 +7329,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -7088,7 +7367,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -7110,7 +7389,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.2.0", + "fastrand 2.3.0", "futures-io", ] @@ -7184,7 +7463,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.41", + "rustix 0.38.42", "tracing", "windows-sys 0.59.0", ] @@ -7238,23 +7517,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] -name = "pprof" -version = "0.12.1" +name = "pprof2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978385d59daf9269189d052ca8a84c1acfd0715c0599a5d5188d4acc078ca46a" +checksum = "8961ed0a916b512e565f8070eb0dfa05773dd140160b45ac9a5ad339b557adeb" dependencies = [ "backtrace", "cfg-if", "findshlibs", "libc", "log", - "nix 0.26.4", + "nix 0.27.1", "once_cell", "parking_lot", "smallvec", "symbolic-demangle", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.9", ] [[package]] @@ -7288,9 +7567,9 @@ dependencies = [ [[package]] name = "predicates" -version = "3.1.2" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ "anstyle", "difflib", @@ -7299,15 +7578,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", @@ -7330,7 +7609,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -7365,30 +7644,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -7427,7 +7682,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", "version_check", "yansi 1.0.1", ] @@ -7452,17 +7707,17 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] name = "proptest" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ - "bit-set", - "bit-vec", + "bit-set 0.8.0", + "bit-vec 0.8.0", "bitflags 2.6.0", "lazy_static", "num-traits", @@ -7482,7 +7737,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive 0.12.6", +] + +[[package]] +name = "prost" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +dependencies = [ + "bytes", + "prost-derive 0.13.4", ] [[package]] @@ -7498,6 +7773,41 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.94", +] + +[[package]] +name = "prost-derive" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.94", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost 0.12.6", +] + [[package]] name = "psl-types" version = "2.0.11" @@ -7519,23 +7829,23 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" dependencies = [ - "idna 1.0.3", + "idna", "psl-types", ] [[package]] name = "pyroscope" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8a53ce01af1087eaeee6ce7c4fbf50ea4040ab1825c0115c4bafa039644ba9" +checksum = "d3a5f63b0d2727095db59045e6a0ef3259b28b90d481ae88f0e3d866d0234ce8" dependencies = [ - "json", "libc", "libflate", "log", "names", - "prost", - "reqwest 0.11.27", + "prost 0.11.9", + "reqwest 0.12.12", + "serde_json", "thiserror 1.0.69", "url", "winapi", @@ -7543,21 +7853,21 @@ dependencies = [ [[package]] name = "pyroscope_pprofrs" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f010b2a981a7f8449a650f25f309e520b5206ea2d89512dcb146aaa5518ff4" +checksum = "614a25777053da6bdca9d84a67892490b5a57590248dbdee3d7bf0716252af70" dependencies = [ "log", - "pprof", + "pprof2", "pyroscope", "thiserror 1.0.69", ] [[package]] name = "quanta" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" +checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" dependencies = [ "crossbeam-utils", "libc", @@ -7620,9 +7930,9 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.1.0", - "rustls 0.23.19", + "rustls 0.23.20", "socket2 0.5.8", - "thiserror 2.0.3", + "thiserror 2.0.9", "tokio", "tracing", ] @@ -7638,10 +7948,10 @@ dependencies = [ "rand", "ring 0.17.8", "rustc-hash 2.1.0", - "rustls 0.23.19", + "rustls 0.23.20", "rustls-pki-types", "slab", - "thiserror 2.0.3", + "thiserror 2.0.9", "tinyvec", "tracing", "web-time", @@ -7649,9 +7959,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" dependencies = [ "cfg_aliases", "libc", @@ -7663,9 +7973,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -7767,9 +8077,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -7808,7 +8118,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -7885,10 +8195,10 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", + "hyper 0.14.32", "hyper-rustls 0.24.2", "ipnet", "js-sys", @@ -7898,6 +8208,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.12", + "rustls-native-certs", "rustls-pemfile 1.0.4", "serde", "serde_json", @@ -7917,21 +8228,22 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", "cookie", "cookie_store", + "futures-channel", "futures-core", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper 1.5.2", + "hyper-rustls 0.27.5", "hyper-util", "ipnet", "js-sys", @@ -7941,7 +8253,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.19", + "rustls 0.23.20", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", @@ -7949,7 +8261,8 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", @@ -8167,15 +8480,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8192,9 +8505,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.19" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "once_cell", "ring 0.17.8", @@ -8236,9 +8549,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" dependencies = [ "web-time", ] @@ -8266,9 +8579,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "rusty-fork" @@ -8338,7 +8651,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -8461,9 +8774,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", @@ -8471,9 +8784,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] @@ -8498,29 +8811,38 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "itoa", "memchr", @@ -8528,6 +8850,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.94", +] + [[package]] name = "serde_spanned" version = "0.6.8" @@ -8551,15 +8884,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_derive", "serde_json", @@ -8569,14 +8902,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -8585,7 +8918,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "itoa", "ryu", "serde", @@ -8796,9 +9129,9 @@ dependencies = [ [[package]] name = "soketto" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37468c595637c10857701c990f93a40ce0e357cedb0953d1c26c8d8027f9bb53" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" dependencies = [ "base64 0.22.1", "bytes", @@ -8906,7 +9239,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -8917,7 +9250,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -8970,7 +9303,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -8983,7 +9316,20 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.90", + "syn 2.0.94", +] + +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand", + "rustc-hex", ] [[package]] @@ -8992,6 +9338,21 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "subtle-encoding" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" +dependencies = [ + "zeroize", +] + +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "svm-rs" version = "0.3.5" @@ -9014,9 +9375,9 @@ dependencies = [ [[package]] name = "symbolic-common" -version = "12.12.3" +version = "12.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ba5365997a4e375660bed52f5b42766475d5bc8ceb1bb13fea09c469ea0f49" +checksum = "cd33e73f154e36ec223c18013f7064a2c120f1162fc086ac9933542def186b00" dependencies = [ "debugid", "memmap2", @@ -9026,9 +9387,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.12.3" +version = "12.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beff338b2788519120f38c59ff4bb15174f52a183e547bac3d6072c2c0aa48aa" +checksum = "89e51191290147f071777e37fe111800bb82a9059f9c95b19d2dd41bfeddf477" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -9048,9 +9409,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" dependencies = [ "proc-macro2", "quote", @@ -9080,7 +9441,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -9148,17 +9509,155 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", - "fastrand 2.2.0", + "fastrand 2.3.0", + "getrandom", "once_cell", - "rustix 0.38.41", + "rustix 0.38.42", "windows-sys 0.59.0", ] +[[package]] +name = "tendermint" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b50aae6ec24c3429149ad59b5b8d3374d7804d4c7d6125ceb97cb53907fb68d" +dependencies = [ + "bytes", + "digest 0.10.7", + "ed25519", + "ed25519-consensus", + "flex-error", + "futures", + "num-traits", + "once_cell", + "prost 0.12.6", + "prost-types", + "serde", + "serde_bytes", + "serde_json", + "serde_repr", + "sha2 0.10.8", + "signature", + "subtle", + "subtle-encoding", + "tendermint-proto 0.36.0", + "time", + "zeroize", +] + +[[package]] +name = "tendermint" +version = "0.40.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9703e34d940c2a293804752555107f8dbe2b84ec4c6dd5203831235868105d2" +dependencies = [ + "bytes", + "digest 0.10.7", + "ed25519", + "ed25519-consensus", + "flex-error", + "futures", + "k256", + "num-traits", + "once_cell", + "prost 0.13.4", + "ripemd", + "serde", + "serde_bytes", + "serde_json", + "serde_repr", + "sha2 0.10.8", + "signature", + "subtle", + "subtle-encoding", + "tendermint-proto 0.40.1", + "time", + "zeroize", +] + +[[package]] +name = "tendermint-config" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e07b383dc8780ebbec04cfb603f3fdaba6ea6663d8dd861425b1ffa7761fe90d" +dependencies = [ + "flex-error", + "serde", + "serde_json", + "tendermint 0.36.0", + "toml 0.8.19", + "url", +] + +[[package]] +name = "tendermint-proto" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f193d04afde6592c20fd70788a10b8cb3823091c07456db70d8a93f5fb99c1" +dependencies = [ + "bytes", + "flex-error", + "prost 0.12.6", + "prost-types", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "tendermint-proto" +version = "0.40.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9e1705aa0fa5ecb2c6aa7fb78c2313c4a31158ea5f02048bf318f849352eb" +dependencies = [ + "bytes", + "flex-error", + "prost 0.13.4", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "tendermint-rpc" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e3c231a3632cab53f92ad4161c730c468c08cfe4f0aa5a6735b53b390aecbd" +dependencies = [ + "async-trait", + "bytes", + "flex-error", + "futures", + "getrandom", + "peg", + "pin-project", + "rand", + "reqwest 0.11.27", + "semver", + "serde", + "serde_bytes", + "serde_json", + "subtle", + "subtle-encoding", + "tendermint 0.36.0", + "tendermint-config", + "tendermint-proto 0.36.0", + "thiserror 1.0.69", + "time", + "tokio", + "tracing", + "url", + "uuid 1.11.0", + "walkdir", +] + [[package]] name = "term" version = "0.7.0" @@ -9193,9 +9692,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "test-case" @@ -9215,7 +9714,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -9226,7 +9725,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", "test-case-core", ] @@ -9235,7 +9734,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "anyhow", - "clap 4.5.21", + "clap", "fuel-core", "fuel-core-bin", "fuel-core-client", @@ -9245,11 +9744,11 @@ dependencies = [ "fuel-core-storage", "fuel-core-trace", "fuel-core-txpool", - "fuel-core-types 0.40.0", + "fuel-core-types 0.40.2", "futures", "itertools 0.12.1", "rand", - "reqwest 0.12.9", + "reqwest 0.12.12", "tempfile", "tokio", ] @@ -9263,15 +9762,9 @@ dependencies = [ "proc-macro2", "quote", "structmeta", - "syn 2.0.90", + "syn 2.0.94", ] -[[package]] -name = "textwrap" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" - [[package]] name = "thiserror" version = "1.0.69" @@ -9283,11 +9776,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.9", ] [[package]] @@ -9298,18 +9791,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -9364,9 +9857,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -9385,9 +9878,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -9424,9 +9917,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -9439,9 +9932,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -9473,7 +9966,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -9498,20 +9991,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.23.19", - "rustls-pki-types", + "rustls 0.23.20", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -9536,9 +10028,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -9583,13 +10075,70 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", "winnow", ] +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.6.20", + "base64 0.21.7", + "bytes", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-timeout 0.4.1", + "percent-encoding", + "pin-project", + "prost 0.12.6", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.7.9", + "base64 0.22.1", + "bytes", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.2", + "hyper-timeout 0.5.2", + "hyper-util", + "percent-encoding", + "pin-project", + "prost 0.13.4", + "socket2 0.5.8", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.4.13" @@ -9598,8 +10147,11 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", + "indexmap 1.9.3", "pin-project", "pin-project-lite", + "rand", + "slab", "tokio", "tokio-util", "tower-layer", @@ -9607,6 +10159,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-http" version = "0.3.5" @@ -9621,7 +10188,7 @@ dependencies = [ "http-body 0.4.6", "http-range-header", "pin-project-lite", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", ] @@ -9690,7 +10257,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -9823,33 +10390,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - [[package]] name = "unicode-width" version = "0.2.0" @@ -9913,7 +10459,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 1.0.3", + "idna", "percent-encoding", ] @@ -10044,9 +10590,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -10055,24 +10601,23 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.47" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", @@ -10083,9 +10628,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -10093,22 +10638,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-encoder" @@ -10128,7 +10673,7 @@ dependencies = [ "ahash", "bitflags 2.6.0", "hashbrown 0.14.5", - "indexmap 2.6.0", + "indexmap 2.7.0", "semver", "serde", ] @@ -10156,7 +10701,7 @@ dependencies = [ "cc", "cfg-if", "hashbrown 0.14.5", - "indexmap 2.6.0", + "indexmap 2.7.0", "libc", "libm", "log", @@ -10168,7 +10713,7 @@ dependencies = [ "postcard", "psm", "rayon", - "rustix 0.38.41", + "rustix 0.38.42", "serde", "serde_derive", "smallvec", @@ -10206,7 +10751,7 @@ dependencies = [ "directories-next", "log", "postcard", - "rustix 0.38.41", + "rustix 0.38.42", "serde", "serde_derive", "sha2 0.10.8", @@ -10224,7 +10769,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", "wasmtime-component-util", "wasmtime-wit-bindgen", "wit-parser", @@ -10270,7 +10815,7 @@ dependencies = [ "cranelift-bitset", "cranelift-entity", "gimli 0.28.1", - "indexmap 2.6.0", + "indexmap 2.7.0", "log", "object", "postcard", @@ -10323,7 +10868,7 @@ checksum = "a2bde986038b819bc43a21fef0610aeb47aabfe3ea09ca3533a7b81023b84ec6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -10334,15 +10879,15 @@ checksum = "8f88e49a9b81746ec0cede5505e40a4012c92cb5054cd7ef4300dc57c36f26b1" dependencies = [ "anyhow", "heck 0.4.1", - "indexmap 2.6.0", + "indexmap 2.7.0", "wit-parser", ] [[package]] name = "web-sys" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -10628,9 +11173,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" dependencies = [ "memchr", ] @@ -10653,7 +11198,7 @@ checksum = "ceeb0424aa8679f3fcf2d6e3cfa381f3d6fa6179976a2c05a6249dd2bb426716" dependencies = [ "anyhow", "id-arena", - "indexmap 2.6.0", + "indexmap 2.7.0", "log", "semver", "serde", @@ -10734,9 +11279,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af310deaae937e48a26602b730250b4949e125f468f11e6990be3e5304ddd96f" +checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" [[package]] name = "xmlparser" @@ -10757,7 +11302,7 @@ dependencies = [ name = "xtask" version = "0.0.0" dependencies = [ - "clap 4.5.21", + "clap", "fuel-core", ] @@ -10833,7 +11378,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", "synstructure", ] @@ -10855,7 +11400,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -10875,7 +11420,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", "synstructure", ] @@ -10896,7 +11441,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -10918,7 +11463,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0a711e83ad3..dc85bbc0535 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ members = [ "crates/services/p2p", "crates/services/producer", "crates/services/relayer", + "crates/services/shared-sequencer", "crates/services/sync", "crates/services/txpool_v2", "crates/services/upgradable-executor", @@ -51,47 +52,49 @@ homepage = "https://fuel.network/" keywords = ["blockchain", "cryptocurrencies", "fuel-vm", "vm"] license = "BUSL-1.1" repository = "https://github.com/FuelLabs/fuel-core" -version = "0.40.0" +version = "0.40.2" [workspace.dependencies] # Workspace members -fuel-core = { version = "0.40.0", path = "./crates/fuel-core", default-features = false } -fuel-core-client-bin = { version = "0.40.0", path = "./bin/fuel-core-client" } -fuel-core-bin = { version = "0.40.0", path = "./bin/fuel-core" } -fuel-core-keygen = { version = "0.40.0", path = "./crates/keygen" } -fuel-core-keygen-bin = { version = "0.40.0", path = "./bin/keygen" } -fuel-core-chain-config = { version = "0.40.0", path = "./crates/chain-config", default-features = false } -fuel-core-client = { version = "0.40.0", path = "./crates/client" } -fuel-core-compression = { version = "0.40.0", path = "./crates/compression" } -fuel-core-database = { version = "0.40.0", path = "./crates/database" } -fuel-core-metrics = { version = "0.40.0", path = "./crates/metrics" } -fuel-core-services = { version = "0.40.0", path = "./crates/services" } -fuel-core-consensus-module = { version = "0.40.0", path = "./crates/services/consensus_module" } -fuel-core-bft = { version = "0.40.0", path = "./crates/services/consensus_module/bft" } -fuel-core-poa = { version = "0.40.0", path = "./crates/services/consensus_module/poa" } -fuel-core-executor = { version = "0.40.0", path = "./crates/services/executor", default-features = false } -fuel-core-importer = { version = "0.40.0", path = "./crates/services/importer" } -fuel-core-gas-price-service = { version = "0.40.0", path = "crates/services/gas_price_service" } -fuel-core-p2p = { version = "0.40.0", path = "./crates/services/p2p" } -fuel-core-producer = { version = "0.40.0", path = "./crates/services/producer" } -fuel-core-relayer = { version = "0.40.0", path = "./crates/services/relayer" } -fuel-core-sync = { version = "0.40.0", path = "./crates/services/sync" } -fuel-core-txpool = { version = "0.40.0", path = "./crates/services/txpool_v2" } -fuel-core-storage = { version = "0.40.0", path = "./crates/storage", default-features = false } -fuel-core-trace = { version = "0.40.0", path = "./crates/trace" } -fuel-core-types = { version = "0.40.0", path = "./crates/types", default-features = false } +fuel-core = { version = "0.40.2", path = "./crates/fuel-core", default-features = false } +fuel-core-client-bin = { version = "0.40.2", path = "./bin/fuel-core-client" } +fuel-core-bin = { version = "0.40.2", path = "./bin/fuel-core" } +fuel-core-keygen = { version = "0.40.2", path = "./crates/keygen" } +fuel-core-keygen-bin = { version = "0.40.2", path = "./bin/keygen" } +fuel-core-chain-config = { version = "0.40.2", path = "./crates/chain-config", default-features = false } +fuel-core-client = { version = "0.40.2", path = "./crates/client" } +fuel-core-compression = { version = "0.40.2", path = "./crates/compression" } +fuel-core-database = { version = "0.40.2", path = "./crates/database" } +fuel-core-metrics = { version = "0.40.2", path = "./crates/metrics" } +fuel-core-services = { version = "0.40.2", path = "./crates/services" } +fuel-core-consensus-module = { version = "0.40.2", path = "./crates/services/consensus_module" } +fuel-core-bft = { version = "0.40.2", path = "./crates/services/consensus_module/bft" } +fuel-core-poa = { version = "0.40.2", path = "./crates/services/consensus_module/poa" } +fuel-core-shared-sequencer = { version = "0.40.2", path = "crates/services/shared-sequencer" } +fuel-core-executor = { version = "0.40.2", path = "./crates/services/executor", default-features = false } +fuel-core-importer = { version = "0.40.2", path = "./crates/services/importer" } +fuel-core-gas-price-service = { version = "0.40.2", path = "crates/services/gas_price_service" } +fuel-core-p2p = { version = "0.40.2", path = "./crates/services/p2p" } +fuel-core-producer = { version = "0.40.2", path = "./crates/services/producer" } +fuel-core-relayer = { version = "0.40.2", path = "./crates/services/relayer" } +fuel-core-sync = { version = "0.40.2", path = "./crates/services/sync" } +fuel-core-txpool = { version = "0.40.2", path = "./crates/services/txpool_v2" } +fuel-core-storage = { version = "0.40.2", path = "./crates/storage", default-features = false } +fuel-core-trace = { version = "0.40.2", path = "./crates/trace" } +fuel-core-types = { version = "0.40.2", path = "./crates/types", default-features = false } fuel-core-tests = { version = "0.0.0", path = "./tests" } -fuel-core-upgradable-executor = { version = "0.40.0", path = "./crates/services/upgradable-executor" } -fuel-core-wasm-executor = { version = "0.40.0", path = "./crates/services/upgradable-executor/wasm-executor", default-features = false } +fuel-core-upgradable-executor = { version = "0.40.2", path = "./crates/services/upgradable-executor" } +fuel-core-wasm-executor = { version = "0.40.2", path = "./crates/services/upgradable-executor/wasm-executor", default-features = false } fuel-core-xtask = { version = "0.0.0", path = "./xtask" } -fuel-gas-price-algorithm = { version = "0.40.0", path = "crates/fuel-gas-price-algorithm" } +fuel-gas-price-algorithm = { version = "0.40.2", path = "crates/fuel-gas-price-algorithm" } # Fuel dependencies -fuel-vm-private = { version = "0.58.2", package = "fuel-vm", default-features = false } +fuel-vm-private = { version = "0.59.1", package = "fuel-vm", default-features = false } # Common dependencies anyhow = "1.0" async-trait = "0.1" +aws-sdk-kms = "1.37" cynic = { version = "3.1.0", features = ["http-reqwest"] } clap = "4.4" derivative = { version = "2" } diff --git a/README.md b/README.md index 52f64d7d27b..b5b60613ddf 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Before pushing any changes or creating pull request please run `source ci_checks ## Building +If you plan to use already pre-compiled binairies you can directly go to [Running a Ignition node](#running-a-ignition-node). + ### System Requirements There are several system requirements including clang. @@ -47,6 +49,50 @@ You'll need `wasm32-unknown-unknown` target installed. rustup target add wasm32-unknown-unknown ``` + +## Running a Ignition node + +If you want to participate in the Ignition network with your own node you can launch it following these simple commands. + +### From pre-compiled binaries + +Follow : https://docs.fuel.network/guides/running-a-node/running-a-mainnet-node/ + +### From source + +Clone the `fuel-core` repository : +``` +git clone https://github.com/FuelLabs/fuel-core.git +``` + +Go to the latest release tag for ignition on the `fuel-core` repository : +``` +git checkout v0.40.2 +``` + +Build your node binary: +```bash +cargo build --release --bin fuel-core --no-default-features --features production +``` + +To run the node follow : https://docs.fuel.network/guides/running-a-node/running-a-mainnet-node/ + +## Running a Local network from source + +Clone the `fuel-core` repository : +``` +git clone https://github.com/FuelLabs/fuel-core.git +``` + +Build your node binary: +```bash +cargo build --release --bin fuel-core --no-default-features --features production +``` + +To run the node follow : https://docs.fuel.network/guides/running-a-node/running-a-local-node/ + +## Setup to make contributions + ### Compiling We recommend using `xtask` to build fuel-core: @@ -111,47 +157,6 @@ $ ./target/debug/fuel-core run --poa-instant=false 2023-06-13T12:44:12.857763Z INFO fuel_core::cli::run: 232: Block production disabled ``` -## Running a Ignition node - -If you want to participate in the Ignition network with your own node you can launch it following these simple commands. - -Install the latest fuelup : -```bash -curl -fsSL https://install.fuel.network/ | sh -fuelup toolchain install latest -``` - -Clone the chain configuration folder : -``` -git clone https://github.com/FuelLabs/chain-configuration -``` - -Generate a keypair for your node: -```bash -fuel-core-keygen new --key-type peering -``` -and copy the secret key displayed. - -Run your node (change all variable with `{}` to your own personal variables): -```bash -fuel-core run \ ---enable-relayer \ ---service-name fuel-ignition-node \ ---keypair {KEYGEN_SECRET_KEY} \ ---relayer {ETHEREUM_RPC_ENDPOINT} \ ---ip=0.0.0.0 --port 4000 --peering-port 30333 \ ---db-path ~/.fuel-ignition \ ---snapshot {PATH_TO_CHAIN_CONFIGURATION_FOLDER}/ignition \ ---utxo-validation --poa-instant false --enable-p2p \ ---bootstrap-nodes /dnsaddr/mainnet.fuel.network \ ---sync-header-batch-size 100 \ ---relayer-v2-listening-contracts=0xAEB0c00D0125A8a788956ade4f4F12Ead9f65DDf \ ---relayer-da-deploy-height=20620434 \ ---relayer-log-page-size=100 \ ---sync-block-stream-buffer-size 30 -``` -Instead of directly placing your personal values on the command we advise you to use, for example, environment variables. - ### Troubleshooting #### Publishing diff --git a/benches/Cargo.toml b/benches/Cargo.toml index e4ee7db927b..b31c8cf948f 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -31,6 +31,7 @@ fuel-core-sync = { path = "./../crates/services/sync", features = [ ] } fuel-core-types = { path = "./../crates/types", features = ["test-helpers"] } futures = { workspace = true } +hex = "0.4.3" itertools = { workspace = true } num_enum = { workspace = true } p256 = { version = "0.13", default-features = false, features = [ @@ -63,7 +64,7 @@ harness = false name = "vm" [features] -default = ["fuel-core/rocksdb"] +default = ["fuel-core/rocksdb", "fuel-core/rocksdb-production"] [[bench]] harness = false diff --git a/benches/benches-outputs/src/lib.rs b/benches/benches-outputs/src/lib.rs index 5ff79c3ae70..505bbc6f3ba 100644 --- a/benches/benches-outputs/src/lib.rs +++ b/benches/benches-outputs/src/lib.rs @@ -1,5 +1,5 @@ use fuel_core_types::fuel_tx::{ - consensus_parameters::gas::GasCostsValuesV4, + consensus_parameters::gas::*, DependentCost, GasCostsValues, }; diff --git a/benches/benches/block_target_gas.rs b/benches/benches/block_target_gas.rs index fddd14a24c9..e1384e65a81 100644 --- a/benches/benches/block_target_gas.rs +++ b/benches/benches/block_target_gas.rs @@ -27,7 +27,13 @@ use fuel_core::{ Config, FuelService, }, - state::historical_rocksdb::StateRewindPolicy, + state::{ + historical_rocksdb::StateRewindPolicy, + rocks_db::{ + ColumnsPolicy, + DatabaseConfig, + }, + }, }; use fuel_core_benches::{ default_gas_costs::default_gas_costs, @@ -266,8 +272,15 @@ fn service_with_many_contracts( .build() .unwrap(); let _drop = rt.enter(); - let mut database = Database::rocksdb_temp(StateRewindPolicy::NoRewind) - .expect("Failed to create database"); + let mut database = Database::rocksdb_temp( + StateRewindPolicy::NoRewind, + DatabaseConfig { + cache_capacity: Some(16 * 1024 * 1024 * 1024), + max_fds: -1, + columns_policy: ColumnsPolicy::OnCreation, + }, + ) + .expect("Failed to create database"); let mut chain_config = ChainConfig::local_testnet(); diff --git a/benches/benches/state.rs b/benches/benches/state.rs index d08ecf2104c..e661ee9ded2 100644 --- a/benches/benches/state.rs +++ b/benches/benches/state.rs @@ -5,10 +5,19 @@ use criterion::{ BenchmarkGroup, Criterion, }; -use fuel_core::database::{ - database_description::on_chain::OnChain, - state::StateInitializer, - Database, +use fuel_core::{ + database::{ + database_description::on_chain::OnChain, + state::StateInitializer, + Database, + }, + state::{ + historical_rocksdb::StateRewindPolicy, + rocks_db::{ + ColumnsPolicy, + DatabaseConfig, + }, + }, }; use fuel_core_storage::{ transactional::{ @@ -71,7 +80,15 @@ fn insert_state_single_contract_database(c: &mut Criterion) { let mut bench_state = |group: &mut BenchmarkGroup, name: &str, n: usize| { group.bench_function(name, |b| { - let mut db = Database::::default(); + let mut db = Database::::rocksdb_temp( + StateRewindPolicy::NoRewind, + DatabaseConfig { + cache_capacity: Some(16 * 1024 * 1024 * 1024), + max_fds: -1, + columns_policy: ColumnsPolicy::OnCreation, + }, + ) + .unwrap(); let contract: ContractId = rng.gen(); setup(&mut db, &contract, n); let outer = db.write_transaction(); diff --git a/benches/benches/transaction_throughput.rs b/benches/benches/transaction_throughput.rs index 5d78818e8da..5e37c3af6a5 100644 --- a/benches/benches/transaction_throughput.rs +++ b/benches/benches/transaction_throughput.rs @@ -9,7 +9,16 @@ use criterion::{ SamplingMode, }; use ed25519_dalek::Signer; -use fuel_core::service::config::Trigger; +use fuel_core::{ + service::{ + config::Trigger, + DbType, + }, + state::rocks_db::{ + ColumnsPolicy, + DatabaseConfig, + }, +}; use fuel_core_benches::*; use fuel_core_storage::transactional::AtomicView; use fuel_core_types::{ @@ -90,6 +99,12 @@ where test_builder.utxo_validation = true; test_builder.gas_limit = Some(10_000_000_000); test_builder.block_size_limit = Some(1_000_000_000_000); + test_builder.database_type = DbType::RocksDb; + test_builder.database_config = DatabaseConfig { + cache_capacity: Some(16 * 1024 * 1024 * 1024), + max_fds: -1, + columns_policy: ColumnsPolicy::OnCreation, + }; // spin up node let transactions: Vec = diff --git a/benches/benches/utils.rs b/benches/benches/utils.rs index 52b82cee529..d3d46efb694 100644 --- a/benches/benches/utils.rs +++ b/benches/benches/utils.rs @@ -36,7 +36,7 @@ pub fn get_state_size() -> u64 { } /// Allocates a byte array from heap and initializes it. Then points `reg` to it. -fn aloc_bytearray(reg: u8, v: [u8; S]) -> Vec { +pub fn aloc_bytearray(reg: u8, v: [u8; S]) -> Vec { let mut ops = vec![op::movi(reg, S as u32), op::aloc(reg)]; for (i, b) in v.iter().enumerate() { if *b != 0 { diff --git a/benches/benches/vm.rs b/benches/benches/vm.rs index f895290bff8..e629ccbf654 100644 --- a/benches/benches/vm.rs +++ b/benches/benches/vm.rs @@ -41,7 +41,6 @@ where let clock = quanta::Clock::new(); let original_db = vm.as_mut().database_mut().clone(); - let original_memory = vm.memory().clone(); // During block production/validation for each state, which may affect the state of the database, // we create a new storage transaction. The code here simulates the same behavior to have // the same nesting level and the same performance. @@ -53,7 +52,6 @@ where let mut total = core::time::Duration::ZERO; for _ in 0..iters { - vm.memory_mut().clone_from(&original_memory); let start = black_box(clock.raw()); match instruction { Instruction::CALL(call) => { @@ -72,7 +70,6 @@ where vm.as_mut().database_mut().reset_changes(); } *vm.as_mut().database_mut() = original_db; - *vm.memory_mut() = original_memory; total }) }); diff --git a/benches/benches/vm_set/blockchain.rs b/benches/benches/vm_set/blockchain.rs index cd51bf6dbc9..04ad9851cc8 100644 --- a/benches/benches/vm_set/blockchain.rs +++ b/benches/benches/vm_set/blockchain.rs @@ -20,7 +20,13 @@ use fuel_core::{ GenesisDatabase, }, service::Config, - state::historical_rocksdb::HistoricalRocksDB, + state::{ + historical_rocksdb::HistoricalRocksDB, + rocks_db::{ + ColumnsPolicy, + DatabaseConfig, + }, + }, }; use fuel_core_benches::*; use fuel_core_storage::{ @@ -47,8 +53,11 @@ use fuel_core_types::{ }, fuel_tx::{ ContractIdExt, + Finalizable, Input, Output, + Transaction, + TransactionBuilder, Word, }, fuel_types::*, @@ -65,6 +74,7 @@ pub struct BenchDb { db: GenesisDatabase, /// Used for RAII cleanup. Contents of this directory are deleted on drop. _tmp_dir: utils::ShallowTempDir, + latest_block: u32, } impl BenchDb { @@ -73,9 +83,12 @@ impl BenchDb { let db = HistoricalRocksDB::::default_open( tmp_dir.path(), - None, Default::default(), - -1, + DatabaseConfig { + cache_capacity: None, + max_fds: -1, + columns_policy: ColumnsPolicy::OnCreation, + }, ) .unwrap(); let db = Arc::new(db); @@ -122,18 +135,40 @@ impl BenchDb { &block.compress(&chain_config.consensus_parameters.chain_id()), ) .unwrap(); - Ok(Self { _tmp_dir: tmp_dir, db: database, + latest_block: 0, }) } + fn add_blocks(&mut self, nb_blocks: u32) { + for i in 1..=nb_blocks { + let mut block = + fuel_core::service::genesis::create_genesis_block(&Config::local_node()); + let transactions = block.transactions_mut(); + // Add a dummy transaction to the block to make the block bigger. + transactions.push(Transaction::Script( + TransactionBuilder::script(vec![], vec![1; 200_000]).finalize(), + )); + let config = Config::local_node(); + let chain_config = config.snapshot_reader.chain_config(); + self.db + .storage::() + .insert( + &i.into(), + &block.compress(&chain_config.consensus_parameters.chain_id()), + ) + .unwrap(); + } + self.latest_block = nb_blocks; + } + /// Creates a `VmDatabase` instance. fn to_vm_database(&self) -> VmStorage> { let consensus = ConsensusHeader { prev_root: Default::default(), - height: 1.into(), + height: self.latest_block.into(), time: Tai64::UNIX_EPOCH, generated: (), }; @@ -517,14 +552,25 @@ pub fn run(c: &mut Criterion) { VmBench::new(op::bhei(0x10)), ); + let mut db = + BenchDb::new(&VmBench::CONTRACT).expect("Unable to fill contract storage"); + db.add_blocks(10000); + run_group_ref( &mut c.benchmark_group("bhsh"), "bhsh", - VmBench::new(op::bhsh(0x10, RegId::ZERO)).with_prepare_script(vec![ - op::movi(0x10, Bytes32::LEN.try_into().unwrap()), - op::aloc(0x10), - op::move_(0x10, RegId::HP), - ]), + VmBench::new(op::bhsh(RegId::HP, 0x11)) + .prepend_prepare_script(vec![ + // Store number of bytes we want to alloc for the future result + op::movi(0x10, Bytes32::LEN.try_into().unwrap()), + // Allocate space for the future result + op::aloc(0x10), + // Add block height to 0x11 + op::movi(0x11, 0xc9), + ]) + // Random height + .with_height(0xca.into()) + .with_db(db.to_vm_database()), ); run_group_ref( diff --git a/benches/benches/vm_set/crypto.rs b/benches/benches/vm_set/crypto.rs index cb266a4df36..32db19b8017 100644 --- a/benches/benches/vm_set/crypto.rs +++ b/benches/benches/vm_set/crypto.rs @@ -1,3 +1,5 @@ +use crate::utils::aloc_bytearray; + use super::run_group_ref; use criterion::{ @@ -28,7 +30,7 @@ pub fn run(c: &mut Criterion) { "eck1", VmBench::new(op::eck1(RegId::HP, 0x20, 0x21)) .with_prepare_script(vec![ - op::gtf_args(0x20, 0x00, GTFArgs::ScriptData), + op::gtf_args(0x20, RegId::ZERO, GTFArgs::ScriptData), op::addi( 0x21, 0x20, @@ -55,7 +57,7 @@ pub fn run(c: &mut Criterion) { "ecr1", VmBench::new(op::ecr1(RegId::HP, 0x20, 0x21)) .with_prepare_script(vec![ - op::gtf_args(0x20, 0x00, GTFArgs::ScriptData), + op::gtf_args(0x20, RegId::ZERO, GTFArgs::ScriptData), op::addi( 0x21, 0x20, @@ -122,7 +124,7 @@ pub fn run(c: &mut Criterion) { format!("{i}"), VmBench::new(op::ed19(0x20, 0x21, RegId::ZERO, 0x10)) .with_prepare_script(vec![ - op::gtf_args(0x20, 0x00, GTFArgs::ScriptData), + op::gtf_args(0x20, RegId::ZERO, GTFArgs::ScriptData), op::addi( 0x21, 0x20, @@ -154,4 +156,61 @@ pub fn run(c: &mut Criterion) { ); } bench_ed19.finish(); + + // ecop testing mul as it's the most expensive operation + let mut points_bytearray = Vec::new(); + // X + points_bytearray.extend( + hex::decode("2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7") + .unwrap(), + ); + // Y + points_bytearray.extend( + hex::decode("21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204") + .unwrap(), + ); + // Scalar + points_bytearray.extend( + hex::decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap(), + ); + // 96 bytes = 1 point and 1 scalar + let prepare_points = aloc_bytearray::<96>(0x10, points_bytearray.try_into().unwrap()); + run_group_ref( + &mut c.benchmark_group("ecop"), + "ecop", + VmBench::new(op::ecop(0x10, RegId::ZERO, 0x01, 0x10)) + .with_prepare_script(prepare_points), + ); + + // ec pairing + let mut bench_epar = c.benchmark_group("epar"); + for i in [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] { + let mut points_bytearray = Vec::new(); + for _ in 0u32..i { + points_bytearray.extend( + hex::decode( + "0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + ) + .unwrap(), + ); + } + bench_epar.throughput(Throughput::Bytes(i as u64)); + run_group_ref( + &mut bench_epar, + format!("{i}"), + VmBench::new(op::epar(0x12, RegId::ZERO, 0x11, 0x10)) + .with_data(points_bytearray) + .with_prepare_script(vec![ + op::movi(0x11, i), + op::gtf_args(0x10, 0x00, GTFArgs::ScriptData), + ]), + ); + } + bench_epar.finish(); } diff --git a/benches/src/bin/collect.rs b/benches/src/bin/collect.rs index 3fe0b3a0850..4fb2a697eb8 100644 --- a/benches/src/bin/collect.rs +++ b/benches/src/bin/collect.rs @@ -1,6 +1,6 @@ use clap::Parser; use fuel_core_types::fuel_tx::{ - consensus_parameters::gas::GasCostsValuesV4, + consensus_parameters::gas::GasCostsValuesV5, ConsensusParameters, GasCosts, }; @@ -371,7 +371,7 @@ pub const GIT: &str = ""#, r#"";"#, r##" pub fn default_gas_costs() -> GasCostsValues { - GasCostsValuesV4 {"##, + GasCostsValuesV5 {"##, r##" }.into() } "##, @@ -495,7 +495,7 @@ impl State { ) } - fn to_gas_costs(&self) -> GasCostsValuesV4 { + fn to_gas_costs(&self) -> GasCostsValuesV5 { serde_yaml::from_value(self.to_yaml()).unwrap() } diff --git a/benches/src/db_lookup_times_utils/utils.rs b/benches/src/db_lookup_times_utils/utils.rs index 72f376e1e7e..1cbd2b0f7f7 100644 --- a/benches/src/db_lookup_times_utils/utils.rs +++ b/benches/src/db_lookup_times_utils/utils.rs @@ -5,7 +5,11 @@ use crate::db_lookup_times_utils::full_block_table::{ use anyhow::anyhow; use fuel_core::{ database::database_description::DatabaseDescription, - state::rocks_db::RocksDb, + state::rocks_db::{ + ColumnsPolicy, + DatabaseConfig, + RocksDb, + }, }; use fuel_core_storage::kv_store::{ KeyValueInspect, @@ -39,7 +43,14 @@ pub fn get_random_block_height( pub fn open_rocks_db( path: &Path, ) -> Result> { - let db = RocksDb::default_open(path, None, -1)?; + let db = RocksDb::default_open( + path, + DatabaseConfig { + cache_capacity: Some(16 * 1024 * 1024 * 1024), + max_fds: -1, + columns_policy: ColumnsPolicy::OnCreation, + }, + )?; Ok(db) } diff --git a/benches/src/default_gas_costs.rs b/benches/src/default_gas_costs.rs index ed10c1c3333..f770326ee47 100644 --- a/benches/src/default_gas_costs.rs +++ b/benches/src/default_gas_costs.rs @@ -1,14 +1,14 @@ use super::*; -use fuel_core_types::fuel_tx::consensus_parameters::gas::GasCostsValuesV4; +use fuel_core_types::fuel_tx::consensus_parameters::gas::GasCostsValuesV5; pub fn default_gas_costs() -> GasCostsValues { - GasCostsValuesV4 { + GasCostsValuesV5 { add: 2, addi: 2, and: 2, andi: 2, bal: 274, bhei: 2, - bhsh: 2, + bhsh: 32, burn: 7566, cb: 2, cfsi: 2, @@ -90,6 +90,11 @@ pub fn default_gas_costs() -> GasCostsValues { wqmm: 6, xor: 2, xori: 2, + ecop: 3500, + epar: DependentCost::HeavyOperation { + base: 69000, + gas_per_unit: 52000, + }, aloc: DependentCost::LightOperation { base: 2, units_per_gas: 35, diff --git a/benches/src/lib.rs b/benches/src/lib.rs index c84fb4e6418..da36e5b7678 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -141,6 +141,8 @@ pub struct VmBenchPrepared { pub diff: diff::Diff, } +const TX_SIZE: u64 = 64 * 1024 * 1024; + impl VmBench { pub const SALT: Salt = Salt::zeroed(); pub const CONTRACT: ContractId = ContractId::zeroed(); @@ -148,10 +150,17 @@ impl VmBench { pub fn new(instruction: Instruction) -> Self { let mut consensus_params = ConsensusParameters::default(); consensus_params.set_tx_params( - TxParameters::default().with_max_gas_per_tx(LARGE_GAS_LIMIT + 1), + TxParameters::default() + .with_max_gas_per_tx(LARGE_GAS_LIMIT + 1) + .with_max_size(TX_SIZE), ); consensus_params.set_fee_params(FeeParameters::default().with_gas_per_byte(0)); consensus_params.set_gas_costs(GasCosts::free()); + consensus_params.set_script_params( + ScriptParameters::default() + .with_max_script_length(TX_SIZE) + .with_max_script_data_length(TX_SIZE), + ); Self { params: consensus_params, @@ -398,7 +407,6 @@ impl TryFrom for VmBenchPrepared { let prepare_script = prepare_script .into_iter() .chain(iter::once(op::ret(RegId::ONE))) - .chain(iter::once(instruction)) .collect(); let mut tx = TransactionBuilder::script(prepare_script, data); @@ -531,9 +539,7 @@ impl TryFrom for VmBenchPrepared { } } - let start_vm = vm.clone(); - let original_db = vm.as_mut().database_mut().clone(); - let original_memory = vm.memory().clone(); + let vm_before_first_instruction = vm.clone(); let mut vm = vm.add_recording(); match instruction { Instruction::CALL(call) => { @@ -546,12 +552,11 @@ impl TryFrom for VmBenchPrepared { } let storage_diff = vm.storage_diff(); let mut vm = vm.remove_recording(); - let mut diff = start_vm.diff(&vm); + let mut diff = vm.rollback_to(&vm_before_first_instruction); diff += storage_diff; let diff: diff::Diff = diff.into(); vm.reset_vm_state(&diff); - *vm.as_mut().database_mut() = original_db; - *vm.memory_mut() = original_memory; + assert_eq!(vm_before_first_instruction, vm); Ok(Self { vm, diff --git a/bin/e2e-test-client/src/tests/test_data/large_state/chain_config.json b/bin/e2e-test-client/src/tests/test_data/large_state/chain_config.json index 7748984b6b1..23adc22f3fc 100644 --- a/bin/e2e-test-client/src/tests/test_data/large_state/chain_config.json +++ b/bin/e2e-test-client/src/tests/test_data/large_state/chain_config.json @@ -48,7 +48,7 @@ "andi": 2, "bal": 366, "bhei": 2, - "bhsh": 2, + "bhsh": 32, "burn": 33949, "cb": 2, "cfei": 2, @@ -129,6 +129,7 @@ "wqmm": 11, "xor": 2, "xori": 2, + "ecop": 3500, "call": { "LightOperation": { "base": 21687, @@ -237,6 +238,12 @@ "gas_per_unit": 26613 } }, + "epar": { + "HeavyOperation": { + "base": 69000, + "gas_per_unit": 52000 + } + }, "contract_root": { "LightOperation": { "base": 45, diff --git a/bin/fuel-core/Cargo.toml b/bin/fuel-core/Cargo.toml index 41d05c2b69a..880a69f9296 100644 --- a/bin/fuel-core/Cargo.toml +++ b/bin/fuel-core/Cargo.toml @@ -29,7 +29,7 @@ fuel-core = { workspace = true, features = ["wasm-executor"] } fuel-core-chain-config = { workspace = true } fuel-core-compression = { workspace = true } fuel-core-metrics = { workspace = true } -fuel-core-poa = { workspace = true } +fuel-core-shared-sequencer = { workspace = true, optional = true } fuel-core-types = { workspace = true, features = ["std"] } hex = { workspace = true } humantime = "2.1" @@ -61,9 +61,13 @@ test-case = { workspace = true } [features] default = ["env", "relayer", "rocksdb"] -aws-kms = ["dep:aws-config", "dep:aws-sdk-kms", "fuel-core-poa/aws-kms"] +aws-kms = ["dep:aws-config", "dep:aws-sdk-kms", "fuel-core-types/aws-kms"] env = ["dep:dotenvy"] p2p = ["fuel-core/p2p", "const_format"] +shared-sequencer = [ + "dep:fuel-core-shared-sequencer", + "fuel-core/shared-sequencer", +] relayer = ["fuel-core/relayer", "dep:url"] parquet = ["fuel-core-chain-config/parquet", "fuel-core-types/serde"] rocksdb = ["fuel-core/rocksdb"] @@ -74,6 +78,7 @@ production = [ "relayer", "rocksdb-production", "p2p", + "shared-sequencer", "parquet", "aws-kms", ] diff --git a/bin/fuel-core/chainspec/local-testnet/chain_config.json b/bin/fuel-core/chainspec/local-testnet/chain_config.json index 2977f4b4b08..43c2927aa34 100644 --- a/bin/fuel-core/chainspec/local-testnet/chain_config.json +++ b/bin/fuel-core/chainspec/local-testnet/chain_config.json @@ -40,14 +40,14 @@ }, "chain_id": 0, "gas_costs": { - "V4": { + "V5": { "add": 2, "addi": 2, "and": 2, "andi": 2, "bal": 274, "bhei": 2, - "bhsh": 2, + "bhsh": 32, "burn": 7566, "cb": 2, "cfsi": 2, @@ -126,6 +126,7 @@ "wqmm": 6, "xor": 2, "xori": 2, + "ecop": 3500, "aloc": { "LightOperation": { "base": 2, @@ -270,6 +271,12 @@ "gas_per_unit": 5776 } }, + "epar": { + "HeavyOperation": { + "base": 69000, + "gas_per_unit": 52000 + } + }, "contract_root": { "LightOperation": { "base": 24, @@ -297,7 +304,7 @@ "privileged_address": "9f0e19d6c2a6283a3222426ab2630d35516b1799b503f37b02105bebe1b8a3e9" } }, - "genesis_state_transition_version": 16, + "genesis_state_transition_version": 18, "consensus": { "PoAV2": { "genesis_signing_key": "e0a9fcde1b73f545252e01b30b50819eb9547d07531fa3df0385c5695736634d", diff --git a/bin/fuel-core/src/cli/rollback.rs b/bin/fuel-core/src/cli/rollback.rs index 12e18a0547b..52b7ea3b2b8 100644 --- a/bin/fuel-core/src/cli/rollback.rs +++ b/bin/fuel-core/src/cli/rollback.rs @@ -3,7 +3,10 @@ use anyhow::Context; use clap::Parser; use fuel_core::{ combined_database::CombinedDatabase, - state::historical_rocksdb::StateRewindPolicy, + state::{ + historical_rocksdb::StateRewindPolicy, + rocks_db::DatabaseConfig, + }, }; use rlimit::{ getrlimit, @@ -51,9 +54,12 @@ pub async fn exec(command: Command) -> anyhow::Result<()> { let path = command.database_path.as_path(); let db = CombinedDatabase::open( path, - 64 * 1024 * 1024, StateRewindPolicy::RewindFullRange, - command.rocksdb_max_fds, + DatabaseConfig { + cache_capacity: Some(64 * 1024 * 1024), + max_fds: command.rocksdb_max_fds, + columns_policy: Default::default(), + }, ) .map_err(Into::::into) .context(format!("failed to open combined database at path {path:?}"))?; diff --git a/bin/fuel-core/src/cli/run.rs b/bin/fuel-core/src/cli/run.rs index 06d00219b50..763ffdef897 100644 --- a/bin/fuel-core/src/cli/run.rs +++ b/bin/fuel-core/src/cli/run.rs @@ -34,6 +34,10 @@ use fuel_core::{ RelayerConsensusConfig, VMConfig, }, + state::rocks_db::{ + ColumnsPolicy, + DatabaseConfig, + }, txpool::config::{ BlackList, Config as TxPoolConfig, @@ -55,8 +59,10 @@ use fuel_core_metrics::config::{ DisableConfig, Module, }; -use fuel_core_poa::signer::SignMode; -use fuel_core_types::blockchain::header::StateTransitionBytecodeVersion; +use fuel_core_types::{ + blockchain::header::StateTransitionBytecodeVersion, + signer::SignMode, +}; use pyroscope::{ pyroscope::PyroscopeAgentRunning, PyroscopeAgent, @@ -85,11 +91,12 @@ use tracing::{ #[cfg(feature = "rocksdb")] use fuel_core::state::historical_rocksdb::StateRewindPolicy; -use super::DEFAULT_DATABASE_CACHE_SIZE; - #[cfg(feature = "p2p")] mod p2p; +#[cfg(feature = "shared-sequencer")] +mod shared_sequencer; + mod consensus; mod graphql; mod profiling; @@ -105,12 +112,8 @@ pub struct Command { pub service_name: String, /// The maximum database cache size in bytes. - #[arg( - long = "max-database-cache-size", - default_value_t = DEFAULT_DATABASE_CACHE_SIZE, - env - )] - pub max_database_cache_size: usize, + #[arg(long = "max-database-cache-size", env)] + pub max_database_cache_size: Option, #[clap( name = "DB_PATH", @@ -256,6 +259,10 @@ pub struct Command { #[cfg(feature = "p2p")] pub sync_args: p2p::SyncArgs, + #[cfg_attr(feature = "shared-sequencer", clap(flatten))] + #[cfg(feature = "shared-sequencer")] + pub shared_sequencer_args: shared_sequencer::Args, + #[arg(long = "disable-metrics", value_delimiter = ',', help = fuel_core_metrics::config::help_string(), env)] pub disabled_metrics: Vec, @@ -316,6 +323,8 @@ impl Command { p2p_args, #[cfg(feature = "p2p")] sync_args, + #[cfg(feature = "shared-sequencer")] + shared_sequencer_args, disabled_metrics, max_da_lag, max_wait_time, @@ -395,8 +404,9 @@ impl Command { // if consensus key is not configured, fallback to dev consensus key let key = default_consensus_dev_key(); warn!( - "Fuel Core is using an insecure test key for consensus. Public key: {}", - key.public_key() + "Fuel Core is using an insecure test key for consensus. Public key: {}, SecretKey: {}", + key.public_key(), + key ); consensus_signer = SignMode::Key(Secret::new(key.into())); } @@ -456,11 +466,17 @@ impl Command { let combined_db_config = CombinedDatabaseConfig { database_path, database_type, - max_database_cache_size, #[cfg(feature = "rocksdb")] - state_rewind_policy, + database_config: DatabaseConfig { + max_fds: rocksdb_max_fds, + cache_capacity: max_database_cache_size, + #[cfg(feature = "production")] + columns_policy: ColumnsPolicy::OnCreation, + #[cfg(not(feature = "production"))] + columns_policy: ColumnsPolicy::Lazy, + }, #[cfg(feature = "rocksdb")] - max_fds: rocksdb_max_fds, + state_rewind_policy, }; let block_importer = fuel_core::service::config::fuel_core_importer::Config::new( @@ -600,6 +616,8 @@ impl Command { p2p: p2p_cfg, #[cfg(feature = "p2p")] sync: sync_args.into(), + #[cfg(feature = "shared-sequencer")] + shared_sequencer: shared_sequencer_args.try_into()?, consensus_signer, name, relayer_consensus_config: verifier, diff --git a/bin/fuel-core/src/cli/run/shared_sequencer.rs b/bin/fuel-core/src/cli/run/shared_sequencer.rs new file mode 100644 index 00000000000..698a4955b05 --- /dev/null +++ b/bin/fuel-core/src/cli/run/shared_sequencer.rs @@ -0,0 +1,56 @@ +use fuel_core_types::fuel_types::Bytes32; + +#[derive(Debug, Clone, clap::Args)] +pub struct Args { + /// If set to true, new blocks will be posted to the shared sequencer chain. + #[clap(long = "enable-ss", action)] + enable: bool, + /// The frequency at which to post blocks to the shared sequencer. + #[clap(long = "ss-block-posting-frequency", env, default_value = "12s")] + block_posting_frequency: humantime::Duration, + /// The RPC address of the sequencer chain + /// (e.g. "http://127.0.0.1:26657") + #[clap(long = "ss-tendermint-api", env)] + tendermint_api: Option, + /// The REST address of the sequencer chain + /// (e.g. "http://127.0.0.1:1317") + #[clap(long = "ss-blockchain-api", env)] + blockchain_api: Option, + /// Topic to post blocks to + /// (e.g. "1111111111111111111111111111111111111111111111111111111111111111") + #[clap( + long = "ss-topic", + env, + default_value = "0000000000000000000000000000000000000000000000000000000000000000" + )] + topic: Bytes32, +} + +#[cfg(feature = "shared-sequencer")] +impl TryFrom for fuel_core_shared_sequencer::Config { + type Error = anyhow::Error; + + fn try_from(val: Args) -> anyhow::Result { + let endpoints = match (val.tendermint_api, val.blockchain_api) { + (Some(tendermint_api), Some(blockchain_api)) => { + Some(fuel_core_shared_sequencer::Endpoints { + tendermint_rpc_api: tendermint_api, + blockchain_rest_api: blockchain_api, + }) + } + (None, None) => None, + _ => { + return Err(anyhow::anyhow!( + "Both tendermint and blockchain API must be set or unset" + )) + } + }; + + Ok(fuel_core_shared_sequencer::Config { + enabled: val.enable, + block_posting_frequency: val.block_posting_frequency.into(), + endpoints, + topic: *val.topic, + }) + } +} diff --git a/bin/fuel-core/src/cli/snapshot.rs b/bin/fuel-core/src/cli/snapshot.rs index 3f562e38733..060dd9a87a2 100644 --- a/bin/fuel-core/src/cli/snapshot.rs +++ b/bin/fuel-core/src/cli/snapshot.rs @@ -6,7 +6,13 @@ use clap::{ }; use fuel_core::{ combined_database::CombinedDatabase, - state::historical_rocksdb::StateRewindPolicy, + state::{ + historical_rocksdb::StateRewindPolicy, + rocks_db::{ + ColumnsPolicy, + DatabaseConfig, + }, + }, types::fuel_types::ContractId, }; use fuel_core_chain_config::ChainConfig; @@ -209,9 +215,12 @@ fn open_db( ) -> anyhow::Result { CombinedDatabase::open( path, - capacity.unwrap_or(1024 * 1024 * 1024), StateRewindPolicy::NoRewind, - max_fds, + DatabaseConfig { + cache_capacity: Some(capacity.unwrap_or(1024 * 1024 * 1024)), + max_fds, + columns_policy: ColumnsPolicy::OnCreation, + }, ) .map_err(Into::::into) .context(format!("failed to open combined database at path {path:?}",)) diff --git a/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_configurable_block_height.snap b/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_configurable_block_height.snap index ec4e7c71b41..cb4cc418132 100644 --- a/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_configurable_block_height.snap +++ b/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_configurable_block_height.snap @@ -123,6 +123,7 @@ expression: json "wqmm": 3, "xor": 1, "xori": 1, + "ecop": 1, "call": { "LightOperation": { "base": 144, @@ -225,6 +226,12 @@ expression: json "units_per_gas": 5 } }, + "epar": { + "HeavyOperation": { + "base": 100, + "gas_per_unit": 1 + } + }, "contract_root": { "LightOperation": { "base": 75, diff --git a/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_local_testnet_config.snap b/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_local_testnet_config.snap index c3123c6bb38..61f8a7c714b 100644 --- a/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_local_testnet_config.snap +++ b/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_local_testnet_config.snap @@ -44,7 +44,7 @@ expression: json }, "chain_id": 0, "gas_costs": { - "V4": { + "V5": { "add": 1, "addi": 1, "and": 1, @@ -130,6 +130,7 @@ expression: json "wqmm": 3, "xor": 1, "xori": 1, + "ecop": 3500, "aloc": { "LightOperation": { "base": 2, @@ -274,6 +275,12 @@ expression: json "units_per_gas": 5 } }, + "epar": { + "HeavyOperation": { + "base": 69000, + "gas_per_unit": 52000 + } + }, "contract_root": { "LightOperation": { "base": 75, @@ -301,7 +308,7 @@ expression: json "privileged_address": "0000000000000000000000000000000000000000000000000000000000000000" } }, - "genesis_state_transition_version": 16, + "genesis_state_transition_version": 18, "consensus": { "PoAV2": { "genesis_signing_key": "22ec92c3105c942a6640bdc4e4907286ec4728e8cfc0d8ac59aad4d8e1ccaefb", diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_configurable_block_height.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_configurable_block_height.snap index f03cb0053c4..d3cd9c62f3c 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_configurable_block_height.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_configurable_block_height.snap @@ -122,6 +122,7 @@ expression: json "wqmm": 3, "xor": 1, "xori": 1, + "ecop": 1, "call": { "LightOperation": { "base": 144, @@ -230,6 +231,12 @@ expression: json "units_per_gas": 5 } }, + "epar": { + "HeavyOperation": { + "base": 100, + "gas_per_unit": 1 + } + }, "contract_root": { "LightOperation": { "base": 75, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_balances.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_balances.snap index 24b18202318..201905633ca 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_balances.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_balances.snap @@ -136,6 +136,7 @@ expression: json "wqmm": 3, "xor": 1, "xori": 1, + "ecop": 1, "call": { "LightOperation": { "base": 144, @@ -244,6 +245,12 @@ expression: json "units_per_gas": 5 } }, + "epar": { + "HeavyOperation": { + "base": 100, + "gas_per_unit": 1 + } + }, "contract_root": { "LightOperation": { "base": 75, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_state.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_state.snap index 00a0bac4d9f..c454ad90979 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_state.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_state.snap @@ -136,6 +136,7 @@ expression: json "wqmm": 3, "xor": 1, "xori": 1, + "ecop": 1, "call": { "LightOperation": { "base": 144, @@ -244,6 +245,12 @@ expression: json "units_per_gas": 5 } }, + "epar": { + "HeavyOperation": { + "base": 100, + "gas_per_unit": 1 + } + }, "contract_root": { "LightOperation": { "base": 75, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_tx_pointer.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_tx_pointer.snap index 6b1d97d832b..8497696b415 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_tx_pointer.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_tx_pointer.snap @@ -132,6 +132,7 @@ expression: json "wqmm": 3, "xor": 1, "xori": 1, + "ecop": 1, "call": { "LightOperation": { "base": 144, @@ -240,6 +241,12 @@ expression: json "units_per_gas": 5 } }, + "epar": { + "HeavyOperation": { + "base": 100, + "gas_per_unit": 1 + } + }, "contract_root": { "LightOperation": { "base": 75, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_utxo_id.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_utxo_id.snap index 00868dc0c0e..877e41f9f83 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_utxo_id.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_utxo_id.snap @@ -132,6 +132,7 @@ expression: json "wqmm": 3, "xor": 1, "xori": 1, + "ecop": 1, "call": { "LightOperation": { "base": 144, @@ -240,6 +241,12 @@ expression: json "units_per_gas": 5 } }, + "epar": { + "HeavyOperation": { + "base": 100, + "gas_per_unit": 1 + } + }, "contract_root": { "LightOperation": { "base": 75, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_local_testnet_config.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_local_testnet_config.snap index 404cf2275d6..93a8b9d2628 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_local_testnet_config.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_local_testnet_config.snap @@ -122,6 +122,7 @@ expression: json "wqmm": 3, "xor": 1, "xori": 1, + "ecop": 1, "call": { "LightOperation": { "base": 144, @@ -230,6 +231,12 @@ expression: json "units_per_gas": 5 } }, + "epar": { + "HeavyOperation": { + "base": 100, + "gas_per_unit": 1 + } + }, "contract_root": { "LightOperation": { "base": 75, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_coin_state.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_coin_state.snap index 735fa9517d3..c68e1b694db 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_coin_state.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_coin_state.snap @@ -135,6 +135,7 @@ expression: json "wqmm": 3, "xor": 1, "xori": 1, + "ecop": 1, "call": { "LightOperation": { "base": 144, @@ -243,6 +244,12 @@ expression: json "units_per_gas": 5 } }, + "epar": { + "HeavyOperation": { + "base": 100, + "gas_per_unit": 1 + } + }, "contract_root": { "LightOperation": { "base": 75, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_contract.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_contract.snap index ba531bfbb32..c4d54dddb49 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_contract.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_contract.snap @@ -130,6 +130,7 @@ expression: json "wqmm": 3, "xor": 1, "xori": 1, + "ecop": 1, "call": { "LightOperation": { "base": 144, @@ -238,6 +239,12 @@ expression: json "units_per_gas": 5 } }, + "epar": { + "HeavyOperation": { + "base": 100, + "gas_per_unit": 1 + } + }, "contract_root": { "LightOperation": { "base": 75, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_message_state.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_message_state.snap index b5d981ce718..ea9d4ae8a21 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_message_state.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_message_state.snap @@ -134,6 +134,7 @@ expression: json "wqmm": 3, "xor": 1, "xori": 1, + "ecop": 1, "call": { "LightOperation": { "base": 144, @@ -242,6 +243,12 @@ expression: json "units_per_gas": 5 } }, + "epar": { + "HeavyOperation": { + "base": 100, + "gas_per_unit": 1 + } + }, "contract_root": { "LightOperation": { "base": 75, diff --git a/crates/client/assets/schema.sdl b/crates/client/assets/schema.sdl index 1c291a567fd..ca280ebc97e 100644 --- a/crates/client/assets/schema.sdl +++ b/crates/client/assets/schema.sdl @@ -443,6 +443,7 @@ type GasCosts { wqmm: U64! xor: U64! xori: U64! + ecop: U64 alocDependentCost: DependentCost! bldd: DependentCost bsiz: DependentCost @@ -467,6 +468,7 @@ type GasCosts { smo: DependentCost! srwq: DependentCost! swwq: DependentCost! + epar: DependentCost contractRoot: DependentCost! stateRoot: DependentCost! vmInitialization: DependentCost! diff --git a/crates/client/src/client/schema/chain.rs b/crates/client/src/client/schema/chain.rs index 31129c74491..c0efa92a27c 100644 --- a/crates/client/src/client/schema/chain.rs +++ b/crates/client/src/client/schema/chain.rs @@ -303,6 +303,7 @@ pub struct GasCosts { pub wqmm: U64, pub xor: U64, pub xori: U64, + pub ecop: Option, pub aloc_dependent_cost: DependentCost, pub bsiz: Option, @@ -328,6 +329,7 @@ pub struct GasCosts { pub smo: DependentCost, pub srwq: DependentCost, pub swwq: DependentCost, + pub epar: Option, // Non-opcodes prices pub contract_root: DependentCost, @@ -350,7 +352,7 @@ impl TryFrom for fuel_core_types::fuel_tx::GasCosts { fn try_from(value: GasCosts) -> Result { match value.version { GasCostsVersion::V1 => Ok(fuel_core_types::fuel_tx::GasCosts::new( - fuel_core_types::fuel_tx::consensus_parameters::gas::GasCostsValuesV4 { + fuel_core_types::fuel_tx::consensus_parameters::gas::GasCostsValuesV5 { add: value.add.into(), addi: value.addi.into(), and: value.and.into(), @@ -436,6 +438,7 @@ impl TryFrom for fuel_core_types::fuel_tx::GasCosts { wqmm: value.wqmm.into(), xor: value.xor.into(), xori: value.xori.into(), + ecop: value.ecop.map(Into::into).unwrap_or(0), aloc: value.aloc_dependent_cost.into(), bsiz: value.bsiz.map(Into::into).unwrap_or(fuel_core_types::fuel_tx::consensus_parameters::DependentCost::free()), @@ -461,6 +464,7 @@ impl TryFrom for fuel_core_types::fuel_tx::GasCosts { smo: value.smo.into(), srwq: value.srwq.into(), swwq: value.swwq.into(), + epar: value.epar.map(Into::into).unwrap_or(fuel_core_types::fuel_tx::consensus_parameters::DependentCost::free()), contract_root: value.contract_root.into(), state_root: value.state_root.into(), vm_initialization: value.vm_initialization.into(), diff --git a/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap b/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap index 4b97aca83d3..5312de603d1 100644 --- a/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap +++ b/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap @@ -1,6 +1,7 @@ --- source: crates/client/src/client/schema/chain.rs expression: operation.query +snapshot_kind: text --- query ChainQuery { chain { @@ -165,6 +166,7 @@ query ChainQuery { wqmm xor xori + ecop alocDependentCost { __typename ... on LightOperation { @@ -429,6 +431,17 @@ query ChainQuery { gasPerUnit } } + epar { + __typename + ... on LightOperation { + base + unitsPerGas + } + ... on HeavyOperation { + base + gasPerUnit + } + } contractRoot { __typename ... on LightOperation { diff --git a/crates/fuel-core/Cargo.toml b/crates/fuel-core/Cargo.toml index 8e3230df1fc..bc8b72d310d 100644 --- a/crates/fuel-core/Cargo.toml +++ b/crates/fuel-core/Cargo.toml @@ -20,6 +20,7 @@ async-graphql-value = "7.0.11" async-trait = { workspace = true } axum = { workspace = true } clap = { workspace = true, features = ["derive"] } +cosmrs = { version = "0.21", optional = true } derive_more = { version = "0.99" } enum-iterator = { workspace = true } fuel-core-chain-config = { workspace = true, features = ["std"] } @@ -35,6 +36,7 @@ fuel-core-poa = { workspace = true } fuel-core-producer = { workspace = true } fuel-core-relayer = { workspace = true, optional = true } fuel-core-services = { workspace = true } +fuel-core-shared-sequencer = { workspace = true, optional = true } fuel-core-storage = { workspace = true } fuel-core-sync = { workspace = true, optional = true } fuel-core-txpool = { workspace = true } @@ -93,6 +95,7 @@ smt = [ ] p2p = ["dep:fuel-core-p2p", "dep:fuel-core-sync"] relayer = ["dep:fuel-core-relayer"] +shared-sequencer = ["dep:fuel-core-shared-sequencer", "dep:cosmrs"] rocksdb = ["dep:rocksdb", "dep:tempfile", "dep:num_cpus", "dep:postcard"] test-helpers = [ "fuel-core-database/test-helpers", @@ -102,6 +105,7 @@ test-helpers = [ "fuel-core-compression/test-helpers", "fuel-core-txpool/test-helpers", "fuel-core-services/test-helpers", + "fuel-core-shared-sequencer?/test-helpers", "fuel-core-importer/test-helpers", ] # features to enable in production, but increase build times diff --git a/crates/fuel-core/proptest-regressions/service/adapters.txt b/crates/fuel-core/proptest-regressions/service/adapters.txt new file mode 100644 index 00000000000..b83038b4a5f --- /dev/null +++ b/crates/fuel-core/proptest-regressions/service/adapters.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 87eb4d9c7c90c2a0ee11edef3ee1b01303db0a6ba1fd34d7fc6146a2c36387f4 # shrinks to gas_price = 1, starting_height = 0, block_horizon = 1, percentage = 100 diff --git a/crates/fuel-core/src/combined_database.rs b/crates/fuel-core/src/combined_database.rs index c0b6d291af1..b8ff591e274 100644 --- a/crates/fuel-core/src/combined_database.rs +++ b/crates/fuel-core/src/combined_database.rs @@ -1,5 +1,9 @@ #[cfg(feature = "rocksdb")] -use crate::state::historical_rocksdb::StateRewindPolicy; +use crate::state::{ + historical_rocksdb::StateRewindPolicy, + rocks_db::DatabaseConfig, +}; + use crate::{ database::{ database_description::{ @@ -36,11 +40,9 @@ use std::path::PathBuf; pub struct CombinedDatabaseConfig { pub database_path: PathBuf, pub database_type: DbType, - pub max_database_cache_size: usize, #[cfg(feature = "rocksdb")] + pub database_config: DatabaseConfig, pub state_rewind_policy: StateRewindPolicy, - #[cfg(feature = "rocksdb")] - pub max_fds: i32, } /// A database that combines the on-chain, off-chain and relayer databases into one entity. @@ -79,24 +81,49 @@ impl CombinedDatabase { #[cfg(feature = "rocksdb")] pub fn open( path: &std::path::Path, - capacity: usize, state_rewind_policy: StateRewindPolicy, - max_fds: i32, + database_config: DatabaseConfig, ) -> crate::database::Result { // Split the fds in equitable manner between the databases - let max_fds = match max_fds { + + let max_fds = match database_config.max_fds { -1 => -1, - _ => max_fds.saturating_div(4), + _ => database_config.max_fds.saturating_div(4), }; + // TODO: Use different cache sizes for different databases - let on_chain = - Database::open_rocksdb(path, capacity, state_rewind_policy, max_fds)?; - let off_chain = - Database::open_rocksdb(path, capacity, state_rewind_policy, max_fds)?; - let relayer = - Database::open_rocksdb(path, capacity, StateRewindPolicy::NoRewind, max_fds)?; - let gas_price = - Database::open_rocksdb(path, capacity, state_rewind_policy, max_fds)?; + let on_chain = Database::open_rocksdb( + path, + state_rewind_policy, + DatabaseConfig { + max_fds, + ..database_config + }, + )?; + let off_chain = Database::open_rocksdb( + path, + state_rewind_policy, + DatabaseConfig { + max_fds, + ..database_config + }, + )?; + let relayer = Database::open_rocksdb( + path, + StateRewindPolicy::NoRewind, + DatabaseConfig { + max_fds, + ..database_config + }, + )?; + let gas_price = Database::open_rocksdb( + path, + state_rewind_policy, + DatabaseConfig { + max_fds, + ..database_config + }, + )?; Ok(Self { on_chain, off_chain, @@ -109,10 +136,11 @@ impl CombinedDatabase { #[cfg(feature = "rocksdb")] pub fn temp_database_with_state_rewind_policy( state_rewind_policy: StateRewindPolicy, + database_config: DatabaseConfig, ) -> DatabaseResult { Ok(Self { - on_chain: Database::rocksdb_temp(state_rewind_policy)?, - off_chain: Database::rocksdb_temp(state_rewind_policy)?, + on_chain: Database::rocksdb_temp(state_rewind_policy, database_config)?, + off_chain: Database::rocksdb_temp(state_rewind_policy, database_config)?, relayer: Default::default(), gas_price: Default::default(), }) @@ -129,19 +157,19 @@ impl CombinedDatabase { ); CombinedDatabase::temp_database_with_state_rewind_policy( config.state_rewind_policy, + config.database_config, )? } else { tracing::info!( - "Opening database {:?} with cache size \"{}\" and state rewind policy \"{:?}\"", + "Opening database {:?} with cache size \"{:?}\" and state rewind policy \"{:?}\"", config.database_path, - config.max_database_cache_size, + config.database_config.cache_capacity, config.state_rewind_policy, ); CombinedDatabase::open( &config.database_path, - config.max_database_cache_size, config.state_rewind_policy, - config.max_fds, + config.database_config, )? } } diff --git a/crates/fuel-core/src/database.rs b/crates/fuel-core/src/database.rs index 41d0451f221..3886062e4fe 100644 --- a/crates/fuel-core/src/database.rs +++ b/crates/fuel-core/src/database.rs @@ -78,7 +78,10 @@ use crate::state::{ HistoricalRocksDB, StateRewindPolicy, }, - rocks_db::RocksDb, + rocks_db::{ + DatabaseConfig, + RocksDb, + }, }; #[cfg(feature = "rocksdb")] use std::path::Path; @@ -201,16 +204,27 @@ where #[cfg(feature = "rocksdb")] pub fn open_rocksdb( path: &Path, - capacity: impl Into>, state_rewind_policy: StateRewindPolicy, - max_fds: i32, + database_config: DatabaseConfig, ) -> Result { + let db = + Self::open_as_historical_rocksdb(path, state_rewind_policy, database_config)?; + + Ok(Self::new(Arc::new(db))) + } + + #[cfg(feature = "rocksdb")] + pub fn open_as_historical_rocksdb( + path: &Path, + state_rewind_policy: StateRewindPolicy, + database_config: DatabaseConfig, + ) -> Result> { use anyhow::Context; + let db = HistoricalRocksDB::::default_open( path, - capacity.into(), state_rewind_policy, - max_fds, + database_config, ) .map_err(Into::::into) .with_context(|| { @@ -220,7 +234,7 @@ where ) })?; - Ok(Self::new(Arc::new(db))) + Ok(db) } /// Converts the regular database to an unchecked database. @@ -255,9 +269,14 @@ where } #[cfg(feature = "rocksdb")] - pub fn rocksdb_temp(rewind_policy: StateRewindPolicy) -> Result { - let db = RocksDb::>::default_open_temp(None)?; - let historical_db = HistoricalRocksDB::new(db, rewind_policy)?; + pub fn rocksdb_temp( + state_rewind_policy: StateRewindPolicy, + database_config: DatabaseConfig, + ) -> Result { + let db = RocksDb::>::default_open_temp_with_params( + database_config, + )?; + let historical_db = HistoricalRocksDB::new(db, state_rewind_policy)?; let data = Arc::new(historical_db); Ok(Self::from_storage(DataSource::new(data, Stage::default()))) } @@ -278,8 +297,15 @@ where } #[cfg(feature = "rocksdb")] { - Self::rocksdb_temp(StateRewindPolicy::NoRewind) - .expect("Failed to create a temporary database") + Self::rocksdb_temp( + StateRewindPolicy::NoRewind, + DatabaseConfig { + cache_capacity: None, + max_fds: 512, + columns_policy: Default::default(), + }, + ) + .expect("Failed to create a temporary database") } } } @@ -1107,9 +1133,8 @@ mod tests { let db = Database::::open_rocksdb( temp_dir.path(), - 1024 * 1024 * 1024, Default::default(), - 512, + DatabaseConfig::config_for_tests(), ) .unwrap(); // rocks db fails diff --git a/crates/fuel-core/src/graphql_api/database.rs b/crates/fuel-core/src/graphql_api/database.rs index 6feaaabc5dc..36f02562ca6 100644 --- a/crates/fuel-core/src/graphql_api/database.rs +++ b/crates/fuel-core/src/graphql_api/database.rs @@ -261,8 +261,13 @@ impl StorageSize for ReadView { } impl StorageRead for ReadView { - fn read(&self, key: &BlobId, buf: &mut [u8]) -> Result, Self::Error> { - StorageRead::::read(self.on_chain.as_ref(), key, buf) + fn read( + &self, + key: &BlobId, + offset: usize, + buf: &mut [u8], + ) -> Result, Self::Error> { + StorageRead::::read(self.on_chain.as_ref(), key, offset, buf) } fn read_alloc(&self, key: &BlobId) -> Result>, Self::Error> { diff --git a/crates/fuel-core/src/graphql_api/indexation.rs b/crates/fuel-core/src/graphql_api/indexation.rs index 1b490c2f13d..d515504863c 100644 --- a/crates/fuel-core/src/graphql_api/indexation.rs +++ b/crates/fuel-core/src/graphql_api/indexation.rs @@ -238,6 +238,7 @@ mod tests { MessageBalances, }, }, + state::rocks_db::DatabaseConfig, }; impl PartialEq for IndexationError { @@ -352,9 +353,12 @@ mod tests { fn balances_enabled_flag_is_respected() { use tempfile::TempDir; let tmp_dir = TempDir::new().unwrap(); - let mut db: Database = - Database::open_rocksdb(tmp_dir.path(), None, Default::default(), 512) - .unwrap(); + let mut db: Database = Database::open_rocksdb( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let mut tx = db.write_transaction(); const BALANCES_ARE_DISABLED: bool = false; @@ -409,9 +413,12 @@ mod tests { fn coins() { use tempfile::TempDir; let tmp_dir = TempDir::new().unwrap(); - let mut db: Database = - Database::open_rocksdb(tmp_dir.path(), None, Default::default(), 512) - .unwrap(); + let mut db: Database = Database::open_rocksdb( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let mut tx = db.write_transaction(); const BALANCES_ARE_ENABLED: bool = true; @@ -481,9 +488,12 @@ mod tests { fn messages() { use tempfile::TempDir; let tmp_dir = TempDir::new().unwrap(); - let mut db: Database = - Database::open_rocksdb(tmp_dir.path(), None, Default::default(), 512) - .unwrap(); + let mut db: Database = Database::open_rocksdb( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let mut tx = db.write_transaction(); const BALANCES_ARE_ENABLED: bool = true; @@ -589,9 +599,12 @@ mod tests { fn coin_balance_overflow_does_not_error() { use tempfile::TempDir; let tmp_dir = TempDir::new().unwrap(); - let mut db: Database = - Database::open_rocksdb(tmp_dir.path(), None, Default::default(), 512) - .unwrap(); + let mut db: Database = Database::open_rocksdb( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let mut tx = db.write_transaction(); const BALANCES_ARE_ENABLED: bool = true; @@ -623,9 +636,12 @@ mod tests { fn message_balance_overflow_does_not_error() { use tempfile::TempDir; let tmp_dir = TempDir::new().unwrap(); - let mut db: Database = - Database::open_rocksdb(tmp_dir.path(), None, Default::default(), 512) - .unwrap(); + let mut db: Database = Database::open_rocksdb( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let mut tx = db.write_transaction(); const BALANCES_ARE_ENABLED: bool = true; @@ -661,9 +677,12 @@ mod tests { fn coin_balance_underflow_causes_error() { use tempfile::TempDir; let tmp_dir = TempDir::new().unwrap(); - let mut db: Database = - Database::open_rocksdb(tmp_dir.path(), None, Default::default(), 512) - .unwrap(); + let mut db: Database = Database::open_rocksdb( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let mut tx = db.write_transaction(); const BALANCES_ARE_ENABLED: bool = true; diff --git a/crates/fuel-core/src/p2p_test_helpers.rs b/crates/fuel-core/src/p2p_test_helpers.rs index 3a5f6e78009..e8e184d1877 100644 --- a/crates/fuel-core/src/p2p_test_helpers.rs +++ b/crates/fuel-core/src/p2p_test_helpers.rs @@ -34,7 +34,6 @@ use fuel_core_p2p::{ }; use fuel_core_poa::{ ports::BlockImporter, - signer::SignMode, Trigger, }; use fuel_core_storage::{ @@ -61,6 +60,7 @@ use fuel_core_types::{ }, secrecy::Secret, services::p2p::GossipsubMessageAcceptance, + signer::SignMode, }; use futures::StreamExt; use rand::{ diff --git a/crates/fuel-core/src/query/message.rs b/crates/fuel-core/src/query/message.rs index 07d2cd9ab56..03b92d4c652 100644 --- a/crates/fuel-core/src/query/message.rs +++ b/crates/fuel-core/src/query/message.rs @@ -115,9 +115,6 @@ pub trait MessageProofData { /// Get the block. fn block(&self, id: &BlockHeight) -> StorageResult; - /// Return all receipts in the given transaction. - fn receipts(&self, transaction_id: &TxId) -> StorageResult>; - /// Get the status of a transaction. fn transaction_status( &self, @@ -138,10 +135,6 @@ impl MessageProofData for ReadView { self.block(id) } - fn receipts(&self, transaction_id: &TxId) -> StorageResult> { - self.receipts(transaction_id) - } - fn transaction_status( &self, transaction_id: &TxId, @@ -165,34 +158,28 @@ pub fn message_proof( desired_nonce: Nonce, commit_block_height: BlockHeight, ) -> StorageResult { - // Check if the receipts for this transaction actually contain this nonce or exit. - let (sender, recipient, nonce, amount, data) = database - .receipts(&transaction_id)? - .into_iter() - .find_map(|r| match r { - Receipt::MessageOut { - sender, - recipient, - nonce, - amount, - data, - .. - } if r.nonce() == Some(&desired_nonce) => { - Some((sender, recipient, nonce, amount, data)) - } - _ => None, - }) - .ok_or::( - anyhow::anyhow!("Desired `nonce` missing in transaction receipts").into(), - )?; - - let Some(data) = data else { - return Err(anyhow::anyhow!("Output message doesn't contain any `data`").into()) - }; - // Get the block id from the transaction status if it's ready. - let message_block_height = match database.transaction_status(&transaction_id) { - Ok(TransactionStatus::Success { block_height, .. }) => block_height, + let (message_block_height, (sender, recipient, nonce, amount, data)) = match database.transaction_status(&transaction_id) { + Ok(TransactionStatus::Success { block_height, receipts, .. }) => ( + block_height, + receipts.into_iter() + .find_map(|r| match r { + Receipt::MessageOut { + sender, + recipient, + nonce, + amount, + data, + .. + } if r.nonce() == Some(&desired_nonce) => { + Some((sender, recipient, nonce, amount, data)) + } + _ => None, + }) + .ok_or::( + anyhow::anyhow!("Desired `nonce` missing in transaction receipts").into(), + )? + ), Ok(TransactionStatus::Submitted { .. }) => { return Err(anyhow::anyhow!( "Unable to obtain the message block height. The transaction has not been processed yet" @@ -219,6 +206,10 @@ pub fn message_proof( } }; + let Some(data) = data else { + return Err(anyhow::anyhow!("Output message doesn't contain any `data`").into()) + }; + // Get the message fuel block header. let (message_block_header, message_block_txs) = match database.block(&message_block_height) { @@ -278,7 +269,11 @@ fn message_receipts_proof( // Get the message receipts from the block. let leaves: Vec> = message_block_txs .iter() - .map(|id| database.receipts(id)) + .filter_map(|id| match database.transaction_status(id) { + Ok(TransactionStatus::Success { receipts, .. }) => Some(Ok(receipts)), + Ok(_) => None, + Err(err) => Some(Err(err)), + }) .try_collect()?; let leaves = leaves.into_iter() // Flatten the receipts after filtering on output messages @@ -337,3 +332,173 @@ pub fn message_status( Ok(MessageStatus::not_found()) } } + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use fuel_core_storage::not_found; + use fuel_core_types::{ + blockchain::block::CompressedBlock, + entities::relayer::message::MerkleProof, + fuel_tx::{ + Address, + Bytes32, + Receipt, + TxId, + }, + fuel_types::{ + BlockHeight, + Nonce, + }, + services::txpool::TransactionStatus, + tai64::Tai64, + }; + + use super::{ + message_proof, + MessageProofData, + }; + + pub struct FakeDB { + pub blocks: HashMap, + pub transaction_statuses: HashMap, + pub receipts: HashMap>, + } + + impl FakeDB { + fn new() -> Self { + Self { + blocks: HashMap::new(), + transaction_statuses: HashMap::new(), + receipts: HashMap::new(), + } + } + + fn insert_block(&mut self, block_height: BlockHeight, block: CompressedBlock) { + self.blocks.insert(block_height, block); + } + + fn insert_transaction_status( + &mut self, + transaction_id: TxId, + status: TransactionStatus, + ) { + self.transaction_statuses.insert(transaction_id, status); + } + + fn insert_receipts(&mut self, transaction_id: TxId, receipts: Vec) { + self.receipts.insert(transaction_id, receipts); + } + } + + impl MessageProofData for FakeDB { + fn block(&self, id: &BlockHeight) -> fuel_core_storage::Result { + self.blocks.get(id).cloned().ok_or(not_found!("Block")) + } + + fn transaction_status( + &self, + transaction_id: &TxId, + ) -> fuel_core_storage::Result { + self.transaction_statuses + .get(transaction_id) + .cloned() + .ok_or(not_found!("Transaction status")) + } + + fn block_history_proof( + &self, + _message_block_height: &BlockHeight, + _commit_block_height: &BlockHeight, + ) -> fuel_core_storage::Result { + // Unused in current tests + Ok(MerkleProof::default()) + } + } + + // Test will try to get the message receipt proof with a block with only valid transactions + // Then add an invalid transaction and check if the proof is still the same (meaning the invalid transaction was ignored) + #[test] + fn test_message_proof_ignore_failed() { + // Create a fake database + let mut database = FakeDB::new(); + + // Given + // Create a block with a valid transaction and receipts + let mut block = CompressedBlock::default(); + let block_height: BlockHeight = BlockHeight::new(1); + block.header_mut().set_block_height(block_height); + let valid_tx_id = Bytes32::new([1; 32]); + let mut valid_tx_receipts = vec![]; + for i in 0..100 { + valid_tx_receipts.push(Receipt::MessageOut { + sender: Address::default(), + recipient: Address::default(), + amount: 0, + nonce: 0.into(), + len: 32, + digest: Bytes32::default(), + data: Some(vec![i; 32]), + }); + } + block.transactions_mut().push(valid_tx_id); + database.insert_block(block_height, block.clone()); + database.insert_transaction_status( + valid_tx_id, + TransactionStatus::Success { + time: Tai64::UNIX_EPOCH, + block_height, + receipts: valid_tx_receipts.clone(), + total_fee: 0, + total_gas: 0, + result: None, + }, + ); + database.insert_receipts(valid_tx_id, valid_tx_receipts.clone()); + + // Get the message proof with the valid transaction + let message_proof_valid_tx = + message_proof(&database, valid_tx_id, Nonce::default(), block_height) + .unwrap(); + + // Add an invalid transaction with receipts to the block + let invalid_tx_id = Bytes32::new([2; 32]); + block.transactions_mut().push(invalid_tx_id); + database.insert_block(block_height, block.clone()); + let mut invalid_tx_receipts = vec![]; + for i in 0..100 { + invalid_tx_receipts.push(Receipt::MessageOut { + sender: Address::default(), + recipient: Address::default(), + amount: 0, + nonce: 0.into(), + len: 33, + digest: Bytes32::default(), + data: Some(vec![i; 33]), + }); + } + database.insert_transaction_status( + invalid_tx_id, + TransactionStatus::Failed { + time: Tai64::UNIX_EPOCH, + block_height, + result: None, + total_fee: 0, + total_gas: 0, + receipts: invalid_tx_receipts.clone(), + }, + ); + database.insert_receipts(invalid_tx_id, invalid_tx_receipts.clone()); + + // When + // Get the message proof with the same message id + let message_proof_invalid_tx = + message_proof(&database, valid_tx_id, Nonce::default(), block_height) + .unwrap(); + + // Then + // The proof should be the same because the invalid transaction was ignored + assert_eq!(message_proof_valid_tx, message_proof_invalid_tx); + } +} diff --git a/crates/fuel-core/src/query/message/test.rs b/crates/fuel-core/src/query/message/test.rs index 447e1c01a78..7c9b217aa44 100644 --- a/crates/fuel-core/src/query/message/test.rs +++ b/crates/fuel-core/src/query/message/test.rs @@ -65,7 +65,6 @@ mockall::mock! { message_block_height: &BlockHeight, commit_block_height: &BlockHeight, ) -> StorageResult; - fn receipts(&self, transaction_id: &TxId) -> StorageResult>; fn transaction_status(&self, transaction_id: &TxId) -> StorageResult; } } @@ -97,16 +96,6 @@ async fn can_build_message_proof() { let mut data = MockProofDataStorage::new(); let mut count = 0; - data.expect_receipts().returning(move |txn_id| { - if *txn_id == transaction_id { - Ok(receipts.to_vec()) - } else { - let r = other_receipts[count..=count].to_vec(); - count += 1; - Ok(r) - } - }); - let commit_block_header = PartialBlockHeader { application: ApplicationHeader { da_height: 0u64.into(), @@ -158,18 +147,23 @@ async fn can_build_message_proof() { }); let message_block_height = *message_block.header().height(); - data.expect_transaction_status() - .with(eq(transaction_id)) - .returning(move |_| { - Ok(TransactionStatus::Success { - block_height: message_block_height, - time: Tai64::UNIX_EPOCH, - result: None, - receipts: vec![], - total_gas: 0, - total_fee: 0, - }) - }); + data.expect_transaction_status().returning(move |tx_id| { + let receipts = if *tx_id == transaction_id { + receipts.to_vec() + } else { + let r = other_receipts[count..=count].to_vec(); + count += 1; + r + }; + Ok(TransactionStatus::Success { + block_height: message_block_height, + time: Tai64::UNIX_EPOCH, + result: None, + receipts, + total_gas: 0, + total_fee: 0, + }) + }); data.expect_block().times(2).returning({ let commit_block = commit_block.clone(); diff --git a/crates/fuel-core/src/schema/chain.rs b/crates/fuel-core/src/schema/chain.rs index 9acdbb01940..830f281c69f 100644 --- a/crates/fuel-core/src/schema/chain.rs +++ b/crates/fuel-core/src/schema/chain.rs @@ -280,7 +280,8 @@ impl GasCosts { GasCostsValues::V1(_) | GasCostsValues::V2(_) | GasCostsValues::V3(_) - | GasCostsValues::V4(_) => GasCostsVersion::V1, + | GasCostsValues::V4(_) + | GasCostsValues::V5(_) => GasCostsVersion::V1, } } @@ -636,6 +637,10 @@ impl GasCosts { self.0.xori().into() } + async fn ecop(&self) -> Option { + self.0.ecop().ok().map(Into::into) + } + async fn aloc_dependent_cost(&self) -> DependentCost { self.0.aloc().into() } @@ -732,6 +737,10 @@ impl GasCosts { self.0.swwq().into() } + async fn epar(&self) -> Option { + self.0.epar().ok().map(Into::into) + } + // Non-opcode prices async fn contract_root(&self) -> DependentCost { diff --git a/crates/fuel-core/src/schema/dap.rs b/crates/fuel-core/src/schema/dap.rs index 7e920f3ead2..808591fa0c3 100644 --- a/crates/fuel-core/src/schema/dap.rs +++ b/crates/fuel-core/src/schema/dap.rs @@ -132,7 +132,12 @@ impl ConcreteStorage { let fee_params = params.fee_params(); let ready_tx = checked_tx - .into_ready(GAS_PRICE, gas_costs, fee_params) + .into_ready( + GAS_PRICE, + gas_costs, + fee_params, + Some(vm_database.block_height()?), + ) .map_err(|e| { anyhow!("Failed to apply dynamic values to checked tx: {:?}", e) })?; @@ -176,7 +181,12 @@ impl ConcreteStorage { let fee_params = params.fee_params(); let ready_tx = checked_tx - .into_ready(GAS_PRICE, gas_costs, fee_params) + .into_ready( + GAS_PRICE, + gas_costs, + fee_params, + Some(vm_database.block_height()?), + ) .map_err(|e| { anyhow!("Failed to apply dynamic values to checked tx: {:?}", e) })?; @@ -456,7 +466,12 @@ impl DapMutation { match checked_tx { CheckedTransaction::Script(script) => { let ready_tx = script - .into_ready(GAS_PRICE, gas_costs, fee_params) + .into_ready( + GAS_PRICE, + gas_costs, + fee_params, + Some(vm.as_ref().block_height()?), + ) .map_err(|e| { anyhow!("Failed to apply dynamic values to checked tx: {:?}", e) })?; diff --git a/crates/fuel-core/src/service.rs b/crates/fuel-core/src/service.rs index 1f751f1de5e..535ebbba86c 100644 --- a/crates/fuel-core/src/service.rs +++ b/crates/fuel-core/src/service.rs @@ -519,6 +519,10 @@ mod tests { // p2p & sync expected_services += 2; } + #[cfg(feature = "shared-sequencer")] + { + expected_services += 1; + } // # Dev-note: Update the `expected_services` when we add/remove a new/old service. assert_eq!(i, expected_services); diff --git a/crates/fuel-core/src/service/adapters.rs b/crates/fuel-core/src/service/adapters.rs index f55d4572335..cb6c5301115 100644 --- a/crates/fuel-core/src/service/adapters.rs +++ b/crates/fuel-core/src/service/adapters.rs @@ -1,13 +1,25 @@ +use crate::{ + database::{ + database_description::relayer::Relayer, + Database, + }, + fuel_core_graphql_api::ports::GasPriceEstimate, + service::{ + sub_services::{ + BlockProducerService, + TxPoolSharedState, + }, + vm_pool::MemoryPool, + }, +}; use fuel_core_consensus_module::{ block_verifier::Verifier, RelayerConsensusConfig, }; use fuel_core_executor::executor::OnceTransactionsSource; +use fuel_core_gas_price_service::v1::service::LatestGasPrice; use fuel_core_importer::ImporterResult; -use fuel_core_poa::{ - ports::BlockSigner, - signer::SignMode, -}; +use fuel_core_poa::ports::BlockSigner; use fuel_core_services::stream::BoxStream; use fuel_core_storage::transactional::Changes; use fuel_core_txpool::BorrowedTxPool; @@ -16,9 +28,13 @@ use fuel_core_types::services::p2p::peer_reputation::AppScore; use fuel_core_types::{ blockchain::{ block::Block, - consensus::Consensus, + consensus::{ + poa::PoAConsensus, + Consensus, + }, }, fuel_tx::Transaction, + fuel_types::BlockHeight, services::{ block_importer::SharedImportResult, block_producer::Components, @@ -27,25 +43,12 @@ use fuel_core_types::{ UncommittedResult, }, }, + signer::SignMode, tai64::Tai64, }; use fuel_core_upgradable_executor::executor::Executor; use std::sync::Arc; -use crate::{ - database::{ - database_description::relayer::Relayer, - Database, - }, - service::{ - sub_services::{ - BlockProducerService, - TxPoolSharedState, - }, - vm_pool::MemoryPool, - }, -}; - pub mod block_importer; pub mod consensus_module; pub mod consensus_parameters_provider; @@ -59,6 +62,8 @@ pub mod p2p; pub mod producer; #[cfg(feature = "relayer")] pub mod relayer; +#[cfg(feature = "shared-sequencer")] +pub mod shared_sequencer; #[cfg(feature = "p2p")] pub mod sync; pub mod txpool; @@ -85,6 +90,162 @@ impl StaticGasPrice { } } +#[cfg(test)] +mod arc_gas_price_estimate_tests { + #![allow(non_snake_case)] + + use super::*; + use proptest::proptest; + + async fn _worst_case__correctly_calculates_value( + gas_price: u64, + starting_height: u32, + block_horizon: u32, + percentage: u16, + ) { + // given + let subject = ArcGasPriceEstimate::new(starting_height, gas_price, percentage); + + // when + let target_height = starting_height.saturating_add(block_horizon); + let estimated = subject + .worst_case_gas_price(target_height.into()) + .await + .unwrap(); + + // then + let mut actual = gas_price; + + for _ in 0..block_horizon { + let change_amount = + actual.saturating_mul(percentage as u64).saturating_div(100); + actual = actual.saturating_add(change_amount); + } + + assert!(estimated >= actual); + } + + proptest! { + #[test] + fn worst_case_gas_price__correctly_calculates_value( + gas_price: u64, + starting_height: u32, + block_horizon in 0..10_000u32, + percentage: u16, + ) { + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(_worst_case__correctly_calculates_value( + gas_price, + starting_height, + block_horizon, + percentage, + )); + } + } + + proptest! { + #[test] + fn worst_case_gas_price__never_overflows( + gas_price: u64, + starting_height: u32, + block_horizon in 0..10_000u32, + percentage: u16 + ) { + let rt = tokio::runtime::Runtime::new().unwrap(); + + // given + let subject = ArcGasPriceEstimate::new(starting_height, gas_price, percentage); + + // when + let target_height = starting_height.saturating_add(block_horizon); + + let _ = rt.block_on(subject.worst_case_gas_price(target_height.into())); + + // then + // doesn't panic with an overflow + } + } +} + +/// Allows communication from other service with more recent gas price data +/// `Height` refers to the height of the block at which the gas price was last updated +/// `GasPrice` refers to the gas price at the last updated block +#[allow(dead_code)] +pub struct ArcGasPriceEstimate { + /// Shared state of latest gas price data + latest_gas_price: LatestGasPrice, + /// The max percentage the gas price can increase per block + percentage: u16, +} + +impl ArcGasPriceEstimate { + #[cfg(test)] + pub fn new(height: Height, price: GasPrice, percentage: u16) -> Self { + let latest_gas_price = LatestGasPrice::new(height, price); + Self { + latest_gas_price, + percentage, + } + } + + pub fn new_from_inner( + inner: LatestGasPrice, + percentage: u16, + ) -> Self { + Self { + latest_gas_price: inner, + percentage, + } + } +} + +impl ArcGasPriceEstimate { + fn get_height_and_gas_price(&self) -> (Height, GasPrice) { + self.latest_gas_price.get() + } +} + +#[async_trait::async_trait] +impl GasPriceEstimate for ArcGasPriceEstimate { + async fn worst_case_gas_price(&self, height: BlockHeight) -> Option { + let (best_height, best_gas_price) = self.get_height_and_gas_price(); + let percentage = self.percentage; + + let worst = cumulative_percentage_change( + best_gas_price, + best_height, + percentage as u64, + height.into(), + ); + Some(worst) + } +} + +#[allow(clippy::cast_possible_truncation)] +pub(crate) fn cumulative_percentage_change( + start_gas_price: u64, + best_height: u32, + percentage: u64, + target_height: u32, +) -> u64 { + let blocks = target_height.saturating_sub(best_height) as f64; + let percentage_as_decimal = percentage as f64 / 100.0; + let multiple = (1.0f64 + percentage_as_decimal).powf(blocks); + let mut approx = start_gas_price as f64 * multiple; + // Account for rounding errors and take a slightly higher value + // Around the `ROUNDING_ERROR_CUTOFF` the rounding errors will cause the estimate to be too low. + // We increase by `ROUNDING_ERROR_COMPENSATION` to account for this. + // This is an unlikely situation in practice, but we want to guarantee that the actual + // gas price is always equal or less than the estimate given here + const ROUNDING_ERROR_CUTOFF: f64 = 16948547188989277.0; + if approx > ROUNDING_ERROR_CUTOFF { + const ROUNDING_ERROR_COMPENSATION: f64 = 2000.0; + approx += ROUNDING_ERROR_COMPENSATION; + } + // `f64` over `u64::MAX` are cast to `u64::MAX` + approx.ceil() as u64 +} + #[derive(Clone)] pub struct PoAAdapter { shared_state: Option, @@ -226,7 +387,34 @@ impl FuelBlockSigner { #[async_trait::async_trait] impl BlockSigner for FuelBlockSigner { async fn seal_block(&self, block: &Block) -> anyhow::Result { - self.mode.seal_block(block).await + let block_hash = block.id(); + let message = block_hash.into_message(); + let signature = self.mode.sign_message(message).await?; + Ok(Consensus::PoA(PoAConsensus::new(signature))) + } + + fn is_available(&self) -> bool { + self.mode.is_available() + } +} + +#[cfg(feature = "shared-sequencer")] +#[async_trait::async_trait] +impl fuel_core_shared_sequencer::ports::Signer for FuelBlockSigner { + async fn sign( + &self, + data: &[u8], + ) -> anyhow::Result { + Ok(self.mode.sign(data).await?) + } + + fn public_key(&self) -> cosmrs::crypto::PublicKey { + let pubkey = self + .mode + .verifying_key() + .expect("Invalid public key") + .expect("Public key not available"); + cosmrs::crypto::PublicKey::from(pubkey) } fn is_available(&self) -> bool { diff --git a/crates/fuel-core/src/service/adapters/shared_sequencer.rs b/crates/fuel-core/src/service/adapters/shared_sequencer.rs new file mode 100644 index 00000000000..611a725c172 --- /dev/null +++ b/crates/fuel-core/src/service/adapters/shared_sequencer.rs @@ -0,0 +1,10 @@ +use crate::service::adapters::BlockImporterAdapter; +use fuel_core_services::stream::BoxStream; +use fuel_core_shared_sequencer::ports::BlocksProvider; +use fuel_core_types::services::block_importer::SharedImportResult; + +impl BlocksProvider for BlockImporterAdapter { + fn subscribe(&self) -> BoxStream { + self.events_shared_result() + } +} diff --git a/crates/fuel-core/src/service/config.rs b/crates/fuel-core/src/service/config.rs index 0e093c2b8b6..02c463334f1 100644 --- a/crates/fuel-core/src/service/config.rs +++ b/crates/fuel-core/src/service/config.rs @@ -4,7 +4,6 @@ use std::{ }; use clap::ValueEnum; -use fuel_core_poa::signer::SignMode; use strum_macros::{ Display, EnumString, @@ -28,7 +27,10 @@ pub use fuel_core_poa::Trigger; #[cfg(feature = "relayer")] use fuel_core_relayer::Config as RelayerConfig; use fuel_core_txpool::config::Config as TxPoolConfig; -use fuel_core_types::blockchain::header::StateTransitionBytecodeVersion; +use fuel_core_types::{ + blockchain::header::StateTransitionBytecodeVersion, + signer::SignMode, +}; use crate::{ combined_database::CombinedDatabaseConfig, @@ -69,6 +71,8 @@ pub struct Config { pub p2p: Option>, #[cfg(feature = "p2p")] pub sync: fuel_core_sync::Config, + #[cfg(feature = "shared-sequencer")] + pub shared_sequencer: fuel_core_shared_sequencer::Config, pub consensus_signer: SignMode, pub name: String, pub relayer_consensus_config: fuel_core_consensus_module::RelayerConsensusConfig, @@ -104,6 +108,8 @@ impl Config { #[cfg(feature = "test-helpers")] pub fn local_node_with_reader(snapshot_reader: SnapshotReader) -> Self { + use crate::state::rocks_db::DatabaseConfig; + let block_importer = fuel_core_importer::Config::new(false); let latest_block = snapshot_reader.last_block_config(); // In tests, we always want to use the native executor as a default configuration. @@ -117,7 +123,12 @@ impl Config { let combined_db_config = CombinedDatabaseConfig { // Set the cache for tests = 10MB - max_database_cache_size: 10 * 1024 * 1024, + #[cfg(feature = "rocksdb")] + database_config: DatabaseConfig { + cache_capacity: Some(10 * 1024 * 1024), + columns_policy: Default::default(), + max_fds: 512, + }, database_path: Default::default(), #[cfg(feature = "rocksdb")] database_type: DbType::RocksDb, @@ -126,8 +137,6 @@ impl Config { #[cfg(feature = "rocksdb")] state_rewind_policy: crate::state::historical_rocksdb::StateRewindPolicy::RewindFullRange, - #[cfg(feature = "rocksdb")] - max_fds: 512, }; let starting_gas_price = 0; let gas_price_change_percent = 0; @@ -182,6 +191,8 @@ impl Config { p2p: Some(P2PConfig::::default("test_network")), #[cfg(feature = "p2p")] sync: fuel_core_sync::Config::default(), + #[cfg(feature = "shared-sequencer")] + shared_sequencer: fuel_core_shared_sequencer::Config::local_node(), consensus_signer: SignMode::Key(fuel_core_types::secrecy::Secret::new( fuel_core_chain_config::default_consensus_dev_key().into(), )), @@ -234,7 +245,7 @@ pub struct VMConfig { } #[derive( - Clone, Debug, Display, Eq, PartialEq, EnumString, EnumVariantNames, ValueEnum, + Clone, Copy, Debug, Display, Eq, PartialEq, EnumString, EnumVariantNames, ValueEnum, )] #[strum(serialize_all = "kebab_case")] pub enum DbType { diff --git a/crates/fuel-core/src/service/sub_services.rs b/crates/fuel-core/src/service/sub_services.rs index 4fd45534b24..9bd53e072a8 100644 --- a/crates/fuel-core/src/service/sub_services.rs +++ b/crates/fuel-core/src/service/sub_services.rs @@ -42,10 +42,7 @@ use fuel_core_gas_price_service::v0::uninitialized_task::{ new_gas_price_service_v0, AlgorithmV0, }; -use fuel_core_poa::{ - signer::SignMode, - Trigger, -}; +use fuel_core_poa::Trigger; use fuel_core_storage::{ self, structured_storage::StructuredStorage, @@ -53,6 +50,7 @@ use fuel_core_storage::{ }; #[cfg(feature = "relayer")] use fuel_core_types::blockchain::primitives::DaBlockHeight; +use fuel_core_types::signer::SignMode; use std::sync::Arc; use tokio::sync::Mutex; @@ -182,7 +180,7 @@ pub fn init_sub_services( let genesis_block_height = *genesis_block.header().height(); let settings = consensus_parameters_provider.clone(); let block_stream = importer_adapter.events_shared_result(); - let metadata = StructuredStorage::new(database.gas_price().clone()); + let metadata = database.gas_price().clone(); let gas_price_service_v0 = new_gas_price_service_v0( config.clone().into(), @@ -190,7 +188,7 @@ pub fn init_sub_services( settings, block_stream, database.gas_price().clone(), - metadata, + StructuredStorage::new(metadata), database.on_chain().clone(), )?; @@ -245,9 +243,22 @@ pub fn init_sub_services( tracing::info!("Enabled manual block production because of `debug` flag"); } + let signer = Arc::new(FuelBlockSigner::new(config.consensus_signer.clone())); + + #[cfg(feature = "shared-sequencer")] + let shared_sequencer = { + let config = config.shared_sequencer.clone(); + + fuel_core_shared_sequencer::service::new_service( + importer_adapter.clone(), + config, + signer.clone(), + )? + }; + let predefined_blocks = InDirectoryPredefinedBlocks::new(config.predefined_blocks_path.clone()); - let poa = (production_enabled).then(|| { + let poa = production_enabled.then(|| { fuel_core_poa::new_service( &last_block_header, poa_config, @@ -255,7 +266,7 @@ pub fn init_sub_services( producer_adapter.clone(), importer_adapter.clone(), p2p_adapter.clone(), - FuelBlockSigner::new(config.consensus_signer.clone()), + signer, predefined_blocks, SystemTime, ) @@ -353,6 +364,8 @@ pub fn init_sub_services( services.push(Box::new(sync)); } } + #[cfg(feature = "shared-sequencer")] + services.push(Box::new(shared_sequencer)); services.push(Box::new(graph_ql)); services.push(Box::new(graphql_worker)); diff --git a/crates/fuel-core/src/state/data_source.rs b/crates/fuel-core/src/state/data_source.rs index cf107d0e1b9..f30f3ee8268 100644 --- a/crates/fuel-core/src/state/data_source.rs +++ b/crates/fuel-core/src/state/data_source.rs @@ -67,9 +67,10 @@ where &self, key: &[u8], column: Self::Column, + offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.data.read(key, column, buf) + self.data.read(key, column, offset, buf) } } diff --git a/crates/fuel-core/src/state/generic_database.rs b/crates/fuel-core/src/state/generic_database.rs index b6f5f2ea464..5a31591268f 100644 --- a/crates/fuel-core/src/state/generic_database.rs +++ b/crates/fuel-core/src/state/generic_database.rs @@ -79,8 +79,13 @@ where M: Mappable, StructuredStorage: StorageRead, { - fn read(&self, key: &M::Key, buf: &mut [u8]) -> Result, Self::Error> { - self.storage.storage::().read(key, buf) + fn read( + &self, + key: &M::Key, + offset: usize, + buf: &mut [u8], + ) -> Result, Self::Error> { + self.storage.storage::().read(key, offset, buf) } fn read_alloc(&self, key: &M::Key) -> Result>, Self::Error> { @@ -124,9 +129,10 @@ where &self, key: &[u8], column: Self::Column, + offset: usize, buf: &mut [u8], ) -> StorageResult> { - KeyValueInspect::read(&self.storage, key, column, buf) + KeyValueInspect::read(&self.storage, key, column, offset, buf) } } diff --git a/crates/fuel-core/src/state/historical_rocksdb.rs b/crates/fuel-core/src/state/historical_rocksdb.rs index a0bd39b2801..6422e2a1c47 100644 --- a/crates/fuel-core/src/state/historical_rocksdb.rs +++ b/crates/fuel-core/src/state/historical_rocksdb.rs @@ -64,6 +64,8 @@ use std::{ path::Path, }; +use super::rocks_db::DatabaseConfig; + pub mod description; pub mod modifications_history; pub mod view_at_height; @@ -104,14 +106,16 @@ where }) } + pub fn inner(&self) -> &RocksDb> { + &self.db + } + pub fn default_open>( path: P, - capacity: Option, state_rewind_policy: StateRewindPolicy, - max_fds: i32, + database_config: DatabaseConfig, ) -> DatabaseResult { - let db = - RocksDb::>::default_open(path, capacity, max_fds)?; + let db = RocksDb::>::default_open(path, database_config)?; Ok(Self { state_rewind_policy, db, @@ -534,9 +538,11 @@ where &self, key: &[u8], column: Self::Column, + offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.db.read(key, Column::OriginalColumn(column), buf) + self.db + .read(key, Column::OriginalColumn(column), offset, buf) } } diff --git a/crates/fuel-core/src/state/iterable_key_value_view.rs b/crates/fuel-core/src/state/iterable_key_value_view.rs index 40ab3483ede..1d8fd02cbbb 100644 --- a/crates/fuel-core/src/state/iterable_key_value_view.rs +++ b/crates/fuel-core/src/state/iterable_key_value_view.rs @@ -67,9 +67,10 @@ where &self, key: &[u8], column: Self::Column, + offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.0.read(key, column, buf) + self.0.read(key, column, offset, buf) } } diff --git a/crates/fuel-core/src/state/key_value_view.rs b/crates/fuel-core/src/state/key_value_view.rs index 9e70037fc21..e507e83329a 100644 --- a/crates/fuel-core/src/state/key_value_view.rs +++ b/crates/fuel-core/src/state/key_value_view.rs @@ -54,8 +54,9 @@ where &self, key: &[u8], column: Self::Column, + offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.0.read(key, column, buf) + self.0.read(key, column, offset, buf) } } diff --git a/crates/fuel-core/src/state/rocks_db.rs b/crates/fuel-core/src/state/rocks_db.rs index e69c6289bed..3f43333c152 100644 --- a/crates/fuel-core/src/state/rocks_db.rs +++ b/crates/fuel-core/src/state/rocks_db.rs @@ -28,6 +28,7 @@ use fuel_core_storage::{ WriteOperation, }, transactional::Changes, + Error as StorageError, Result as StorageResult, }; use itertools::Itertools; @@ -95,10 +96,40 @@ impl Drop for DropResources { } } +#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)] +/// Defined behaviour for opening the columns of the database. +pub enum ColumnsPolicy { + #[cfg_attr(not(feature = "rocksdb-production"), default)] + // Open a new column only when a database interaction is done with it. + Lazy, + #[cfg_attr(feature = "rocksdb-production", default)] + // Open all columns on creation on the service. + OnCreation, +} + +/// Configuration to create a database +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct DatabaseConfig { + pub cache_capacity: Option, + pub max_fds: i32, + pub columns_policy: ColumnsPolicy, +} + +#[cfg(feature = "test-helpers")] +impl DatabaseConfig { + pub fn config_for_tests() -> Self { + Self { + cache_capacity: None, + max_fds: 512, + columns_policy: ColumnsPolicy::Lazy, + } + } +} + pub struct RocksDb { read_options: ReadOptions, db: Arc, - create_family: Arc>>, + create_family: Option>>>, snapshot: Option>, metrics: Arc, // used for RAII @@ -124,14 +155,28 @@ impl RocksDb where Description: DatabaseDescription, { + /// Allows consumers to get the inner db handle + pub fn inner(&self) -> &DB { + &self.db + } + pub fn default_open_temp(capacity: Option) -> DatabaseResult { + Self::default_open_temp_with_params(DatabaseConfig { + cache_capacity: capacity, + max_fds: 512, + columns_policy: Default::default(), + }) + } + + pub fn default_open_temp_with_params( + database_config: DatabaseConfig, + ) -> DatabaseResult { let tmp_dir = TempDir::new().unwrap(); let path = tmp_dir.path(); let result = Self::open( path, enum_iterator::all::().collect::>(), - capacity, - 512, + database_config, ); let mut db = result?; @@ -150,14 +195,12 @@ where pub fn default_open>( path: P, - capacity: Option, - max_fds: i32, + database_config: DatabaseConfig, ) -> DatabaseResult { Self::open( path, enum_iterator::all::().collect::>(), - capacity, - max_fds, + database_config, ) } @@ -171,18 +214,16 @@ where pub fn open>( path: P, columns: Vec, - capacity: Option, - max_fds: i32, + database_config: DatabaseConfig, ) -> DatabaseResult { - Self::open_with(DB::open_cf_descriptors, path, columns, capacity, max_fds) + Self::open_with(DB::open_cf_descriptors, path, columns, database_config) } pub fn open_read_only>( path: P, columns: Vec, - capacity: Option, error_if_log_file_exist: bool, - max_fds: i32, + database_config: DatabaseConfig, ) -> DatabaseResult { Self::open_with( |options, primary_path, cfs| { @@ -195,8 +236,7 @@ where }, path, columns, - capacity, - max_fds, + database_config, ) } @@ -204,8 +244,7 @@ where path: PrimaryPath, secondary_path: SecondaryPath, columns: Vec, - capacity: Option, - max_fds: i32, + database_config: DatabaseConfig, ) -> DatabaseResult where PrimaryPath: AsRef, @@ -222,8 +261,7 @@ where }, path, columns, - capacity, - max_fds, + database_config, ) } @@ -231,8 +269,7 @@ where opener: F, path: P, columns: Vec, - capacity: Option, - max_fds: i32, + database_config: DatabaseConfig, ) -> DatabaseResult where F: Fn( @@ -256,7 +293,7 @@ where // See https://github.com/facebook/rocksdb/blob/a1523efcdf2f0e8133b9a9f6e170a0dad49f928f/include/rocksdb/table.h#L246-L271 for details on what the format versions are/do. block_opts.set_format_version(5); - if let Some(capacity) = capacity { + if let Some(capacity) = database_config.cache_capacity { // Set cache size 1/3 of the capacity as recommended by // https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning#block-cache-size let block_cache_size = capacity / 3; @@ -280,7 +317,7 @@ where let cpu_number = i32::try_from(num_cpus::get()).expect("The number of CPU can't exceed `i32`"); opts.increase_parallelism(cmp::max(1, cpu_number / 2)); - if let Some(capacity) = capacity { + if let Some(capacity) = database_config.cache_capacity { // Set cache size 1/3 of the capacity. Another 1/3 is // used by block cache and the last 1 / 3 remains for other purposes: // @@ -291,7 +328,7 @@ where } opts.set_max_background_jobs(6); opts.set_bytes_per_sync(1048576); - opts.set_max_open_files(max_fds); + opts.set_max_open_files(database_config.max_fds); let existing_column_families = DB::list_cf(&opts, &path).unwrap_or_default(); @@ -307,7 +344,10 @@ where } } - if cf_descriptors_to_open.is_empty() { + if database_config.columns_policy == ColumnsPolicy::OnCreation + || (database_config.columns_policy == ColumnsPolicy::Lazy + && cf_descriptors_to_open.is_empty()) + { opts.create_if_missing(true); } @@ -348,8 +388,17 @@ where } .map_err(|e| DatabaseError::Other(e.into()))?; + let create_family = match database_config.columns_policy { + ColumnsPolicy::OnCreation => { + for (name, opt) in cf_descriptors_to_create { + db.create_cf(name, &opt) + .map_err(|e| DatabaseError::Other(e.into()))?; + } + None + } + ColumnsPolicy::Lazy => Some(Arc::new(Mutex::new(cf_descriptors_to_create))), + }; let db = Arc::new(db); - let create_family = Arc::new(Mutex::new(cf_descriptors_to_create)); let rocks_db = RocksDb { read_options: Self::generate_read_options(&None), @@ -423,26 +472,29 @@ where match family { None => { - let mut lock = self - .create_family - .lock() - .expect("The create family lock should be available"); - - let name = Self::col_name(column); - let Some(family) = lock.remove(&name) else { - return self - .db - .cf_handle(&Self::col_name(column)) - .expect("No column family found"); - }; - - self.db - .create_cf(&name, &family) - .expect("Couldn't create column family"); - - let family = self.db.cf_handle(&name).expect("invalid column state"); - - family + if let Some(create_family) = &self.create_family { + let mut lock = create_family + .lock() + .expect("The create family lock should be available"); + + let name = Self::col_name(column); + let Some(family) = lock.remove(&name) else { + return self + .db + .cf_handle(&Self::col_name(column)) + .expect("No column family found"); + }; + + self.db + .create_cf(&name, &family) + .expect("Couldn't create column family"); + + let family = self.db.cf_handle(&name).expect("invalid column state"); + + family + } else { + panic!("Columns in the DB should have been created on DB opening"); + } } Some(family) => family, } @@ -758,7 +810,8 @@ where &self, key: &[u8], column: Self::Column, - mut buf: &mut [u8], + offset: usize, + buf: &mut [u8], ) -> StorageResult> { self.metrics.read_meter.inc(); let column_metrics = self.metrics.columns_read_statistic.get(&column.id()); @@ -769,10 +822,21 @@ where .get_pinned_cf_opt(&self.cf(column), key, &self.read_options) .map_err(|e| DatabaseError::Other(e.into()))? .map(|value| { - let read = value.len(); - std::io::Write::write_all(&mut buf, value.as_ref()) - .map_err(|e| DatabaseError::Other(anyhow::anyhow!(e)))?; - StorageResult::Ok(read) + let bytes_len = value.len(); + let start = offset; + let end = offset.saturating_add(buf.len()); + + if end > bytes_len { + return Err(StorageError::Other(anyhow::anyhow!( + "Offset `{offset}` is out of bounds `{bytes_len}` \ + for key `{:?}` and column `{column:?}`", + key + ))); + } + + let starting_from_offset = &value[start..end]; + buf[..].copy_from_slice(starting_from_offset); + Ok(buf.len()) }) .transpose()?; @@ -913,7 +977,8 @@ mod tests { fn create_db() -> (RocksDb, TempDir) { let tmp_dir = TempDir::new().unwrap(); ( - RocksDb::default_open(tmp_dir.path(), None, 512).unwrap(), + RocksDb::default_open(tmp_dir.path(), DatabaseConfig::config_for_tests()) + .unwrap(), tmp_dir, ) } @@ -925,17 +990,24 @@ mod tests { // Given let old_columns = vec![Column::Coins, Column::Messages, Column::UploadedBytecodes]; - let database_with_old_columns = - RocksDb::::open(tmp_dir.path(), old_columns.clone(), None, 512) - .expect("Failed to open database with old columns"); + let database_with_old_columns = RocksDb::::open( + tmp_dir.path(), + old_columns.clone(), + DatabaseConfig::config_for_tests(), + ) + .expect("Failed to open database with old columns"); drop(database_with_old_columns); // When let mut new_columns = old_columns; new_columns.push(Column::ContractsAssets); new_columns.push(Column::Metadata); - let database_with_new_columns = - RocksDb::::open(tmp_dir.path(), new_columns, None, 512).map(|_| ()); + let database_with_new_columns = RocksDb::::open( + tmp_dir.path(), + new_columns, + DatabaseConfig::config_for_tests(), + ) + .map(|_| ()); // Then assert_eq!(Ok(()), database_with_new_columns); @@ -1104,7 +1176,11 @@ mod tests { // When let columns = enum_iterator::all::<::Column>() .collect::>(); - let result = RocksDb::::open(tmp_dir.path(), columns, None, 512); + let result = RocksDb::::open( + tmp_dir.path(), + columns, + DatabaseConfig::config_for_tests(), + ); // Then assert!(result.is_err()); @@ -1121,9 +1197,8 @@ mod tests { let result = RocksDb::::open_read_only( tmp_dir.path(), old_columns.clone(), - None, false, - 512, + DatabaseConfig::config_for_tests(), ) .map(|_| ()); @@ -1144,8 +1219,7 @@ mod tests { tmp_dir.path(), secondary_temp.path(), old_columns.clone(), - None, - 512, + DatabaseConfig::config_for_tests(), ) .map(|_| ()); @@ -1237,8 +1311,11 @@ mod tests { enum_iterator::all::<::Column>() .skip(1) .collect::>(); - let open_with_part_of_columns = - RocksDb::::open(tmp_dir.path(), part_of_columns, None, 512); + let open_with_part_of_columns = RocksDb::::open( + tmp_dir.path(), + part_of_columns, + DatabaseConfig::config_for_tests(), + ); // Then let _ = open_with_part_of_columns diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/.gitignore b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/.gitignore new file mode 100644 index 00000000000..a3253ee3df3 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +data diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/Cargo.toml b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/Cargo.toml new file mode 100644 index 00000000000..88119f5f5f5 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "fuel-gas-price-data-fetcher" +version = "0.0.1" +edition = "2021" +publish = false + +[workspace] + +[dependencies] +anyhow = "1.0.86" +clap = { version = "4.5.16", features = ["derive"] } +csv = "1.3.0" +fuel-gas-price-algorithm = { path = ".." } +futures = "0.3.30" +plotters = "0.3.5" +rand = "0.8.5" +rand_distr = "0.4.3" +serde = { version = "1.0.209", features = ["derive"] } +tokio = { version = "1.40.0", features = ["macros", "rt", "rt-multi-thread"] } +reqwest = { version = "0.12.11", features = ["json"] } +serde_json = { version = "1.0.134" } +fuel-core-client = { version = "0.40.2" } # locked to whatever version you're supposed to be fetching data from +fuel-core-types = { version = "0.40.2" } +postcard = { version = "1.0" } +tracing = { version = "0.1.41" } +tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } + +async-trait = "0.1" +cynic = { version = "2.2", features = ["http-reqwest"] } +itertools = { version = "0.13" } + +[build-dependencies] +fuel-core-client = { version = "0.40.2" } diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/README.md b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/README.md new file mode 100644 index 00000000000..935451cb9f9 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/README.md @@ -0,0 +1,10 @@ +# Gas Price Analysis Data Fetcher + +Binary allowing retrieveing the L1 blob and L2 block data needed by the gas price simulation binary. +It requires being able to connect to the block committer, and either being able to connect to a sentry node or access to the database of a mainnet synched node. + +## Usage + +``` +cargo run -- --block-committer-endpoint ${BLOCK_COMMITTER_URL} --block-range 0 1000 --db-path ${FUEL_MAINNET_DB_PATH} +``` diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/build.rs b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/build.rs new file mode 100644 index 00000000000..f215fc0766e --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/build.rs @@ -0,0 +1,14 @@ +#![deny(clippy::arithmetic_side_effects)] +#![deny(clippy::cast_possible_truncation)] +#![deny(unused_crate_dependencies)] +#![deny(warnings)] + +use std::fs; + +fn main() { + fs::create_dir_all("target").expect("Unable to create target directory"); + fs::write("target/schema.sdl", fuel_core_client::SCHEMA_SDL) + .expect("Unable to write schema file"); + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/client_ext.rs b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/client_ext.rs new file mode 100644 index 00000000000..dc8aa8ee78f --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/client_ext.rs @@ -0,0 +1,236 @@ +#![deny(clippy::arithmetic_side_effects)] +#![deny(clippy::cast_possible_truncation)] +#![deny(warnings)] + +// This was copied from https://github.com/FuelLabs/fuel-core-client-ext/blob/b792ef76cbcf82eda45a944b15433682fe094fee/src/lib.rs + +use cynic::QueryBuilder; +use fuel_core_client::{ + client, + client::{ + pagination::{ + PaginatedResult, + PaginationRequest, + }, + schema::{ + block::{ + BlockByHeightArgs, + Consensus, + Header, + }, + schema, + tx::OpaqueTransactionWithStatus, + ConnectionArgs, + PageInfo, + }, + types::{ + TransactionResponse, + TransactionStatus, + }, + FuelClient, + }, +}; +use fuel_core_types::{ + blockchain::{ + self, + block::Block, + header::{ + ApplicationHeader, + ConsensusHeader, + PartialBlockHeader, + }, + SealedBlock, + }, + fuel_tx::{ + Bytes32, + Receipt, + }, +}; +use itertools::Itertools; + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema_path = "./target/schema.sdl", + graphql_type = "Query", + variables = "ConnectionArgs" +)] +pub struct FullBlocksQuery { + #[arguments(after: $after, before: $before, first: $first, last: $last)] + pub blocks: FullBlockConnection, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema_path = "./target/schema.sdl", graphql_type = "BlockConnection")] +pub struct FullBlockConnection { + pub edges: Vec, + pub page_info: PageInfo, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema_path = "./target/schema.sdl", graphql_type = "BlockEdge")] +pub struct FullBlockEdge { + pub cursor: String, + pub node: FullBlock, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema_path = "./target/schema.sdl", + graphql_type = "Query", + variables = "BlockByHeightArgs" +)] +pub struct FullBlockByHeightQuery { + #[arguments(height: $height)] + pub block: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema_path = "./target/schema.sdl", graphql_type = "Block")] +pub struct FullBlock { + pub header: Header, + pub consensus: Consensus, + pub transactions: Vec, +} + +impl From for PaginatedResult { + fn from(conn: FullBlockConnection) -> Self { + PaginatedResult { + cursor: conn.page_info.end_cursor, + has_next_page: conn.page_info.has_next_page, + has_previous_page: conn.page_info.has_previous_page, + results: conn.edges.into_iter().map(|e| e.node).collect(), + } + } +} + +#[async_trait::async_trait] +pub trait ClientExt { + async fn full_blocks( + &self, + request: PaginationRequest, + ) -> std::io::Result>; +} + +#[async_trait::async_trait] +impl ClientExt for FuelClient { + async fn full_blocks( + &self, + request: PaginationRequest, + ) -> std::io::Result> { + let query = FullBlocksQuery::build(request.into()); + let blocks = self.query(query).await?.blocks.into(); + Ok(blocks) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SealedBlockWithMetadata { + pub block: SealedBlock, + pub receipts: Vec>>, +} + +impl TryFrom for SealedBlockWithMetadata { + type Error = anyhow::Error; + + fn try_from(full_block: FullBlock) -> Result { + let transactions: Vec = full_block + .transactions + .into_iter() + .map(TryInto::try_into) + .try_collect()?; + + let receipts = transactions + .iter() + .map(|tx| &tx.status) + .map(|status| match status { + TransactionStatus::Success { receipts, .. } => Some(receipts.clone()), + _ => None, + }) + .collect_vec(); + + let messages = receipts + .iter() + .flatten() + .flat_map(|receipt| receipt.iter().filter_map(|r| r.message_id())) + .collect_vec(); + + let transactions = transactions + .into_iter() + .map(|tx| tx.transaction) + .collect_vec(); + + let partial_header = PartialBlockHeader { + application: ApplicationHeader { + da_height: full_block.header.da_height.0.into(), + consensus_parameters_version: full_block + .header + .consensus_parameters_version + .into(), + state_transition_bytecode_version: full_block + .header + .state_transition_bytecode_version + .into(), + generated: Default::default(), + }, + consensus: ConsensusHeader { + prev_root: full_block.header.prev_root.into(), + height: full_block.header.height.into(), + time: full_block.header.time.into(), + generated: Default::default(), + }, + }; + + let header = partial_header + .generate( + &transactions, + &messages, + full_block.header.event_inbox_root.into(), + ) + .map_err(|e| anyhow::anyhow!(e))?; + + let actual_id: Bytes32 = full_block.header.id.into(); + let expected_id: Bytes32 = header.id().into(); + if expected_id != actual_id { + return Err(anyhow::anyhow!("Header id mismatch")); + } + + let block = Block::try_from_executed(header, transactions) + .ok_or(anyhow::anyhow!("Failed to create block from transactions"))?; + + let consensus: client::types::Consensus = full_block.consensus.into(); + + let consensus = match consensus { + client::types::Consensus::Genesis(genesis) => { + use blockchain::consensus as core_consensus; + core_consensus::Consensus::Genesis(core_consensus::Genesis { + chain_config_hash: genesis.chain_config_hash, + coins_root: genesis.coins_root, + contracts_root: genesis.contracts_root, + messages_root: genesis.messages_root, + transactions_root: genesis.transactions_root, + }) + } + client::types::Consensus::PoAConsensus(poa) => { + use blockchain::consensus as core_consensus; + core_consensus::Consensus::PoA(core_consensus::poa::PoAConsensus { + signature: poa.signature, + }) + } + client::types::Consensus::Unknown => { + return Err(anyhow::anyhow!("Unknown consensus type")); + } + }; + + let sealed = SealedBlock { + entity: block, + consensus, + }; + + let sealed = SealedBlockWithMetadata { + block: sealed, + receipts, + }; + + Ok(sealed) + } +} diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/layer1.rs b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/layer1.rs new file mode 100644 index 00000000000..cbb618c3281 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/layer1.rs @@ -0,0 +1,94 @@ +use std::ops::Range; + +use fuel_core_types::fuel_types::BlockHeight; +use reqwest::{ + header::{ + HeaderMap, + CONTENT_TYPE, + }, + Url, +}; + +use crate::types::BlockCommitterCosts; + +pub struct BlockCommitterDataFetcher { + client: reqwest::Client, + endpoint: Url, + num_responses: usize, +} + +impl BlockCommitterDataFetcher { + pub fn new(endpoint: Url, num_responses: usize) -> anyhow::Result { + let mut content_type_json_header = HeaderMap::new(); + content_type_json_header.insert( + CONTENT_TYPE, + "application/json" + .parse() + .expect("Content-Type header value is valid"), + ); + let client = reqwest::ClientBuilder::new() + .default_headers(content_type_json_header) + .build()?; + Ok(Self { + client, + endpoint, + num_responses, + }) + } + + // TODO: Better error type; qed + async fn fetch_blob_data( + &self, + from_height: u64, + ) -> anyhow::Result> { + let query = self.endpoint.join("v1/costs")?.join(&format!( + "?variant=specific&value={}&limit={}", + from_height, self.num_responses + ))?; + + tracing::debug!("Query: {}", query.as_str()); + + let response = self.client.get(query).send().await?; + if !response.status().is_success() { + return Err(anyhow::anyhow!( + "Failed to fetch data from block committer: {}", + response.status(), + ) + .into()); + } + + let block_committer_costs = response.json::>().await?; + Ok(block_committer_costs) + } + + pub async fn fetch_l1_block_costs( + &self, + blocks: Range, + ) -> Result, anyhow::Error> { + let mut block_costs = vec![]; + let mut current_block_height = blocks.start; + while current_block_height < blocks.end { + let Ok(mut costs) = + self.fetch_blob_data((*current_block_height).into()).await + else { + Err(anyhow::anyhow!( + "Could not fetch data for block {}", + current_block_height + ))? + }; + + if costs.is_empty() { + // Might be that the block committer doesn't have data for the block, in which case we return prematurely. + // If this happens, we should increase the value of results returned by the block committer in the query. + break; + } + + // Block committer will return the data for the block in the next batch, hence we don't increment the height of the last + // block. + current_block_height = (*costs.last().unwrap().end_height).into(); + block_costs.append(&mut costs); + } + + Ok(block_costs) + } +} diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/layer2.rs b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/layer2.rs new file mode 100644 index 00000000000..754ca64af38 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/layer2.rs @@ -0,0 +1,214 @@ +use std::{ + collections::{ + HashMap, + HashSet, + }, + ops::Range, +}; + +use crate::types::{ + BytesSize, + GasUnits, + Layer2BlockData, +}; + +use super::client_ext::{ + ClientExt, + SealedBlockWithMetadata, +}; +use fuel_core_client::client::{ + pagination::{ + PageDirection, + PaginationRequest, + }, + FuelClient, +}; +use fuel_core_types::{ + fuel_tx::{ + Chargeable, + ConsensusParameters, + }, + fuel_types::BlockHeight, +}; +use itertools::Itertools; + +#[derive(Clone)] +pub struct BlockFetcher { + client: FuelClient, +} + +impl BlockFetcher { + pub fn new(url: impl AsRef) -> anyhow::Result { + let client = FuelClient::new(url)?; + Ok(Self { client }) + } +} + +impl BlockFetcher { + pub async fn blocks_for( + &self, + range: Range, + ) -> anyhow::Result> { + if range.is_empty() { + return Ok(vec![]); + } + + let start = range.start.saturating_sub(1); + let size = i32::try_from(range.len()).expect("Should be a valid i32"); + + let request = PaginationRequest { + cursor: Some(start.to_string()), + results: size, + direction: PageDirection::Forward, + }; + let response = self.client.full_blocks(request).await?; + let blocks = response + .results + .into_iter() + .map(TryInto::try_into) + .try_collect()?; + Ok(blocks) + } + + pub async fn get_l2_block_data( + &self, + range: Range, + num_results: usize, + ) -> anyhow::Result> { + let range: Range = range.start.try_into()?..range.end.try_into()?; + let mut ranges: Vec> = + Vec::with_capacity(range.len().saturating_div(num_results)); + + for start in range.clone().step_by(num_results) { + let end = start.saturating_add(num_results as u32).min(range.end); + ranges.push(start..end); + } + + let mut blocks = Vec::with_capacity(range.len()); + for range in ranges { + tracing::info!("Fetching blocks for range {:?}", range); + let blocks_for_range = self.blocks_for(range).await?; + blocks.extend(blocks_for_range); + } + + let consensus_parameters_versions = blocks + .iter() + .map(|b| b.block.entity.header().consensus_parameters_version) + .collect::>(); + + tracing::debug!( + "Consensus parameter versions: {:?}", + consensus_parameters_versions + ); + + let mut consensus_parameters: HashMap = HashMap::new(); + for consensus_parameters_version in consensus_parameters_versions { + let cp = self + .client + .consensus_parameters(consensus_parameters_version.try_into()?) + .await?; + + if let Some(cp) = cp { + tracing::debug!( + "Found consensus parameters for version {}: {:?}", + consensus_parameters_version, + cp + ); + consensus_parameters.insert(consensus_parameters_version, cp); + } + } + + let mut block_data = HashMap::with_capacity(range.len()); + + for b in blocks { + let block_height = height(&b); + let consensus_parameters = consensus_parameters + .get(&b.block.entity.header().consensus_parameters_version) + .ok_or(anyhow::anyhow!( + "Consensus parameters not found for block {}", + block_height + ))?; + let block_size = + BytesSize(postcard::to_allocvec(&b.block)?.len().try_into()?); + + let gas_consumed = total_gas_consumed(&b, consensus_parameters)?; + let capacity = GasUnits(consensus_parameters.block_gas_limit()); + let bytes_capacity = + BytesSize(consensus_parameters.block_transaction_size_limit()); + let transactions_count = b.block.entity.transactions().len(); + + block_data.insert( + block_height, + Layer2BlockData { + block_height, + block_size, + gas_consumed, + capacity, + bytes_capacity, + transactions_count, + }, + ); + } + + Ok(block_data) + } +} + +fn height(block: &SealedBlockWithMetadata) -> BlockHeight { + *block.block.entity.header().height() +} + +fn total_gas_consumed( + block: &SealedBlockWithMetadata, + consensus_parameters: &ConsensusParameters, +) -> Result { + let min_gas: u64 = block + .block + .entity + .transactions() + .iter() + .filter_map(|tx| match tx { + fuel_core_types::fuel_tx::Transaction::Script(chargeable_transaction) => { + Some(chargeable_transaction.min_gas( + consensus_parameters.gas_costs(), + consensus_parameters.fee_params(), + )) + } + fuel_core_types::fuel_tx::Transaction::Create(chargeable_transaction) => { + Some(chargeable_transaction.min_gas( + consensus_parameters.gas_costs(), + consensus_parameters.fee_params(), + )) + } + fuel_core_types::fuel_tx::Transaction::Mint(_mint) => None, + fuel_core_types::fuel_tx::Transaction::Upgrade(chargeable_transaction) => { + Some(chargeable_transaction.min_gas( + consensus_parameters.gas_costs(), + consensus_parameters.fee_params(), + )) + } + fuel_core_types::fuel_tx::Transaction::Upload(chargeable_transaction) => { + Some(chargeable_transaction.min_gas( + consensus_parameters.gas_costs(), + consensus_parameters.fee_params(), + )) + } + fuel_core_types::fuel_tx::Transaction::Blob(chargeable_transaction) => { + Some(chargeable_transaction.min_gas( + consensus_parameters.gas_costs(), + consensus_parameters.fee_params(), + )) + } + }) + .sum(); + let gas_consumed = block + .receipts + .iter() + .flatten() + .map(|r| r.iter().filter_map(|r| r.gas_used()).sum::()) + .sum(); + let total_gas = min_gas + .checked_add(gas_consumed) + .ok_or(anyhow::anyhow!("Gas overflow"))?; + Ok(GasUnits(total_gas)) +} diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/main.rs new file mode 100644 index 00000000000..e8abaeefd28 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/main.rs @@ -0,0 +1,170 @@ +use std::{ + env, + path::PathBuf, +}; + +use fuel_core_types::fuel_types::BlockHeight; +use layer1::BlockCommitterDataFetcher; +use reqwest::Url; +use tracing_subscriber::EnvFilter; +use types::Layer2BlockData; + +use clap::{ + Args, + Parser, +}; + +use tracing_subscriber::prelude::*; + +pub mod client_ext; +mod layer1; +mod layer2; +mod summary; +mod types; + +// const SENTRY_NODE_GRAPHQL_RESULTS_PER_QUERY: usize = 5_000; +const SENTRY_NODE_GRAPHQL_RESULTS_PER_QUERY: usize = 40; + +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Arg { + #[clap(flatten)] + l2_block_data_source: L2BlockDataSource, + #[arg(short, long)] + /// Endpoint of the block committer to fetch L1 blobs data + block_committer_endpoint: Url, + #[arg( + short = 'r', + long, + num_args = 2..=2, + required = true + )] + /// Range of blocks to fetch the data for. Lower bound included, Upper bound excluded. + block_range: Vec, + + #[arg(required = true)] + /// The output CSV file where Block and Blob data will be written to + output_file: PathBuf, +} + +#[derive(Debug, Args)] +#[group(required = true, multiple = false)] +struct L2BlockDataSource { + #[arg(short, long)] + /// Path of the database stored by a fuel-node to retrieve L2 block data. Alternatively, the endpoint of a sentry node can be provided using --sentry-node-endpoint. + db_path: Option, + #[arg(short, long)] + /// Endpoint of the sentry node to fetch L2 block data. Alternatively, the path of the database stored by a fuel-node can be provided using --db-path. + sentry_node_endpoint: Option, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let Arg { + block_committer_endpoint, + l2_block_data_source, + block_range, + output_file, + } = Arg::parse(); + + let filter = match env::var_os("RUST_LOG") { + Some(_) => { + EnvFilter::try_from_default_env().expect("Invalid `RUST_LOG` provided") + } + None => EnvFilter::new("info"), + }; + + let fmt = tracing_subscriber::fmt::Layer::default() + .with_level(true) + .boxed(); + + tracing_subscriber::registry().with(fmt).with(filter).init(); + + // Safety: The block range is always a vector of length 2 + let start_block_included = BlockHeight::from(block_range[0]); + // When requested a set of results, the block committer will fetch the data for the next blob which + // might not include the current height. Each blob contains 3_600 blocks, hence we subtract + // this amount from the block height we use for the first request. + let end_block_excluded = BlockHeight::from(block_range[1]); + let block_range = start_block_included..end_block_excluded; + + if end_block_excluded < start_block_included { + return Err(anyhow::anyhow!( + "Invalid block range - start block must be lower than end block: {}..{}", + start_block_included, + end_block_excluded + )); + } + + let block_committer_data_fetcher = + BlockCommitterDataFetcher::new(block_committer_endpoint, 10)?; + + let block_costs = block_committer_data_fetcher + .fetch_l1_block_costs(block_range) + .await?; + + tracing::debug!("{:?}", block_costs); + match l2_block_data_source { + L2BlockDataSource { + db_path: Some(_db_path), + sentry_node_endpoint: None, + } => { + todo!(); + } + L2BlockDataSource { + db_path: None, + sentry_node_endpoint: Some(sentry_node_endpoint), + } => { + tracing::info!( + "Retrieving L2 data from sentry node: {}", + sentry_node_endpoint + ); + let sentry_node_client = layer2::BlockFetcher::new(sentry_node_endpoint)?; + let blocks_range = start_block_included..end_block_excluded; + + tracing::info!( + "Retrieving L2 data for blocks: {}..{}", + start_block_included, + end_block_excluded + ); + let res = sentry_node_client + .get_l2_block_data(blocks_range, SENTRY_NODE_GRAPHQL_RESULTS_PER_QUERY) + .await; + tracing::info!("results: {:?}", res); + + let blocks_with_gas_consumed = res?; + for ( + _block_height, + Layer2BlockData { + block_height, + block_size, + gas_consumed, + capacity, + bytes_capacity, + transactions_count, + }, + ) in &blocks_with_gas_consumed + { + tracing::debug!( + "Block Height: {}, Block Size: {}, Gas Consumed: {}, Capacity: {}, Bytes Capacity: {}, Transactions count: {}", + block_height, **block_size, **gas_consumed, **capacity, **bytes_capacity, transactions_count + ); + } + summary::summarise_available_data( + &output_file, + &block_costs, + &blocks_with_gas_consumed, + ) + .inspect_err(|e| { + tracing::error!("Failed to write to CSV file: {:?}, {:?}", output_file, e) + })?; + } + _ => { + return Err(anyhow::anyhow!( + "Either db-path or sentry-node-endpoint must be provided" + )); + } + }; + + Ok(()) +} diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/summary.rs b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/summary.rs new file mode 100644 index 00000000000..bdc1ba7ccd9 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/summary.rs @@ -0,0 +1,73 @@ +use std::{ + collections::HashMap, + fs::File, + path::Path, +}; + +use fuel_core_types::fuel_types::BlockHeight; + +use crate::types::{ + BlockCommitterCosts, + Layer2BlockData, +}; + +#[derive(Debug, serde::Serialize)] +struct BlockSummary { + l1_block_number: u64, + l1_blob_fee_wei: u128, + l2_block_number: u32, + l2_gas_fullness: u64, + l2_gas_capacity: u64, + l2_byte_size: u64, + l2_byte_capacity: u64, + l2_block_transactions_count: usize, +} + +fn summarise_data_for_block_committer_costs( + costs: &BlockCommitterCosts, + l2_data: &HashMap, +) -> Vec { + tracing::info!("Building summary for: {:?}", costs); + let block_range_len: usize = costs.len().try_into().unwrap_or_default(); + let mut summaries = Vec::with_capacity(block_range_len); + for block_height in costs.iter() { + let Ok(block_height): Result = block_height.try_into() else { + continue + }; + let l2_data = l2_data.get(&block_height); + if let Some(l2_data) = l2_data { + summaries.push(BlockSummary { + l1_block_number: *costs.da_block_height, + l1_blob_fee_wei: *costs.cost, + l2_block_number: block_height, + l2_gas_fullness: *l2_data.gas_consumed, + l2_gas_capacity: *l2_data.capacity, + l2_byte_size: *l2_data.block_size, + l2_byte_capacity: *l2_data.bytes_capacity, + l2_block_transactions_count: l2_data.transactions_count, + }); + } + } + summaries +} + +pub fn summarise_available_data( + output_file_path: &Path, + costs: &[BlockCommitterCosts], + l2_data: &HashMap, +) -> Result<(), anyhow::Error> { + let file_writer = File::create(output_file_path)?; + let mut writer = csv::WriterBuilder::new() + .has_headers(true) + .from_writer(file_writer); + for block_costs_entry in costs { + let summaries = + summarise_data_for_block_committer_costs(block_costs_entry, l2_data); + for summary in summaries { + tracing::debug!("Serializing record: {:?}", summary); + writer.serialize(summary)?; + } + } + writer.flush()?; + Ok(()) +} diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/types.rs b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/types.rs new file mode 100644 index 00000000000..ecbc1642373 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-fetcher/src/types.rs @@ -0,0 +1,103 @@ +use std::{ + iter::Map, + ops::{ + Deref, + RangeInclusive, + }, +}; + +use fuel_core_types::{ + blockchain::primitives::DaBlockHeight, + fuel_types::BlockHeight, +}; +use serde::{ + Deserialize, + Serialize, +}; + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[serde(transparent)] +pub struct Wei(u128); + +impl Deref for Wei { + type Target = u128; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[serde(transparent)] +pub struct GasUnits(pub u64); + +impl Deref for GasUnits { + type Target = u64; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct BytesSize(pub u64); + +impl Deref for BytesSize { + type Target = u64; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct BlockCommitterCosts { + /// The cost of the block, supposedly in Wei but need to check + pub cost: Wei, + pub size: BytesSize, + pub da_block_height: DaBlockHeight, + pub start_height: BlockHeight, + pub end_height: BlockHeight, +} + +impl BlockCommitterCosts { + pub fn iter(&self) -> impl Iterator { + let start_height = *self.start_height; + let end_height = *self.end_height; + (start_height..=end_height) + .map(|raw_block_height| BlockHeight::from(raw_block_height)) + } + + pub fn len(&self) -> u32 { + // Remove 1 from end height because range is inclusive. + self.end_height + .saturating_sub(1) + .saturating_sub(*self.start_height) + } +} + +impl IntoIterator for BlockCommitterCosts { + type Item = BlockHeight; + + // `impl Trait` in associated types is unstable + // see issue #63063 for more information + // We must specify the concrete type here. + type IntoIter = Map, fn(u32) -> BlockHeight>; + + fn into_iter(self) -> Self::IntoIter { + let start_height = *self.start_height; + let end_height = *self.end_height; + (start_height..=end_height) + .map(|raw_block_height| BlockHeight::from(raw_block_height)) + } +} + +#[derive(Debug)] +pub struct Layer2BlockData { + pub block_height: BlockHeight, + pub block_size: BytesSize, + pub gas_consumed: GasUnits, + pub capacity: GasUnits, + pub bytes_capacity: BytesSize, + pub transactions_count: usize, +} diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-reader/.gitignore b/crates/fuel-gas-price-algorithm/gas-price-data-reader/.gitignore new file mode 100644 index 00000000000..ae872b4d900 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-reader/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +data \ No newline at end of file diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-reader/Cargo.toml b/crates/fuel-gas-price-algorithm/gas-price-data-reader/Cargo.toml new file mode 100644 index 00000000000..baa9e89a078 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-reader/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "gas-price-data-reader" +version = "0.1.0" +edition = "2021" + +[dependencies] +csv = "1.3.1" +serde = { version = "1.0.217", features = ["derive"] } + +[workspace] diff --git a/crates/fuel-gas-price-algorithm/gas-price-data-reader/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-data-reader/src/main.rs new file mode 100644 index 00000000000..5e9accdeafc --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-data-reader/src/main.rs @@ -0,0 +1,84 @@ +use std::collections::HashMap; + +const WEI_PER_ETH: f64 = 1_000_000_000_000_000_000.; +// l1_block_number,l1_blob_fee_wei,l2_block_number,l2_gas_fullness,l2_gas_capacity,l2_byte_size,l2_byte_capacity +// 21403864,509018984154240,9099900,0,30000000,488,260096 +// 21403864,509018984154240,9099901,1073531,30000000,3943,260096 +// 21403864,509018984154240,9099902,0,30000000,488,260096 +// parse data +#[derive(Debug, serde::Deserialize, Eq, PartialEq, Hash)] +struct Record { + l1_block_number: u64, + l1_blob_fee_wei: u128, + l2_block_number: u64, + l2_gas_fullness: u64, + l2_gas_capacity: u64, + l2_byte_size: u64, + l2_byte_capacity: u64, +} +fn get_records_from_csv_file(file_path: &str) -> Vec { + let mut rdr = csv::ReaderBuilder::new() + .has_headers(true) + .from_path(file_path) + .unwrap(); + let headers = csv::StringRecord::from(vec![ + "l1_block_number", + "l1_blob_fee_wei", + "l2_block_number", + "l2_gas_fullness", + "l2_gas_capacity", + "l2_byte_size", + "l2_byte_capacity", + ]); + let records = rdr + .records() + .skip(1) + .map(|r| r.unwrap().deserialize(Some(&headers)).unwrap()) + .collect::>(); + records +} + +const ENV_VAR_NAME: &str = "BLOCK_HISTORY_FILE"; +fn get_path_to_file() -> String { + if let Some(path) = std::env::var_os(ENV_VAR_NAME) { + return path.to_str().unwrap().to_string(); + } else { + let maybe_path = std::env::args().nth(1); + if let Some(path) = maybe_path { + return path; + } else { + panic!("Please provide a path to the file or set the {ENV_VAR_NAME} environment variable"); + } + } +} + +fn main() { + let path = get_path_to_file(); + let records = get_records_from_csv_file(&path); + let length = records.len(); + let costs = records + .iter() + .map(|r| (r.l1_block_number, r.l1_blob_fee_wei)) + .collect::>(); + let total_costs: u128 = costs.values().sum(); + let total_l2_gas = records.iter().map(|r| r.l2_gas_fullness).sum::(); + + // println!("Average cost: {}", average); + println!("Length: {}", length); + println!("Total cost: {}", total_costs); + println!("Total cost (ETH): {}", total_costs as f64 / WEI_PER_ETH); + println!( + "Average cost per l2 block: {}", + total_costs / length as u128 + ); + println!( + "Average cost per l2 block (ETH): {}", + (total_costs as f64 / length as f64) / WEI_PER_ETH + ); + // get cost per l2 gas fullness + let average_cost_per_l2_gas_fullness = total_costs / total_l2_gas as u128; + println!( + "Average cost per l2 gas: {}", + average_cost_per_l2_gas_fullness + ); +} diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 2464f196a09..ac670d7b38f 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -19,6 +19,10 @@ pub enum Error { FailedToIncludeL2BlockData(String), #[error("L2 block expected but not found in unrecorded blocks: {height}")] L2BlockExpectedNotFound { height: u32 }, + #[error("Could not insert unrecorded block: {0}")] + CouldNotInsertUnrecordedBlock(String), + #[error("Could not remove unrecorded block: {0}")] + CouldNotRemoveUnrecordedBlock(String), } // TODO: separate exec gas price and DA gas price into newtypes for clarity @@ -59,6 +63,27 @@ impl AlgorithmV1 { } } +pub type Height = u32; +pub type Bytes = u64; + +pub trait UnrecordedBlocks { + fn insert(&mut self, height: Height, bytes: Bytes) -> Result<(), String>; + + fn remove(&mut self, height: &Height) -> Result, String>; +} + +impl UnrecordedBlocks for BTreeMap { + fn insert(&mut self, height: Height, bytes: Bytes) -> Result<(), String> { + self.insert(height, bytes); + Ok(()) + } + + fn remove(&mut self, height: &Height) -> Result, String> { + let value = self.remove(height); + Ok(value) + } +} + /// The state of the algorithm used to update the gas price algorithm for each block /// /// Because there will always be a delay between blocks submitted to the L2 chain and the blocks @@ -96,8 +121,6 @@ impl AlgorithmV1 { /// The DA portion also uses a moving average of the profits over the last `avg_window` blocks /// instead of the actual profit. Setting the `avg_window` to 1 will effectively disable the /// moving average. -type Height = u32; -type Bytes = u64; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] pub struct AlgorithmUpdaterV1 { // Execution @@ -143,8 +166,6 @@ pub struct AlgorithmUpdaterV1 { pub latest_da_cost_per_byte: u128, /// Activity of L2 pub l2_activity: L2ActivityTracker, - /// The unrecorded blocks that are used to calculate the projected cost of recording blocks - pub unrecorded_blocks: BTreeMap, /// Total unrecorded block bytes pub unrecorded_blocks_bytes: u128, } @@ -269,10 +290,28 @@ impl L2ActivityTracker { pub fn current_activity(&self) -> u16 { self.chain_activity } + + pub fn max_activity(&self) -> u16 { + self.max_activity + } + + pub fn capped_activity_threshold(&self) -> u16 { + self.capped_activity_threshold + } + + pub fn decrease_activity_threshold(&self) -> u16 { + self.decrease_activity_threshold + } + + pub fn block_activity_threshold(&self) -> ClampedPercentage { + self.block_activity_threshold + } } /// A value that represents a value between 0 and 100. Higher values are clamped to 100 -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, PartialOrd)] +#[derive( + serde::Serialize, serde::Deserialize, Debug, Copy, Clone, PartialEq, PartialOrd, +)] pub struct ClampedPercentage { value: u8, } @@ -300,27 +339,34 @@ impl core::ops::Deref for ClampedPercentage { } impl AlgorithmUpdaterV1 { - pub fn update_da_record_data( + pub fn update_da_record_data( &mut self, heights: &[u32], recorded_bytes: u32, recording_cost: u128, + unrecorded_blocks: &mut U, ) -> Result<(), Error> { if !heights.is_empty() { - self.da_block_update(heights, recorded_bytes as u128, recording_cost)?; + self.da_block_update( + heights, + recorded_bytes as u128, + recording_cost, + unrecorded_blocks, + )?; self.recalculate_projected_cost(); self.update_da_gas_price(); } Ok(()) } - pub fn update_l2_block_data( + pub fn update_l2_block_data( &mut self, height: u32, used: u64, capacity: NonZeroU64, block_bytes: u64, fee_wei: u128, + unrecorded_blocks: &mut U, ) -> Result<(), Error> { let expected = self.l2_block_height.saturating_add(1); if height != expected { @@ -351,7 +397,9 @@ impl AlgorithmUpdaterV1 { self.update_da_gas_price(); // metadata - self.unrecorded_blocks.insert(height, block_bytes); + unrecorded_blocks + .insert(height, block_bytes) + .map_err(Error::CouldNotInsertUnrecordedBlock)?; self.unrecorded_blocks_bytes = self .unrecorded_blocks_bytes .saturating_add(block_bytes as u128); @@ -512,13 +560,14 @@ impl AlgorithmUpdaterV1 { .saturating_div(100) } - fn da_block_update( + fn da_block_update( &mut self, heights: &[u32], recorded_bytes: u128, recording_cost: u128, + unrecorded_blocks: &mut U, ) -> Result<(), Error> { - self.update_unrecorded_block_bytes(heights); + self.update_unrecorded_block_bytes(heights, unrecorded_blocks)?; let new_da_block_cost = self .latest_known_total_da_cost_excess @@ -540,10 +589,17 @@ impl AlgorithmUpdaterV1 { // Get the bytes for all specified heights, or get none of them. // Always remove the blocks from the unrecorded blocks so they don't build up indefinitely - fn update_unrecorded_block_bytes(&mut self, heights: &[u32]) { + fn update_unrecorded_block_bytes( + &mut self, + heights: &[u32], + unrecorded_blocks: &mut U, + ) -> Result<(), Error> { let mut total: u128 = 0; for expected_height in heights { - let maybe_bytes = self.unrecorded_blocks.remove(expected_height); + let maybe_bytes = unrecorded_blocks + .remove(expected_height) + .map_err(Error::CouldNotRemoveUnrecordedBlock)?; + if let Some(bytes) = maybe_bytes { total = total.saturating_add(bytes as u128); } else { @@ -554,6 +610,8 @@ impl AlgorithmUpdaterV1 { } } self.unrecorded_blocks_bytes = self.unrecorded_blocks_bytes.saturating_sub(total); + + Ok(()) } fn recalculate_projected_cost(&mut self) { diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests.rs index 9e3dc879651..c2dcaba36fa 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests.rs @@ -4,8 +4,11 @@ use crate::v1::{ AlgorithmUpdaterV1, + Bytes, + Height, L2ActivityTracker, }; +use std::collections::BTreeMap; #[cfg(test)] mod algorithm_v1_tests; @@ -38,7 +41,7 @@ pub struct UpdaterBuilder { da_cost_per_byte: u128, project_total_cost: u128, latest_known_total_cost: u128, - unrecorded_blocks: Vec, + unrecorded_blocks_bytes: u64, last_profit: i128, second_to_last_profit: i128, da_gas_price_factor: u64, @@ -65,7 +68,7 @@ impl UpdaterBuilder { da_cost_per_byte: 0, project_total_cost: 0, latest_known_total_cost: 0, - unrecorded_blocks: vec![], + unrecorded_blocks_bytes: 0, last_profit: 0, second_to_last_profit: 0, da_gas_price_factor: 1, @@ -146,8 +149,14 @@ impl UpdaterBuilder { self } - fn with_unrecorded_blocks(mut self, unrecorded_blocks: Vec) -> Self { - self.unrecorded_blocks = unrecorded_blocks; + fn with_unrecorded_blocks( + mut self, + unrecorded_blocks: &BTreeMap, + ) -> Self { + let unrecorded_block_bytes = unrecorded_blocks + .iter() + .fold(0u64, |acc, (_, bytes)| acc + bytes); + self.unrecorded_blocks_bytes = unrecorded_block_bytes; self } @@ -180,11 +189,6 @@ impl UpdaterBuilder { latest_da_cost_per_byte: self.da_cost_per_byte, projected_total_da_cost: self.project_total_cost, latest_known_total_da_cost_excess: self.latest_known_total_cost, - unrecorded_blocks: self - .unrecorded_blocks - .iter() - .map(|b| (b.height, b.block_bytes)) - .collect(), last_profit: self.last_profit, second_to_last_profit: self.second_to_last_profit, min_da_gas_price: self.min_da_gas_price, @@ -193,10 +197,7 @@ impl UpdaterBuilder { .try_into() .expect("Should never be non-zero"), l2_activity: self.l2_activity, - unrecorded_blocks_bytes: self - .unrecorded_blocks - .iter() - .fold(0u128, |acc, b| acc + u128::from(b.block_bytes)), + unrecorded_blocks_bytes: self.unrecorded_blocks_bytes as u128, } } } diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs index 4b87b0d4ad0..6d1feb220b4 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs @@ -1,7 +1,5 @@ -use crate::v1::tests::{ - BlockBytes, - UpdaterBuilder, -}; +use crate::v1::tests::UpdaterBuilder; +use std::collections::BTreeMap; #[test] fn update_da_record_data__if_receives_batch_with_unknown_blocks_will_include_known_blocks_with_previous_cost( @@ -11,14 +9,11 @@ fn update_da_record_data__if_receives_batch_with_unknown_blocks_will_include_kno let recorded_cost = 1_000_000; let recorded_bytes = 500; let block_bytes = 1000; - let unrecorded_blocks = vec![BlockBytes { - height: 1, - block_bytes, - }]; + let mut unrecorded_blocks: BTreeMap<_, _> = [(1, block_bytes)].into_iter().collect(); let cost_per_byte = 333; let known_total_cost = 10_000; let mut updater = UpdaterBuilder::new() - .with_unrecorded_blocks(unrecorded_blocks) + .with_unrecorded_blocks(&unrecorded_blocks) .with_da_cost_per_byte(cost_per_byte) .with_known_total_cost(known_total_cost) .build(); @@ -26,7 +21,12 @@ fn update_da_record_data__if_receives_batch_with_unknown_blocks_will_include_kno // when updater - .update_da_record_data(&recorded_heights, recorded_bytes, recorded_cost) + .update_da_record_data( + &recorded_heights, + recorded_bytes, + recorded_cost, + &mut unrecorded_blocks, + ) .unwrap(); // then @@ -45,14 +45,11 @@ fn update_da_record_data__if_receives_batch_with_unknown_blocks_will_never_incre let recorded_cost = 200; let block_bytes = 1000; let recorded_bytes = 500; - let unrecorded_blocks = vec![BlockBytes { - height: 1, - block_bytes, - }]; + let mut unrecorded_blocks: BTreeMap<_, _> = [(1, block_bytes)].into_iter().collect(); let cost_per_byte = 333; let known_total_cost = 10_000; let mut updater = UpdaterBuilder::new() - .with_unrecorded_blocks(unrecorded_blocks) + .with_unrecorded_blocks(&unrecorded_blocks) .with_da_cost_per_byte(cost_per_byte) .with_known_total_cost(known_total_cost) .build(); @@ -60,7 +57,12 @@ fn update_da_record_data__if_receives_batch_with_unknown_blocks_will_never_incre // when updater - .update_da_record_data(&recorded_heights, recorded_bytes, recorded_cost) + .update_da_record_data( + &recorded_heights, + recorded_bytes, + recorded_cost, + &mut unrecorded_blocks, + ) .unwrap(); // then @@ -76,13 +78,10 @@ fn update_da_record_data__updates_cost_per_byte() { // given let da_cost_per_byte = 20; let block_bytes = 1000; - let unrecorded_blocks = vec![BlockBytes { - height: 1, - block_bytes, - }]; + let mut unrecorded_blocks: BTreeMap<_, _> = [(1, block_bytes)].into_iter().collect(); let mut updater = UpdaterBuilder::new() .with_da_cost_per_byte(da_cost_per_byte) - .with_unrecorded_blocks(unrecorded_blocks) + .with_unrecorded_blocks(&unrecorded_blocks) .build(); let new_cost_per_byte = 100; @@ -91,7 +90,12 @@ fn update_da_record_data__updates_cost_per_byte() { let recorded_heights: Vec = (1u32..2).collect(); // when updater - .update_da_record_data(&recorded_heights, recorded_bytes, recorded_cost) + .update_da_record_data( + &recorded_heights, + recorded_bytes, + recorded_cost, + &mut unrecorded_blocks, + ) .unwrap(); // then @@ -107,26 +111,14 @@ fn update_da_record_data__updates_known_total_cost() { let l2_block_height = 15; let projected_total_cost = 2000; let known_total_cost = 1500; - let unrecorded_blocks = vec![ - BlockBytes { - height: 11, - block_bytes: 1000, - }, - BlockBytes { - height: 12, - block_bytes: 2000, - }, - BlockBytes { - height: 13, - block_bytes: 1500, - }, - ]; + let mut unrecorded_blocks: BTreeMap<_, _> = + [(11, 1000), (12, 2000), (13, 1500)].into_iter().collect(); let mut updater = UpdaterBuilder::new() .with_da_cost_per_byte(da_cost_per_byte) .with_l2_block_height(l2_block_height) .with_projected_total_cost(projected_total_cost) .with_known_total_cost(known_total_cost) - .with_unrecorded_blocks(unrecorded_blocks) + .with_unrecorded_blocks(&unrecorded_blocks) .build(); let recorded_heights: Vec = (11u32..14).collect(); @@ -134,7 +126,12 @@ fn update_da_record_data__updates_known_total_cost() { let recorded_cost = 300; // when updater - .update_da_record_data(&recorded_heights, recorded_bytes, recorded_cost) + .update_da_record_data( + &recorded_heights, + recorded_bytes, + recorded_cost, + &mut unrecorded_blocks, + ) .unwrap(); // then @@ -149,24 +146,12 @@ fn update_da_record_data__if_da_height_matches_l2_height_projected_and_known_mat let da_cost_per_byte = 20; let l2_block_height = 13; let known_total_cost = 1500; - let unrecorded_blocks = vec![ - BlockBytes { - height: 11, - block_bytes: 1000, - }, - BlockBytes { - height: 12, - block_bytes: 2000, - }, - BlockBytes { - height: 13, - block_bytes: 1500, - }, - ]; + let mut unrecorded_blocks: BTreeMap<_, _> = + [(11, 1000), (12, 2000), (13, 1500)].into_iter().collect(); let guessed_cost: u64 = unrecorded_blocks - .iter() - .map(|block| block.block_bytes * da_cost_per_byte) + .values() + .map(|bytes| bytes * da_cost_per_byte) .sum(); let projected_total_cost = known_total_cost + guessed_cost; let mut updater = UpdaterBuilder::new() @@ -174,7 +159,7 @@ fn update_da_record_data__if_da_height_matches_l2_height_projected_and_known_mat .with_l2_block_height(l2_block_height) .with_projected_total_cost(projected_total_cost as u128) .with_known_total_cost(known_total_cost as u128) - .with_unrecorded_blocks(unrecorded_blocks) + .with_unrecorded_blocks(&unrecorded_blocks) .build(); let block_bytes = 1000; @@ -186,11 +171,16 @@ fn update_da_record_data__if_da_height_matches_l2_height_projected_and_known_mat let recorded_cost = block_cost * 3; // when updater - .update_da_record_data(&recorded_heights, recorded_bytes, recorded_cost) + .update_da_record_data( + &recorded_heights, + recorded_bytes, + recorded_cost, + &mut unrecorded_blocks, + ) .unwrap(); // then - assert_eq!(updater.unrecorded_blocks.len(), 0); + assert_eq!(unrecorded_blocks.len(), 0); assert_eq!( updater.projected_total_da_cost, updater.latest_known_total_da_cost_excess @@ -205,35 +195,16 @@ fn update_da_record_data__da_block_updates_projected_total_cost_with_known_and_g let l2_block_height = 15; let original_known_total_cost: u128 = 1500; let block_bytes = 1000; - let mut unrecorded_blocks = vec![ - BlockBytes { - height: 11, - block_bytes: 1000, - }, - BlockBytes { - height: 12, - block_bytes: 2000, - }, - BlockBytes { - height: 13, - block_bytes: 1500, - }, - ]; + let remaining = vec![(14, block_bytes), (15, block_bytes)]; + let mut pairs = vec![(11, 1000), (12, 2000), (13, 1500)]; + + pairs.extend(remaining.clone()); + + let mut unrecorded_blocks: BTreeMap<_, _> = pairs.into_iter().collect(); - let remaining = vec![ - BlockBytes { - height: 14, - block_bytes, - }, - BlockBytes { - height: 15, - block_bytes, - }, - ]; - unrecorded_blocks.extend(remaining.clone()); let guessed_cost: u128 = unrecorded_blocks - .iter() - .map(|block| block.block_bytes as u128 * da_cost_per_byte) + .values() + .map(|bytes| *bytes as u128 * da_cost_per_byte) .sum(); let projected_total_cost: u128 = original_known_total_cost + guessed_cost; let mut updater = UpdaterBuilder::new() @@ -241,7 +212,7 @@ fn update_da_record_data__da_block_updates_projected_total_cost_with_known_and_g .with_l2_block_height(l2_block_height) .with_projected_total_cost(projected_total_cost) .with_known_total_cost(original_known_total_cost) - .with_unrecorded_blocks(unrecorded_blocks) + .with_unrecorded_blocks(&unrecorded_blocks) .build(); let new_cost_per_byte = 100; @@ -252,7 +223,12 @@ fn update_da_record_data__da_block_updates_projected_total_cost_with_known_and_g // when updater - .update_da_record_data(&recorded_heights, recorded_bytes, recorded_cost) + .update_da_record_data( + &recorded_heights, + recorded_bytes, + recorded_cost, + &mut unrecorded_blocks, + ) .unwrap(); // then @@ -260,7 +236,7 @@ fn update_da_record_data__da_block_updates_projected_total_cost_with_known_and_g let new_known_total_cost = original_known_total_cost + recorded_cost; let guessed_part: u128 = remaining .iter() - .map(|block| block.block_bytes as u128 * new_cost_per_byte) + .map(|(_, bytes)| *bytes as u128 * new_cost_per_byte) .sum(); let expected = new_known_total_cost + guessed_part; assert_eq!(actual, expected); @@ -271,27 +247,17 @@ fn update_da_record_data__updates_known_total_cost_if_blocks_are_out_of_order() // given let da_cost_per_byte = 20; let block_bytes = 1000; - let unrecorded_blocks = vec![ - BlockBytes { - height: 1, - block_bytes, - }, - BlockBytes { - height: 2, - block_bytes, - }, - BlockBytes { - height: 3, - block_bytes, - }, - ]; + let mut unrecorded_blocks: BTreeMap<_, _> = + [(1, block_bytes), (2, block_bytes), (3, block_bytes)] + .into_iter() + .collect(); let old_known_total_cost = 500; let old_projected_total_cost = old_known_total_cost + (block_bytes as u128 * da_cost_per_byte * 3); let old_da_cost_per_byte = 20; let mut updater = UpdaterBuilder::new() .with_da_cost_per_byte(da_cost_per_byte) - .with_unrecorded_blocks(unrecorded_blocks) + .with_unrecorded_blocks(&unrecorded_blocks) .with_da_cost_per_byte(old_da_cost_per_byte) .with_known_total_cost(old_known_total_cost) .with_projected_total_cost(old_projected_total_cost) @@ -303,7 +269,12 @@ fn update_da_record_data__updates_known_total_cost_if_blocks_are_out_of_order() // when updater - .update_da_record_data(&recorded_heights, recorded_bytes, recorded_cost as u128) + .update_da_record_data( + &recorded_heights, + recorded_bytes, + recorded_cost as u128, + &mut unrecorded_blocks, + ) .unwrap(); // then @@ -318,27 +289,17 @@ fn update_da_record_data__updates_projected_total_cost_if_blocks_are_out_of_orde // given let da_cost_per_byte = 20; let block_bytes = 1000; - let unrecorded_blocks = vec![ - BlockBytes { - height: 1, - block_bytes, - }, - BlockBytes { - height: 2, - block_bytes, - }, - BlockBytes { - height: 3, - block_bytes, - }, - ]; + let mut unrecorded_blocks: BTreeMap<_, _> = + [(1, block_bytes), (2, block_bytes), (3, block_bytes)] + .into_iter() + .collect(); let old_known_total_cost = 500; let old_projected_total_cost = old_known_total_cost + (block_bytes as u128 * da_cost_per_byte * 3); let old_da_cost_per_byte = 20; let mut updater = UpdaterBuilder::new() .with_da_cost_per_byte(da_cost_per_byte) - .with_unrecorded_blocks(unrecorded_blocks) + .with_unrecorded_blocks(&unrecorded_blocks) .with_da_cost_per_byte(old_da_cost_per_byte) .with_known_total_cost(old_known_total_cost) .with_projected_total_cost(old_projected_total_cost) @@ -350,7 +311,12 @@ fn update_da_record_data__updates_projected_total_cost_if_blocks_are_out_of_orde // when updater - .update_da_record_data(&recorded_heights, recorded_bytes, recorded_cost as u128) + .update_da_record_data( + &recorded_heights, + recorded_bytes, + recorded_cost as u128, + &mut unrecorded_blocks, + ) .unwrap(); // then @@ -365,23 +331,13 @@ fn update_da_record_data__updates_unrecorded_blocks() { // given let da_cost_per_byte = 20; let block_bytes = 1000; - let unrecorded_blocks = vec![ - BlockBytes { - height: 1, - block_bytes, - }, - BlockBytes { - height: 2, - block_bytes, - }, - BlockBytes { - height: 3, - block_bytes, - }, - ]; + let mut unrecorded_blocks: BTreeMap<_, _> = + [(1, block_bytes), (2, block_bytes), (3, block_bytes)] + .into_iter() + .collect(); let mut updater = UpdaterBuilder::new() .with_da_cost_per_byte(da_cost_per_byte) - .with_unrecorded_blocks(unrecorded_blocks) + .with_unrecorded_blocks(&unrecorded_blocks) .build(); let new_cost_per_byte = 100; let recorded_bytes = 500; @@ -390,12 +346,17 @@ fn update_da_record_data__updates_unrecorded_blocks() { // when updater - .update_da_record_data(&recorded_heights, recorded_bytes, recorded_cost) + .update_da_record_data( + &recorded_heights, + recorded_bytes, + recorded_cost, + &mut unrecorded_blocks, + ) .unwrap(); // then let expected = vec![(1, block_bytes)]; - let actual: Vec<_> = updater.unrecorded_blocks.into_iter().collect(); + let actual: Vec<_> = unrecorded_blocks.into_iter().collect(); assert_eq!(actual, expected); } @@ -405,14 +366,11 @@ fn update_da_record_data__da_block_lowers_da_gas_price() { let da_cost_per_byte = 40; let l2_block_height = 11; let original_known_total_cost = 150; - let unrecorded_blocks = vec![BlockBytes { - height: 11, - block_bytes: 3000, - }]; + let mut unrecorded_blocks: BTreeMap<_, _> = [(11, 3000)].into_iter().collect(); let da_p_component = 2; let guessed_cost: u64 = unrecorded_blocks - .iter() - .map(|block| block.block_bytes * da_cost_per_byte) + .values() + .map(|bytes| bytes * da_cost_per_byte) .sum(); let projected_total_cost = original_known_total_cost + guessed_cost; @@ -423,19 +381,19 @@ fn update_da_record_data__da_block_lowers_da_gas_price() { .with_l2_block_height(l2_block_height) .with_projected_total_cost(projected_total_cost as u128) .with_known_total_cost(original_known_total_cost as u128) - .with_unrecorded_blocks(unrecorded_blocks.clone()) + .with_unrecorded_blocks(&unrecorded_blocks) .build(); let new_cost_per_byte = 100; - let (recorded_heights, recorded_cost) = - unrecorded_blocks - .iter() - .fold((vec![], 0), |(mut range, cost), block| { - range.push(block.height); - (range, cost + block.block_bytes * new_cost_per_byte) - }); - let min = recorded_heights.iter().min().unwrap(); - let max = recorded_heights.iter().max().unwrap(); + let (recorded_heights, recorded_cost) = unrecorded_blocks.iter().fold( + (vec![], 0), + |(mut range, cost), (height, bytes)| { + range.push(height); + (range, cost + bytes * new_cost_per_byte) + }, + ); + let min = *recorded_heights.iter().min().unwrap(); + let max = *recorded_heights.iter().max().unwrap(); let recorded_range: Vec = (*min..(max + 1)).collect(); let recorded_bytes = 500; @@ -443,7 +401,12 @@ fn update_da_record_data__da_block_lowers_da_gas_price() { // when updater - .update_da_record_data(&recorded_range, recorded_bytes, recorded_cost as u128) + .update_da_record_data( + &recorded_range, + recorded_bytes, + recorded_cost as u128, + &mut unrecorded_blocks, + ) .unwrap(); // then @@ -459,14 +422,11 @@ fn update_da_record_data__da_block_increases_da_gas_price() { let da_cost_per_byte = 40; let l2_block_height = 11; let original_known_total_cost = 150; - let unrecorded_blocks = vec![BlockBytes { - height: 11, - block_bytes: 3000, - }]; + let mut unrecorded_blocks: BTreeMap<_, _> = [(11, 3000)].into_iter().collect(); let da_p_component = 2; let guessed_cost: u64 = unrecorded_blocks - .iter() - .map(|block| block.block_bytes * da_cost_per_byte) + .values() + .map(|bytes| bytes * da_cost_per_byte) .sum(); let projected_total_cost = original_known_total_cost + guessed_cost; @@ -477,19 +437,20 @@ fn update_da_record_data__da_block_increases_da_gas_price() { .with_l2_block_height(l2_block_height) .with_projected_total_cost(projected_total_cost as u128) .with_known_total_cost(original_known_total_cost as u128) - .with_unrecorded_blocks(unrecorded_blocks.clone()) + .with_unrecorded_blocks(&unrecorded_blocks) .build(); let new_cost_per_byte = 100; - let (recorded_heights, recorded_cost) = - unrecorded_blocks - .iter() - .fold((vec![], 0), |(mut range, cost), block| { - range.push(block.height); - (range, cost + block.block_bytes * new_cost_per_byte) - }); - let min = recorded_heights.iter().min().unwrap(); - let max = recorded_heights.iter().max().unwrap(); + let (recorded_heights, recorded_cost) = unrecorded_blocks.iter().fold( + (vec![], 0), + |(mut range, cost), (height, bytes)| { + range.push(height); + (range, cost + bytes * new_cost_per_byte) + }, + ); + + let min = *recorded_heights.iter().min().unwrap(); + let max = *recorded_heights.iter().max().unwrap(); let recorded_range: Vec = (*min..(max + 1)).collect(); let recorded_bytes = 500; @@ -497,7 +458,12 @@ fn update_da_record_data__da_block_increases_da_gas_price() { // when updater - .update_da_record_data(&recorded_range, recorded_bytes, recorded_cost as u128) + .update_da_record_data( + &recorded_range, + recorded_bytes, + recorded_cost as u128, + &mut unrecorded_blocks, + ) .unwrap(); // then @@ -513,14 +479,11 @@ fn update_da_record_data__da_block_will_not_change_da_gas_price() { let da_cost_per_byte = 40; let l2_block_height = 11; let original_known_total_cost = 150; - let unrecorded_blocks = vec![BlockBytes { - height: 11, - block_bytes: 3000, - }]; + let mut unrecorded_blocks: BTreeMap<_, _> = [(11, 3000)].into_iter().collect(); let da_p_component = 2; let guessed_cost: u64 = unrecorded_blocks - .iter() - .map(|block| block.block_bytes * da_cost_per_byte) + .values() + .map(|bytes| bytes * da_cost_per_byte) .sum(); let projected_total_cost = original_known_total_cost + guessed_cost; @@ -531,19 +494,19 @@ fn update_da_record_data__da_block_will_not_change_da_gas_price() { .with_l2_block_height(l2_block_height) .with_projected_total_cost(projected_total_cost as u128) .with_known_total_cost(original_known_total_cost as u128) - .with_unrecorded_blocks(unrecorded_blocks.clone()) + .with_unrecorded_blocks(&unrecorded_blocks) .build(); let new_cost_per_byte = 100; - let (recorded_heights, recorded_cost) = - unrecorded_blocks - .iter() - .fold((vec![], 0), |(mut range, cost), block| { - range.push(block.height); - (range, cost + block.block_bytes * new_cost_per_byte) - }); - let min = recorded_heights.iter().min().unwrap(); - let max = recorded_heights.iter().max().unwrap(); + let (recorded_heights, recorded_cost) = unrecorded_blocks.iter().fold( + (vec![], 0), + |(mut range, cost), (height, bytes)| { + range.push(height); + (range, cost + bytes * new_cost_per_byte) + }, + ); + let min = *recorded_heights.iter().min().unwrap(); + let max = *recorded_heights.iter().max().unwrap(); let recorded_range: Vec = (*min..(max + 1)).collect(); let recorded_bytes = 500; @@ -551,7 +514,12 @@ fn update_da_record_data__da_block_will_not_change_da_gas_price() { // when updater - .update_da_record_data(&recorded_range, recorded_bytes, recorded_cost as u128) + .update_da_record_data( + &recorded_range, + recorded_bytes, + recorded_cost as u128, + &mut unrecorded_blocks, + ) .unwrap(); // then diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index 8ce3b6b5d4e..6f0dbc4debe 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -3,9 +3,12 @@ use crate::v1::{ BlockBytes, UpdaterBuilder, }, + Bytes, Error, + Height, L2ActivityTracker, }; +use std::collections::BTreeMap; fn decrease_l2_activity() -> L2ActivityTracker { let normal = 1; @@ -62,6 +65,10 @@ fn positive_profit_updater_builder() -> UpdaterBuilder { .with_exec_gas_price_change_percent(0) } +fn empty_unrecorded_blocks() -> BTreeMap { + BTreeMap::new() +} + #[test] fn update_l2_block_data__updates_l2_block() { // given @@ -79,7 +86,14 @@ fn update_l2_block_data__updates_l2_block() { // when updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data( + height, + used, + capacity, + block_bytes, + fee, + &mut empty_unrecorded_blocks(), + ) .unwrap(); // then @@ -104,7 +118,14 @@ fn update_l2_block_data__skipped_block_height_throws_error() { // when let actual_error = updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data( + height, + used, + capacity, + block_bytes, + fee, + &mut empty_unrecorded_blocks(), + ) .unwrap_err(); // then @@ -128,10 +149,11 @@ fn update_l2_block_data__updates_projected_cost() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let fee = 100; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -155,10 +177,18 @@ fn update_l2_block_data__updates_the_total_reward_value() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let fee = 10_000; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, gas_used, capacity, block_bytes, fee) + .update_l2_block_data( + height, + gas_used, + capacity, + block_bytes, + fee, + unrecorded_blocks, + ) .unwrap(); // then @@ -183,10 +213,11 @@ fn update_l2_block_data__even_threshold_will_not_change_exec_gas_price() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let fee = 200; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -212,10 +243,11 @@ fn update_l2_block_data__below_threshold_will_decrease_exec_gas_price() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let fee = 200; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -243,10 +275,11 @@ fn update_l2_block_data__above_threshold_will_increase_exec_gas_price() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let fee = 200; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -275,10 +308,11 @@ fn update_l2_block_data__exec_price_will_not_go_below_min() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let fee = 200; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -305,10 +339,11 @@ fn update_l2_block_data__updates_last_and_last_last_profit() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let fee = 0; // No fee so it's easier to calculate profit + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -326,6 +361,7 @@ fn update_l2_block_data__positive_profit_decrease_gas_price() { // given let mut updater = positive_profit_updater_builder().build(); let old_gas_price = updater.algorithm().calculate(); + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when let block_bytes = 500u64; @@ -336,6 +372,7 @@ fn update_l2_block_data__positive_profit_decrease_gas_price() { 100.try_into().unwrap(), block_bytes, 200, + unrecorded_blocks, ) .unwrap(); @@ -374,6 +411,7 @@ fn update_l2_block_data__price_does_not_decrease_more_than_max_percent() { .with_last_profit(last_profit, last_last_profit) .with_da_max_change_percent(max_da_change_percent) .build(); + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when let height = updater.l2_block_height + 1; @@ -382,7 +420,7 @@ fn update_l2_block_data__price_does_not_decrease_more_than_max_percent() { let block_bytes = 1000; let fee = 200; updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -408,6 +446,7 @@ fn update_l2_block_data__da_price_does_not_increase_more_than_max_percent() { let last_last_profit = 0; let max_da_change_percent = 5; let large_starting_reward = 0; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); let mut updater = UpdaterBuilder::new() .with_starting_exec_gas_price(starting_exec_gas_price) .with_da_p_component(da_p_component) @@ -428,7 +467,7 @@ fn update_l2_block_data__da_price_does_not_increase_more_than_max_percent() { let block_bytes = 1000; let fee = 200; updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -454,6 +493,7 @@ fn update_l2_block_data__never_drops_below_minimum_da_gas_price() { let last_profit = i128::MAX; let avg_window = 10; let large_reward = u128::MAX; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); let mut updater = UpdaterBuilder::new() .with_starting_exec_gas_price(starting_exec_gas_price) .with_min_exec_gas_price(starting_exec_gas_price) @@ -477,6 +517,7 @@ fn update_l2_block_data__never_drops_below_minimum_da_gas_price() { 100.try_into().unwrap(), 1000, fee, + unrecorded_blocks, ) .unwrap(); @@ -497,6 +538,7 @@ fn update_l2_block_data__even_profit_maintains_price() { let da_gas_price_denominator = 1; let block_bytes = 500u64; let starting_reward = starting_cost; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); let mut updater = UpdaterBuilder::new() .with_starting_exec_gas_price(starting_exec_gas_price) .with_starting_da_gas_price(starting_da_gas_price) @@ -518,6 +560,7 @@ fn update_l2_block_data__even_profit_maintains_price() { 100.try_into().unwrap(), block_bytes, total_fee.into(), + unrecorded_blocks, ) .unwrap(); let algo = updater.algorithm(); @@ -534,6 +577,7 @@ fn update_l2_block_data__negative_profit_increase_gas_price() { let mut updater = negative_profit_updater_builder().build(); let algo = updater.algorithm(); let old_gas_price = algo.calculate(); + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when let height = updater.l2_block_height + 1; @@ -542,7 +586,7 @@ fn update_l2_block_data__negative_profit_increase_gas_price() { let block_bytes = 500u64; let fee = 0; updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -570,10 +614,18 @@ fn update_l2_block_data__adds_l2_block_to_unrecorded_blocks() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let new_gas_price = 100; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, used, capacity, block_bytes, new_gas_price) + .update_l2_block_data( + height, + used, + capacity, + block_bytes, + new_gas_price, + unrecorded_blocks, + ) .unwrap(); // then @@ -582,7 +634,7 @@ fn update_l2_block_data__adds_l2_block_to_unrecorded_blocks() { block_bytes, }; let expected = block_bytes.block_bytes; - let actual = updater.unrecorded_blocks.get(&block_bytes.height).unwrap(); + let actual = unrecorded_blocks.get(&block_bytes.height).unwrap(); assert_eq!(expected, *actual); } @@ -592,14 +644,13 @@ fn update_l2_block_data__retains_existing_blocks_and_adds_l2_block_to_unrecorded // given let starting_block = 0; let first_block_bytes = 1200; - let preexisting_block = BlockBytes { - height: 0, - block_bytes: first_block_bytes, - }; + let mut unrecorded_blocks: BTreeMap<_, _> = vec![(starting_block, first_block_bytes)] + .into_iter() + .collect(); let mut updater = UpdaterBuilder::new() .with_l2_block_height(starting_block) - .with_unrecorded_blocks(vec![preexisting_block.clone()]) + .with_unrecorded_blocks(&unrecorded_blocks) .build(); let height = 1; @@ -610,7 +661,14 @@ fn update_l2_block_data__retains_existing_blocks_and_adds_l2_block_to_unrecorded // when updater - .update_l2_block_data(height, used, capacity, new_block_bytes, new_gas_price) + .update_l2_block_data( + height, + used, + capacity, + new_block_bytes, + new_gas_price, + &mut unrecorded_blocks, + ) .unwrap(); // then @@ -618,14 +676,12 @@ fn update_l2_block_data__retains_existing_blocks_and_adds_l2_block_to_unrecorded height, block_bytes: new_block_bytes, }; - let contains_block_bytes = - updater.unrecorded_blocks.contains_key(&block_bytes.height); + let contains_block_bytes = unrecorded_blocks.contains_key(&block_bytes.height); assert!(contains_block_bytes); // and - let contains_preexisting_block_bytes = updater - .unrecorded_blocks - .contains_key(&preexisting_block.height); + let contains_preexisting_block_bytes = + unrecorded_blocks.contains_key(&starting_block); assert!(contains_preexisting_block_bytes); // and @@ -653,6 +709,7 @@ fn update_l2_block_data__da_gas_price_wants_to_increase_will_hold_if_activity_in .build(); let algo = updater.algorithm(); let old_gas_price = algo.calculate(); + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when let height = updater.l2_block_height + 1; @@ -661,7 +718,7 @@ fn update_l2_block_data__da_gas_price_wants_to_increase_will_hold_if_activity_in let block_bytes = 500u64; let fee = 0; updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -679,6 +736,7 @@ fn update_l2_block_data__da_gas_price_wants_to_decrease_will_decrease_if_activit .with_activity(capped_activity) .build(); let old_gas_price = updater.algorithm().calculate(); + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when let block_bytes = 500u64; @@ -689,6 +747,7 @@ fn update_l2_block_data__da_gas_price_wants_to_decrease_will_decrease_if_activit 100.try_into().unwrap(), block_bytes, 200, + unrecorded_blocks, ) .unwrap(); @@ -712,6 +771,7 @@ fn update_l2_block_data__da_gas_price_wants_to_increase_will_decrease_if_activit .build(); let algo = updater.algorithm(); let old_gas_price = algo.calculate(); + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when let height = updater.l2_block_height + 1; @@ -720,7 +780,7 @@ fn update_l2_block_data__da_gas_price_wants_to_increase_will_decrease_if_activit let block_bytes = 500u64; let fee = 0; updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -743,6 +803,7 @@ fn update_l2_block_data__da_gas_price_wants_to_decrease_will_decrease_if_activit .with_activity(decrease_activity) .build(); let old_gas_price = updater.algorithm().calculate(); + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when let block_bytes = 500u64; @@ -753,6 +814,7 @@ fn update_l2_block_data__da_gas_price_wants_to_decrease_will_decrease_if_activit 100.try_into().unwrap(), block_bytes, 200, + unrecorded_blocks, ) .unwrap(); @@ -786,10 +848,11 @@ fn update_l2_block_data__above_threshold_increase_activity() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let fee = 200; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -818,10 +881,11 @@ fn update_l2_block_data__below_threshold_decrease_activity() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let fee = 200; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -859,10 +923,11 @@ fn update_l2_block_data__if_activity_at_max_will_stop_increasing() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let fee = 200; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then @@ -891,10 +956,11 @@ fn update_l2_block_data__if_activity_is_zero_will_stop_decreasing() { let capacity = 100.try_into().unwrap(); let block_bytes = 1000; let fee = 200; + let unrecorded_blocks = &mut empty_unrecorded_blocks(); // when updater - .update_l2_block_data(height, used, capacity, block_bytes, fee) + .update_l2_block_data(height, used, capacity, block_bytes, fee, unrecorded_blocks) .unwrap(); // then diff --git a/crates/services/consensus_module/poa/Cargo.toml b/crates/services/consensus_module/poa/Cargo.toml index 5d71379853d..3a04386767e 100644 --- a/crates/services/consensus_module/poa/Cargo.toml +++ b/crates/services/consensus_module/poa/Cargo.toml @@ -12,12 +12,10 @@ version = { workspace = true } [dependencies] anyhow = { workspace = true } async-trait = { workspace = true } -aws-sdk-kms = { version = "1.37.0", optional = true } fuel-core-chain-config = { workspace = true } fuel-core-services = { workspace = true } fuel-core-storage = { workspace = true, features = ["std"] } fuel-core-types = { workspace = true, features = ["std"] } -k256 = { version = "0.13.3", features = ["ecdsa-core"], optional = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tokio = { workspace = true, features = ["full"] } @@ -25,7 +23,6 @@ tokio-stream = { workspace = true } tracing = { workspace = true } [dev-dependencies] -aws-config = { version = "1.1.7", features = ["behavior-version-latest"] } fuel-core-poa = { path = ".", features = ["test-helpers"] } fuel-core-services = { workspace = true, features = ["test-helpers"] } fuel-core-storage = { path = "./../../../storage", features = ["test-helpers"] } @@ -37,7 +34,6 @@ test-case = { workspace = true } tokio = { workspace = true, features = ["full", "test-util"] } [features] -aws-kms = ["dep:aws-sdk-kms", "dep:k256"] test-helpers = [ "fuel-core-storage/test-helpers", "fuel-core-types/test-helpers", diff --git a/crates/services/consensus_module/poa/src/config.rs b/crates/services/consensus_module/poa/src/config.rs index c2a11ff1fca..9336ff672e0 100644 --- a/crates/services/consensus_module/poa/src/config.rs +++ b/crates/services/consensus_module/poa/src/config.rs @@ -1,8 +1,9 @@ -use fuel_core_types::fuel_types::ChainId; +use fuel_core_types::{ + fuel_types::ChainId, + signer::SignMode, +}; use tokio::time::Duration; -use crate::signer::SignMode; - #[derive(Debug, Clone)] pub struct Config { pub trigger: Trigger, diff --git a/crates/services/consensus_module/poa/src/lib.rs b/crates/services/consensus_module/poa/src/lib.rs index 19dfeb826cf..7b5e32871fe 100644 --- a/crates/services/consensus_module/poa/src/lib.rs +++ b/crates/services/consensus_module/poa/src/lib.rs @@ -13,7 +13,6 @@ mod service_test; pub mod config; pub mod ports; pub mod service; -pub mod signer; pub mod verifier; pub use config::{ diff --git a/crates/services/consensus_module/poa/src/service.rs b/crates/services/consensus_module/poa/src/service.rs index dc09c2d4dc2..abd916d0b92 100644 --- a/crates/services/consensus_module/poa/src/service.rs +++ b/crates/services/consensus_module/poa/src/service.rs @@ -123,9 +123,8 @@ pub(crate) enum RequestType { Manual, Trigger, } - pub struct MainTask { - signer: S, + signer: Arc, block_producer: B, block_importer: I, txpool: T, @@ -157,7 +156,7 @@ where block_producer: B, block_importer: I, p2p_port: P, - signer: S, + signer: Arc, predefined_blocks: PB, clock: C, ) -> Self { @@ -613,7 +612,7 @@ pub fn new_service( block_producer: B, block_importer: I, p2p_port: P, - block_signer: S, + block_signer: Arc, predefined_blocks: PB, clock: C, ) -> Service diff --git a/crates/services/consensus_module/poa/src/service_test.rs b/crates/services/consensus_module/poa/src/service_test.rs index 5230e03f42b..99ff0c1aa7c 100644 --- a/crates/services/consensus_module/poa/src/service_test.rs +++ b/crates/services/consensus_module/poa/src/service_test.rs @@ -15,7 +15,6 @@ use crate::{ TransactionsSource, }, service::MainTask, - signer::SignMode, Config, Service, Trigger, @@ -31,7 +30,10 @@ use fuel_core_storage::transactional::Changes; use fuel_core_types::{ blockchain::{ block::Block, - consensus::Consensus, + consensus::{ + poa::PoAConsensus, + Consensus, + }, header::{ BlockHeader, PartialBlockHeader, @@ -51,6 +53,7 @@ use fuel_core_types::{ ExecutionResult, UncommittedResult, }, + signer::SignMode, tai64::{ Tai64, Tai64N, @@ -184,7 +187,7 @@ impl TestContextBuilder { producer, importer, p2p_port, - FakeBlockSigner { succeeds: true }, + FakeBlockSigner { succeeds: true }.into(), predefined_blocks, watch, ); @@ -201,9 +204,12 @@ struct FakeBlockSigner { impl BlockSigner for FakeBlockSigner { async fn seal_block(&self, block: &Block) -> anyhow::Result { if self.succeeds { - SignMode::Key(Secret::new(default_consensus_dev_key().into())) - .seal_block(block) - .await + let signature = + SignMode::Key(Secret::new(default_consensus_dev_key().into())) + .sign_message(block.id().into_message()) + .await?; + + Ok(Consensus::PoA(PoAConsensus::new(signature))) } else { Err(anyhow::anyhow!("failed to sign block")) } @@ -368,7 +374,7 @@ async fn remove_skipped_transactions() { block_producer, block_importer, p2p_port, - FakeBlockSigner { succeeds: true }, + FakeBlockSigner { succeeds: true }.into(), predefined_blocks, time.watch(), ); @@ -487,7 +493,7 @@ async fn consensus_service__run__will_include_sequential_predefined_blocks_befor block_producer, block_importer, generate_p2p_port(), - FakeBlockSigner { succeeds: true }, + FakeBlockSigner { succeeds: true }.into(), InMemoryPredefinedBlocks::new(blocks_map), time.watch(), ); @@ -551,7 +557,7 @@ async fn consensus_service__run__will_insert_predefined_blocks_in_correct_order( block_producer, block_importer, generate_p2p_port(), - FakeBlockSigner { succeeds: true }, + FakeBlockSigner { succeeds: true }.into(), InMemoryPredefinedBlocks::new(predefined_blocks_map), time.watch(), ); diff --git a/crates/services/executor/src/executor.rs b/crates/services/executor/src/executor.rs index bc690c52d3d..1277b143d9c 100644 --- a/crates/services/executor/src/executor.rs +++ b/crates/services/executor/src/executor.rs @@ -1626,7 +1626,12 @@ where .iter() .map(|input| input.predicate_gas_used()) .collect(); - let ready_tx = checked_tx.into_ready(gas_price, gas_costs, fee_params)?; + let ready_tx = checked_tx.into_ready( + gas_price, + gas_costs, + fee_params, + Some(*header.height()), + )?; let mut vm = Interpreter::with_storage( memory, diff --git a/crates/services/gas_price_service/src/common/fuel_core_storage_adapter.rs b/crates/services/gas_price_service/src/common/fuel_core_storage_adapter.rs index 4d452421ba1..506885fe78b 100644 --- a/crates/services/gas_price_service/src/common/fuel_core_storage_adapter.rs +++ b/crates/services/gas_price_service/src/common/fuel_core_storage_adapter.rs @@ -1,40 +1,49 @@ -use crate::common::utils::{ - BlockInfo, - Error as GasPriceError, - Result as GasPriceResult, -}; -use anyhow::anyhow; -use fuel_core_types::fuel_types::BlockHeight; - use crate::{ common::{ fuel_core_storage_adapter::storage::{ + BundleIdTable, GasPriceColumn, GasPriceMetadata, }, updater_metadata::UpdaterMetadata, + utils::{ + BlockInfo, + Error as GasPriceError, + Result as GasPriceResult, + }, + }, + ports::{ + GasPriceServiceAtomicStorage, + GetDaBundleId, + GetMetadataStorage, + SetDaBundleId, + SetMetadataStorage, }, - ports::MetadataStorage, }; +use anyhow::anyhow; +use core::cmp::min; use fuel_core_storage::{ codec::{ postcard::Postcard, Encode, }, kv_store::KeyValueInspect, - structured_storage::StructuredStorage, transactional::{ Modifiable, + StorageTransaction, WriteTransaction, }, + Error as StorageError, StorageAsMut, StorageAsRef, + StorageInspect, }; use fuel_core_types::{ blockchain::{ block::Block, header::ConsensusParametersVersion, }, + fuel_merkle::storage::StorageMutate, fuel_tx::{ field::{ MintAmount, @@ -42,18 +51,39 @@ use fuel_core_types::{ }, Transaction, }, + fuel_types::BlockHeight, }; -use std::cmp::min; #[cfg(test)] mod metadata_tests; pub mod storage; -impl MetadataStorage for StructuredStorage +impl SetMetadataStorage for Storage +where + Storage: Send + Sync, + Storage: Modifiable, + for<'a> StorageTransaction<&'a mut Storage>: + StorageMutate, +{ + fn set_metadata(&mut self, metadata: &UpdaterMetadata) -> GasPriceResult<()> { + let block_height = metadata.l2_block_height(); + let mut tx = self.write_transaction(); + tx.storage_as_mut::() + .insert(&block_height, metadata) + .and_then(|_| tx.commit()) + .map_err(|err| GasPriceError::CouldNotSetMetadata { + block_height, + source_error: err.into(), + })?; + Ok(()) + } +} + +impl GetMetadataStorage for Storage where - Storage: KeyValueInspect + Modifiable, Storage: Send + Sync, + Storage: StorageInspect, { fn get_metadata( &self, @@ -67,17 +97,57 @@ where })?; Ok(metadata.map(|inner| inner.into_owned())) } +} - fn set_metadata(&mut self, metadata: &UpdaterMetadata) -> GasPriceResult<()> { - let block_height = metadata.l2_block_height(); - let mut tx = self.write_transaction(); - tx.storage_as_mut::() - .insert(&block_height, metadata) - .and_then(|_| tx.commit()) - .map_err(|err| GasPriceError::CouldNotSetMetadata { - block_height, - source_error: err.into(), - })?; +impl GetDaBundleId for Storage +where + Storage: Send + Sync, + Storage: StorageInspect, +{ + fn get_bundle_id(&self, block_height: &BlockHeight) -> GasPriceResult> { + let bundle_id = self + .storage::() + .get(block_height) + .map_err(|err| GasPriceError::CouldNotFetchDARecord(err.into()))? + .map(|no| *no); + Ok(bundle_id) + } +} + +impl GasPriceServiceAtomicStorage for Storage +where + Storage: 'static, + Storage: GetMetadataStorage + GetDaBundleId, + Storage: KeyValueInspect + Modifiable + Send + Sync, +{ + type Transaction<'a> = StorageTransaction<&'a mut Storage> where Self: 'a; + + fn begin_transaction(&mut self) -> GasPriceResult> { + let tx = self.write_transaction(); + Ok(tx) + } + + fn commit_transaction(transaction: Self::Transaction<'_>) -> GasPriceResult<()> { + transaction + .commit() + .map_err(|err| GasPriceError::CouldNotCommit(err.into()))?; + Ok(()) + } +} + +impl SetDaBundleId for Storage +where + Storage: Send + Sync, + Storage: StorageMutate, +{ + fn set_bundle_id( + &mut self, + block_height: &BlockHeight, + bundle_id: u32, + ) -> GasPriceResult<()> { + self.storage_as_mut::() + .insert(block_height, &bundle_id) + .map_err(|err| GasPriceError::CouldNotFetchDARecord(err.into()))?; Ok(()) } } @@ -87,6 +157,7 @@ pub struct GasPriceSettings { pub gas_price_factor: u64, pub block_gas_limit: u64, } + pub trait GasPriceSettingsProvider: Send + Sync + Clone { fn settings( &self, @@ -107,6 +178,7 @@ pub fn get_block_info( block_gas_capacity: block_gas_limit, block_bytes: Postcard::encode(block).len() as u64, block_fees: fee, + gas_price, }; Ok(info) } diff --git a/crates/services/gas_price_service/src/common/fuel_core_storage_adapter/storage.rs b/crates/services/gas_price_service/src/common/fuel_core_storage_adapter/storage.rs index 43d6835dcc1..e9f20b411d8 100644 --- a/crates/services/gas_price_service/src/common/fuel_core_storage_adapter/storage.rs +++ b/crates/services/gas_price_service/src/common/fuel_core_storage_adapter/storage.rs @@ -27,6 +27,8 @@ use fuel_core_types::fuel_types::BlockHeight; pub enum GasPriceColumn { Metadata = 0, State = 1, + UnrecordedBlocks = 2, + BundleId = 3, } impl GasPriceColumn { @@ -68,3 +70,46 @@ impl TableWithBlueprint for GasPriceMetadata { GasPriceColumn::State } } + +/// The storage for all the unrecorded blocks from gas price algorithm, used for guessing the cost +/// for future blocks to be recorded on the DA chain +pub struct UnrecordedBlocksTable; + +type BlockSizeInBytes = u64; + +impl Mappable for UnrecordedBlocksTable { + type Key = Self::OwnedKey; + type OwnedKey = BlockHeight; + type Value = Self::OwnedValue; + type OwnedValue = BlockSizeInBytes; +} + +impl TableWithBlueprint for UnrecordedBlocksTable { + type Blueprint = Plain, Postcard>; + type Column = GasPriceColumn; + + fn column() -> Self::Column { + GasPriceColumn::UnrecordedBlocks + } +} + +pub struct BundleIdTable; + +/// The sequence number or bundle id of the posted blocks. +type BundleId = u32; + +impl Mappable for BundleIdTable { + type Key = Self::OwnedKey; + type OwnedKey = BlockHeight; + type Value = Self::OwnedValue; + type OwnedValue = BundleId; +} + +impl TableWithBlueprint for BundleIdTable { + type Blueprint = Plain, Postcard>; + type Column = GasPriceColumn; + + fn column() -> Self::Column { + GasPriceColumn::BundleId + } +} diff --git a/crates/services/gas_price_service/src/common/utils.rs b/crates/services/gas_price_service/src/common/utils.rs index a3813e53e4f..5c944bd9fb1 100644 --- a/crates/services/gas_price_service/src/common/utils.rs +++ b/crates/services/gas_price_service/src/common/utils.rs @@ -19,6 +19,8 @@ pub enum Error { CouldNotInitUpdater(anyhow::Error), #[error("Failed to convert metadata to concrete type. THere is no migration path for this metadata version")] CouldNotConvertMetadata, // todo(https://github.com/FuelLabs/fuel-core/issues/2286) + #[error("Failed to commit to storage: {0:?}")] + CouldNotCommit(anyhow::Error), } pub type Result = core::result::Result; @@ -40,5 +42,7 @@ pub enum BlockInfo { block_bytes: u64, // The fees the block has collected block_fees: u64, + // The gas price used in the block + gas_price: u64, }, } diff --git a/crates/services/gas_price_service/src/ports.rs b/crates/services/gas_price_service/src/ports.rs index cb829ab21fb..0ccd77cb01a 100644 --- a/crates/services/gas_price_service/src/ports.rs +++ b/crates/services/gas_price_service/src/ports.rs @@ -1,16 +1,20 @@ +use fuel_core_storage::Result as StorageResult; +use fuel_core_types::{ + blockchain::block::Block, + fuel_tx::Transaction, + fuel_types::BlockHeight, +}; + use crate::{ common::{ updater_metadata::UpdaterMetadata, utils::Result, }, v0::metadata::V0AlgorithmConfig, - v1::metadata::V1AlgorithmConfig, -}; -use fuel_core_storage::Result as StorageResult; -use fuel_core_types::{ - blockchain::block::Block, - fuel_tx::Transaction, - fuel_types::BlockHeight, + v1::{ + metadata::V1AlgorithmConfig, + uninitialized_task::fuel_storage_unrecorded_blocks::AsUnrecordedBlocks, + }, }; pub trait L2Data: Send + Sync { @@ -21,10 +25,41 @@ pub trait L2Data: Send + Sync { ) -> StorageResult>>; } -pub trait MetadataStorage: Send + Sync { +pub trait SetMetadataStorage: Send + Sync { + fn set_metadata(&mut self, metadata: &UpdaterMetadata) -> Result<()>; +} + +pub trait GetMetadataStorage: Send + Sync { fn get_metadata(&self, block_height: &BlockHeight) -> Result>; - fn set_metadata(&mut self, metadata: &UpdaterMetadata) -> Result<()>; +} + +pub trait SetDaBundleId: Send + Sync { + fn set_bundle_id(&mut self, block_height: &BlockHeight, bundle_id: u32) + -> Result<()>; +} + +pub trait GetDaBundleId: Send + Sync { + fn get_bundle_id(&self, block_height: &BlockHeight) -> Result>; +} + +pub trait GasPriceServiceAtomicStorage +where + Self: 'static, + Self: Send + Sync, + Self: GetMetadataStorage + GetDaBundleId, +{ + type Transaction<'a>: AsUnrecordedBlocks + + SetMetadataStorage + + GetMetadataStorage + + SetDaBundleId + + GetDaBundleId + where + Self: 'a; + + fn begin_transaction(&mut self) -> Result>; + + fn commit_transaction(transaction: Self::Transaction<'_>) -> Result<()>; } /// Provides the latest block height. diff --git a/crates/services/gas_price_service/src/v0/metadata.rs b/crates/services/gas_price_service/src/v0/metadata.rs index f42e33e041c..d16f0c078ae 100644 --- a/crates/services/gas_price_service/src/v0/metadata.rs +++ b/crates/services/gas_price_service/src/v0/metadata.rs @@ -1,11 +1,23 @@ use fuel_gas_price_algorithm::v0::AlgorithmUpdaterV0; +/// DO NOT TOUCH! DEPLOYED TO MAINNET +/// FIELDS PREFIXED WITH _ ARE UNUSED BUT EXIST ON MAINNET DB's +/// IF YOU WANT TO MODIFY THIS, MAKE A MIGRATION IN crates/fuel-core/src/service/genesis/importer/gas_price.rs #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] pub struct V0Metadata { /// The gas price to cover the execution of the next block pub new_exec_price: u64, + // Execution + /// The lowest the algorithm allows the exec gas price to go + pub _min_exec_gas_price: u64, + /// The Percentage the execution gas price will change in a single block, either increase or decrease + /// based on the fullness of the last L2 block + pub _exec_gas_price_change_percent: u64, /// The height for which the `new_exec_price` is calculated, which should be the _next_ block pub l2_block_height: u32, + /// The threshold of gas usage above and below which the gas price will increase or decrease + /// This is a percentage of the total capacity of the L2 block + pub _l2_block_fullness_threshold_percent: u64, } pub struct V0AlgorithmConfig { @@ -20,6 +32,10 @@ impl From for V0Metadata { Self { new_exec_price: updater.new_exec_price, l2_block_height: updater.l2_block_height, + _min_exec_gas_price: updater.min_exec_gas_price, + _exec_gas_price_change_percent: updater.exec_gas_price_change_percent, + _l2_block_fullness_threshold_percent: updater + .l2_block_fullness_threshold_percent, } } } diff --git a/crates/services/gas_price_service/src/v0/service.rs b/crates/services/gas_price_service/src/v0/service.rs index 557d8cd6842..e0a794c0da9 100644 --- a/crates/services/gas_price_service/src/v0/service.rs +++ b/crates/services/gas_price_service/src/v0/service.rs @@ -4,7 +4,10 @@ use crate::{ updater_metadata::UpdaterMetadata, utils::BlockInfo, }, - ports::MetadataStorage, + ports::{ + GetMetadataStorage, + SetMetadataStorage, + }, v0::algorithm::SharedV0Algorithm, }; use anyhow::anyhow; @@ -35,7 +38,7 @@ pub struct GasPriceServiceV0 { impl GasPriceServiceV0 where - Metadata: MetadataStorage, + Metadata: GetMetadataStorage + SetMetadataStorage, { pub fn new( l2_block_source: L2, @@ -120,7 +123,7 @@ where impl GasPriceServiceV0 where L2: L2BlockSource, - Metadata: MetadataStorage, + Metadata: GetMetadataStorage + SetMetadataStorage, { async fn process_l2_block_res( &mut self, @@ -138,7 +141,7 @@ where impl RunnableTask for GasPriceServiceV0 where L2: L2BlockSource, - Metadata: MetadataStorage, + Metadata: GetMetadataStorage + SetMetadataStorage, { async fn run(&mut self, watcher: &mut StateWatcher) -> TaskNextAction { tracing::trace!("Call of `run` function of the gas price service v0"); @@ -178,7 +181,10 @@ mod tests { Result as GasPriceResult, }, }, - ports::MetadataStorage, + ports::{ + GetMetadataStorage, + SetMetadataStorage, + }, v0::{ metadata::V0AlgorithmConfig, service::GasPriceServiceV0, @@ -217,7 +223,14 @@ mod tests { } } - impl MetadataStorage for FakeMetadata { + impl SetMetadataStorage for FakeMetadata { + fn set_metadata(&mut self, metadata: &UpdaterMetadata) -> GasPriceResult<()> { + *self.inner.lock().unwrap() = Some(metadata.clone()); + Ok(()) + } + } + + impl GetMetadataStorage for FakeMetadata { fn get_metadata( &self, _: &BlockHeight, @@ -225,11 +238,6 @@ mod tests { let metadata = self.inner.lock().unwrap().clone(); Ok(metadata) } - - fn set_metadata(&mut self, metadata: &UpdaterMetadata) -> GasPriceResult<()> { - *self.inner.lock().unwrap() = Some(metadata.clone()); - Ok(()) - } } #[tokio::test] @@ -242,6 +250,7 @@ mod tests { block_gas_capacity: 100, block_bytes: 100, block_fees: 100, + gas_price: 100, }; let (l2_block_sender, l2_block_receiver) = mpsc::channel(1); diff --git a/crates/services/gas_price_service/src/v0/tests.rs b/crates/services/gas_price_service/src/v0/tests.rs index 294c5029a4d..85aeb4fe36a 100644 --- a/crates/services/gas_price_service/src/v0/tests.rs +++ b/crates/services/gas_price_service/src/v0/tests.rs @@ -16,8 +16,9 @@ use crate::{ }, ports::{ GasPriceData, + GetMetadataStorage, L2Data, - MetadataStorage, + SetMetadataStorage, }, v0::{ metadata::{ @@ -86,27 +87,23 @@ impl FakeMetadata { } } -impl MetadataStorage for FakeMetadata { - fn get_metadata(&self, _: &BlockHeight) -> GasPriceResult> { - let metadata = self.inner.lock().unwrap().clone(); - Ok(metadata) - } - +impl SetMetadataStorage for FakeMetadata { fn set_metadata(&mut self, metadata: &UpdaterMetadata) -> GasPriceResult<()> { *self.inner.lock().unwrap() = Some(metadata.clone()); Ok(()) } } -struct ErroringMetadata; - -impl MetadataStorage for ErroringMetadata { +impl GetMetadataStorage for FakeMetadata { fn get_metadata(&self, _: &BlockHeight) -> GasPriceResult> { - Err(GasPriceError::CouldNotFetchMetadata { - source_error: anyhow!("boo!"), - }) + let metadata = self.inner.lock().unwrap().clone(); + Ok(metadata) } +} + +struct ErroringMetadata; +impl SetMetadataStorage for ErroringMetadata { fn set_metadata(&mut self, _: &UpdaterMetadata) -> GasPriceResult<()> { Err(GasPriceError::CouldNotSetMetadata { block_height: Default::default(), @@ -115,6 +112,14 @@ impl MetadataStorage for ErroringMetadata { } } +impl GetMetadataStorage for ErroringMetadata { + fn get_metadata(&self, _: &BlockHeight) -> GasPriceResult> { + Err(GasPriceError::CouldNotFetchMetadata { + source_error: anyhow!("boo!"), + }) + } +} + fn arbitrary_config() -> V0AlgorithmConfig { V0AlgorithmConfig { starting_gas_price: 100, @@ -128,6 +133,9 @@ fn arbitrary_metadata() -> V0Metadata { V0Metadata { new_exec_price: 100, l2_block_height: 0, + _min_exec_gas_price: 0, + _exec_gas_price_change_percent: 0, + _l2_block_fullness_threshold_percent: 0, } } @@ -149,6 +157,7 @@ async fn next_gas_price__affected_by_new_l2_block() { block_gas_capacity: 100, block_bytes: 100, block_fees: 100, + gas_price: 100, }; let (l2_block_sender, l2_block_receiver) = tokio::sync::mpsc::channel(1); let l2_block_source = FakeL2BlockSource { @@ -192,6 +201,7 @@ async fn next__new_l2_block_saves_old_metadata() { block_gas_capacity: 100, block_bytes: 100, block_fees: 100, + gas_price: 100, }; let (l2_block_sender, l2_block_receiver) = tokio::sync::mpsc::channel(1); let l2_block_source = FakeL2BlockSource { @@ -334,6 +344,7 @@ async fn uninitialized_task__new__if_exists_already_reload_old_values_with_overr let V0Metadata { new_exec_price, l2_block_height, + .. } = original_metadata; let UninitializedTask { algo_updater, .. } = service; assert_eq!(algo_updater.new_exec_price, new_exec_price); diff --git a/crates/services/gas_price_service/src/v0/uninitialized_task.rs b/crates/services/gas_price_service/src/v0/uninitialized_task.rs index 569739605c7..0dd0e395d68 100644 --- a/crates/services/gas_price_service/src/v0/uninitialized_task.rs +++ b/crates/services/gas_price_service/src/v0/uninitialized_task.rs @@ -18,7 +18,7 @@ use crate::{ GasPriceData, GasPriceServiceConfig, L2Data, - MetadataStorage, + SetMetadataStorage, }, v0::{ algorithm::SharedV0Algorithm, @@ -45,6 +45,7 @@ use fuel_core_types::{ }; use fuel_gas_price_algorithm::v0::AlgorithmUpdaterV0; +use crate::ports::GetMetadataStorage; pub use fuel_gas_price_algorithm::v0::AlgorithmV0; pub struct UninitializedTask { @@ -65,7 +66,7 @@ where L2DataStore: L2Data, L2DataStoreView: AtomicView, GasPriceStore: GasPriceData, - Metadata: MetadataStorage, + Metadata: GetMetadataStorage + SetMetadataStorage, SettingsProvider: GasPriceSettingsProvider, { pub fn new( @@ -166,7 +167,7 @@ where L2DataStore: L2Data, L2DataStoreView: AtomicView, GasPriceStore: GasPriceData, - Metadata: MetadataStorage, + Metadata: GetMetadataStorage + SetMetadataStorage, SettingsProvider: GasPriceSettingsProvider, { const NAME: &'static str = "GasPriceServiceV0"; @@ -193,7 +194,7 @@ pub fn initialize_algorithm( metadata_storage: &Metadata, ) -> GasPriceResult<(AlgorithmUpdaterV0, SharedV0Algorithm)> where - Metadata: MetadataStorage, + Metadata: GetMetadataStorage + SetMetadataStorage, { let min_exec_gas_price = config.min_gas_price; let exec_gas_price_change_percent = config.gas_price_change_percent; @@ -243,7 +244,7 @@ fn sync_gas_price_db_with_on_chain_storage< where L2DataStore: L2Data, L2DataStoreView: AtomicView, - Metadata: MetadataStorage, + Metadata: GetMetadataStorage + SetMetadataStorage, SettingsProvider: GasPriceSettingsProvider, { let metadata = metadata_storage @@ -287,7 +288,7 @@ fn sync_v0_metadata( where L2DataStore: L2Data, L2DataStoreView: AtomicView, - Metadata: MetadataStorage, + Metadata: SetMetadataStorage, SettingsProvider: GasPriceSettingsProvider, { let first = metadata_height.saturating_add(1); @@ -344,7 +345,7 @@ where L2DataStoreView: AtomicView, GasPriceStore: GasPriceData, SettingsProvider: GasPriceSettingsProvider, - Metadata: MetadataStorage, + Metadata: GetMetadataStorage + SetMetadataStorage, { let v0_config = config.v0().ok_or(anyhow::anyhow!("Expected V0 config"))?; let gas_price_init = UninitializedTask::new( diff --git a/crates/services/gas_price_service/src/v1/da_source_service.rs b/crates/services/gas_price_service/src/v1/da_source_service.rs index ff2c703b518..bae8d3d46bc 100644 --- a/crates/services/gas_price_service/src/v1/da_source_service.rs +++ b/crates/services/gas_price_service/src/v1/da_source_service.rs @@ -2,13 +2,15 @@ use crate::v1::da_source_service::service::DaBlockCostsSource; use std::time::Duration; pub mod block_committer_costs; +#[cfg(test)] pub mod dummy_costs; pub mod service; #[derive(Debug, Default, Clone, Eq, Hash, PartialEq)] pub struct DaBlockCosts { + pub bundle_id: u32, pub l2_blocks: Vec, - pub blob_size_bytes: u32, + pub bundle_size_bytes: u32, pub blob_cost_wei: u128, } @@ -30,8 +32,9 @@ mod tests { async fn run__when_da_block_cost_source_gives_value_shared_state_is_updated() { // given let expected_da_cost = DaBlockCosts { + bundle_id: 1, l2_blocks: (0..10).collect(), - blob_size_bytes: 1024 * 128, + bundle_size_bytes: 1024 * 128, blob_cost_wei: 2, }; let notifier = Arc::new(tokio::sync::Notify::new()); diff --git a/crates/services/gas_price_service/src/v1/da_source_service/block_committer_costs.rs b/crates/services/gas_price_service/src/v1/da_source_service/block_committer_costs.rs index 59498849996..98c024933c8 100644 --- a/crates/services/gas_price_service/src/v1/da_source_service/block_committer_costs.rs +++ b/crates/services/gas_price_service/src/v1/da_source_service/block_committer_costs.rs @@ -40,7 +40,7 @@ pub struct BlockCommitterDaBlockCosts { #[derive(Debug, Deserialize, Serialize, Clone, Default, PartialEq)] pub struct RawDaBlockCosts { /// Sequence number (Monotonically increasing nonce) - pub sequence_number: u32, + pub bundle_id: u32, /// The range of blocks that the costs apply to pub blocks_heights: Vec, /// The DA block height of the last transaction for the range of blocks @@ -54,12 +54,13 @@ pub struct RawDaBlockCosts { impl From<&RawDaBlockCosts> for DaBlockCosts { fn from(raw_da_block_costs: &RawDaBlockCosts) -> Self { DaBlockCosts { + bundle_id: raw_da_block_costs.bundle_id, l2_blocks: raw_da_block_costs .blocks_heights .clone() .into_iter() .collect(), - blob_size_bytes: raw_da_block_costs.total_size_bytes, + bundle_size_bytes: raw_da_block_costs.total_size_bytes, blob_cost_wei: raw_da_block_costs.total_cost, } } @@ -82,9 +83,9 @@ where { async fn request_da_block_cost(&mut self) -> DaBlockCostsResult { let raw_da_block_costs = match self.last_raw_da_block_costs { - Some(ref last_value) => self - .client - .get_costs_by_seqno(last_value.sequence_number + 1), + Some(ref last_value) => { + self.client.get_costs_by_seqno(last_value.bundle_id + 1) + } _ => self.client.get_latest_costs(), } .await?; @@ -98,7 +99,7 @@ where |costs: DaBlockCostsResult, last_value| { let costs = costs.expect("Defined to be OK"); let blob_size_bytes = costs - .blob_size_bytes + .bundle_size_bytes .checked_sub(last_value.total_size_bytes) .ok_or(anyhow!("Blob size bytes underflow"))?; let blob_cost_wei = raw_da_block_costs @@ -106,7 +107,7 @@ where .checked_sub(last_value.total_cost) .ok_or(anyhow!("Blob cost wei underflow"))?; Ok(DaBlockCosts { - blob_size_bytes, + bundle_size_bytes: blob_size_bytes, blob_cost_wei, ..costs }) @@ -116,6 +117,10 @@ where self.last_raw_da_block_costs = Some(raw_da_block_costs.clone()); Ok(da_block_costs) } + async fn set_last_value(&mut self, bundle_id: u32) -> DaBlockCostsResult<()> { + self.last_raw_da_block_costs = self.client.get_costs_by_seqno(bundle_id).await?; + Ok(()) + } } pub struct BlockCommitterHttpApi { @@ -201,7 +206,7 @@ mod tests { // arbitrary logic to generate a new value let mut value = self.value.clone(); if let Some(value) = &mut value { - value.sequence_number = seq_no; + value.bundle_id = seq_no; value.blocks_heights = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .to_vec() .iter() @@ -224,7 +229,7 @@ mod tests { fn test_da_block_costs() -> RawDaBlockCosts { RawDaBlockCosts { - sequence_number: 1, + bundle_id: 1, blocks_heights: (0..10).collect(), da_block_height: 1u64.into(), total_cost: 1, @@ -299,7 +304,7 @@ mod tests { // arbitrary logic to generate a new value let mut value = self.value.clone(); if let Some(value) = &mut value { - value.sequence_number = seq_no; + value.bundle_id = seq_no; value.blocks_heights = value.blocks_heights.iter().map(|x| x + seq_no).collect(); value.da_block_height = value.da_block_height + 1u64.into(); diff --git a/crates/services/gas_price_service/src/v1/da_source_service/dummy_costs.rs b/crates/services/gas_price_service/src/v1/da_source_service/dummy_costs.rs index 8fe22a89e0b..5204ea5fba0 100644 --- a/crates/services/gas_price_service/src/v1/da_source_service/dummy_costs.rs +++ b/crates/services/gas_price_service/src/v1/da_source_service/dummy_costs.rs @@ -34,4 +34,8 @@ impl DaBlockCostsSource for DummyDaBlockCosts { } } } + + async fn set_last_value(&mut self, _bundle_id: u32) -> DaBlockCostsResult<()> { + unimplemented!("This is a dummy implementation"); + } } diff --git a/crates/services/gas_price_service/src/v1/da_source_service/service.rs b/crates/services/gas_price_service/src/v1/da_source_service/service.rs index 328f73e6a20..d7dfca30a20 100644 --- a/crates/services/gas_price_service/src/v1/da_source_service/service.rs +++ b/crates/services/gas_price_service/src/v1/da_source_service/service.rs @@ -32,10 +32,7 @@ impl SharedState { /// This struct houses the shared_state, polling interval /// and a source, which does the actual fetching of the data -pub struct DaSourceService -where - Source: DaBlockCostsSource, -{ +pub struct DaSourceService { poll_interval: Interval, source: Source, shared_state: SharedState, @@ -72,6 +69,7 @@ where #[async_trait::async_trait] pub trait DaBlockCostsSource: Send + Sync { async fn request_da_block_cost(&mut self) -> Result; + async fn set_last_value(&mut self, bundle_id: u32) -> Result<()>; } #[async_trait::async_trait] diff --git a/crates/services/gas_price_service/src/v1/metadata.rs b/crates/services/gas_price_service/src/v1/metadata.rs index 54000623bac..ef2e88466f5 100644 --- a/crates/services/gas_price_service/src/v1/metadata.rs +++ b/crates/services/gas_price_service/src/v1/metadata.rs @@ -3,10 +3,7 @@ use fuel_gas_price_algorithm::v1::{ AlgorithmUpdaterV1, L2ActivityTracker, }; -use std::{ - collections::BTreeMap, - num::NonZeroU64, -}; +use std::num::NonZeroU64; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] pub struct V1Metadata { @@ -30,9 +27,9 @@ pub struct V1Metadata { pub second_to_last_profit: i128, /// The latest known cost per byte for recording blocks on the DA chain pub latest_da_cost_per_byte: u128, - /// List of (height, size) for l2 blocks that have not been recorded on the DA chain (that we know), + /// Track the total bytes of all l2 blocks that have not been recorded on the DA chain (that we know), /// but have been used to estimate the cost of recording blocks on the DA chain - pub unrecorded_blocks: Vec<(u32, u64)>, + pub unrecorded_block_bytes: u128, } impl V1Metadata { @@ -54,17 +51,20 @@ impl V1Metadata { last_profit: 0, second_to_last_profit: 0, latest_da_cost_per_byte: 0, - unrecorded_blocks: vec![], + unrecorded_block_bytes: 0, }; Ok(metadata) } } +#[derive(Debug, Clone, PartialEq)] pub struct V1AlgorithmConfig { pub new_exec_gas_price: u64, pub min_exec_gas_price: u64, pub exec_gas_price_change_percent: u16, pub l2_block_fullness_threshold_percent: u8, + // TODO:We don't need this after we implement + // https://github.com/FuelLabs/fuel-core/issues/2481 pub gas_price_factor: NonZeroU64, pub min_da_gas_price: u64, pub max_da_gas_price_change_percent: u16, @@ -74,51 +74,40 @@ pub struct V1AlgorithmConfig { pub capped_range_size: u16, pub decrease_range_size: u16, pub block_activity_threshold: u8, - pub unrecorded_blocks: Vec<(u32, u64)>, } -impl From<&V1AlgorithmConfig> for AlgorithmUpdaterV1 { - fn from(value: &V1AlgorithmConfig) -> Self { - let l2_activity = L2ActivityTracker::new_full( - value.normal_range_size, - value.capped_range_size, - value.decrease_range_size, - value.block_activity_threshold.into(), - ); - let unrecorded_blocks: BTreeMap<_, _> = - value.unrecorded_blocks.clone().into_iter().collect(); - let unrecorded_blocks_bytes: u128 = unrecorded_blocks - .values() - .map(|size| u128::from(*size)) - .sum(); - Self { - // TODO:We don't need this after we implement - // https://github.com/FuelLabs/fuel-core/issues/2481 - new_scaled_exec_price: value - .new_exec_gas_price - .saturating_mul(value.gas_price_factor.get()), - l2_block_height: 0, - new_scaled_da_gas_price: value.min_da_gas_price, - gas_price_factor: value.gas_price_factor, - total_da_rewards_excess: 0, - latest_known_total_da_cost_excess: 0, - projected_total_da_cost: 0, - last_profit: 0, - second_to_last_profit: 0, - latest_da_cost_per_byte: 0, - l2_activity, - min_exec_gas_price: value.min_exec_gas_price, - exec_gas_price_change_percent: value.exec_gas_price_change_percent, - l2_block_fullness_threshold_percent: value - .l2_block_fullness_threshold_percent - .into(), - min_da_gas_price: value.min_da_gas_price, - max_da_gas_price_change_percent: value.max_da_gas_price_change_percent, - da_p_component: value.da_p_component, - da_d_component: value.da_d_component, - unrecorded_blocks, - unrecorded_blocks_bytes, - } +pub fn updater_from_config(value: &V1AlgorithmConfig) -> AlgorithmUpdaterV1 { + let l2_activity = L2ActivityTracker::new_full( + value.normal_range_size, + value.capped_range_size, + value.decrease_range_size, + value.block_activity_threshold.into(), + ); + let unrecorded_blocks_bytes = 0; + AlgorithmUpdaterV1 { + new_scaled_exec_price: value + .new_exec_gas_price + .saturating_mul(value.gas_price_factor.get()), + l2_block_height: 0, + new_scaled_da_gas_price: value.min_da_gas_price, + gas_price_factor: value.gas_price_factor, + total_da_rewards_excess: 0, + latest_known_total_da_cost_excess: 0, + projected_total_da_cost: 0, + last_profit: 0, + second_to_last_profit: 0, + latest_da_cost_per_byte: 0, + l2_activity, + min_exec_gas_price: value.min_exec_gas_price, + exec_gas_price_change_percent: value.exec_gas_price_change_percent, + l2_block_fullness_threshold_percent: value + .l2_block_fullness_threshold_percent + .into(), + min_da_gas_price: value.min_da_gas_price, + max_da_gas_price_change_percent: value.max_da_gas_price_change_percent, + da_p_component: value.da_p_component, + da_d_component: value.da_d_component, + unrecorded_blocks_bytes, } } @@ -134,7 +123,7 @@ impl From for V1Metadata { last_profit: updater.last_profit, second_to_last_profit: updater.second_to_last_profit, latest_da_cost_per_byte: updater.latest_da_cost_per_byte, - unrecorded_blocks: updater.unrecorded_blocks.into_iter().collect(), + unrecorded_block_bytes: updater.unrecorded_blocks_bytes, } } } @@ -149,17 +138,12 @@ pub fn v1_algorithm_from_metadata( config.decrease_range_size, config.block_activity_threshold.into(), ); - let unrecorded_blocks_bytes: u128 = metadata - .unrecorded_blocks - .iter() - .map(|(_, size)| u128::from(*size)) - .sum(); + let unrecorded_blocks_bytes: u128 = metadata.unrecorded_block_bytes; let projected_portion = unrecorded_blocks_bytes.saturating_mul(metadata.latest_da_cost_per_byte); let projected_total_da_cost = metadata .latest_known_total_da_cost_excess .saturating_add(projected_portion); - let unrecorded_blocks = metadata.unrecorded_blocks.into_iter().collect(); AlgorithmUpdaterV1 { new_scaled_exec_price: metadata.new_scaled_exec_price, l2_block_height: metadata.l2_block_height, @@ -181,7 +165,6 @@ pub fn v1_algorithm_from_metadata( max_da_gas_price_change_percent: config.max_da_gas_price_change_percent, da_p_component: config.da_p_component, da_d_component: config.da_d_component, - unrecorded_blocks, unrecorded_blocks_bytes, } } diff --git a/crates/services/gas_price_service/src/v1/service.rs b/crates/services/gas_price_service/src/v1/service.rs index f95649144a3..0e9b4c2f741 100644 --- a/crates/services/gas_price_service/src/v1/service.rs +++ b/crates/services/gas_price_service/src/v1/service.rs @@ -3,9 +3,18 @@ use crate::{ gas_price_algorithm::SharedGasPriceAlgo, l2_block_source::L2BlockSource, updater_metadata::UpdaterMetadata, - utils::BlockInfo, + utils::{ + BlockInfo, + Result as GasPriceResult, + }, + }, + ports::{ + GasPriceServiceAtomicStorage, + GetDaBundleId, + GetMetadataStorage, + SetDaBundleId, + SetMetadataStorage, }, - ports::MetadataStorage, v0::metadata::V0Metadata, v1::{ algorithm::SharedV1Algorithm, @@ -18,10 +27,15 @@ use crate::{ DaBlockCosts, }, metadata::{ + updater_from_config, v1_algorithm_from_metadata, V1AlgorithmConfig, V1Metadata, }, + uninitialized_task::fuel_storage_unrecorded_blocks::{ + AsUnrecordedBlocks, + FuelStorageUnrecordedBlocks, + }, }, }; use anyhow::anyhow; @@ -32,94 +46,124 @@ use fuel_core_services::{ StateWatcher, TaskNextAction, }; +use fuel_core_types::fuel_types::BlockHeight; use fuel_gas_price_algorithm::{ v0::AlgorithmUpdaterV0, v1::{ AlgorithmUpdaterV1, AlgorithmV1, + UnrecordedBlocks, }, }; use futures::FutureExt; -use std::num::NonZeroU64; -use tokio::sync::broadcast::{ - error::RecvError, - Receiver, +use std::{ + num::NonZeroU64, + sync::Arc, }; +use tokio::sync::broadcast::Receiver; + +#[derive(Debug, Clone)] +pub struct LatestGasPrice { + inner: Arc>, +} + +impl LatestGasPrice { + pub fn new(height: Height, price: GasPrice) -> Self { + let pair = (height, price); + let inner = Arc::new(parking_lot::RwLock::new(pair)); + Self { inner } + } + + pub fn set(&mut self, height: Height, price: GasPrice) { + *self.inner.write() = (height, price); + } +} -use crate::common::utils::Result as GasPriceResult; +impl LatestGasPrice { + pub fn get(&self) -> (Height, GasPrice) { + *self.inner.read() + } +} /// The service that updates the gas price algorithm. -pub struct GasPriceServiceV1 -where - DA: DaBlockCostsSource, -{ +pub struct GasPriceServiceV1 { /// The algorithm that can be used in the next block shared_algo: SharedV1Algorithm, + /// The latest gas price + latest_gas_price: LatestGasPrice, /// The L2 block source l2_block_source: L2, - /// The metadata storage - metadata_storage: Metadata, /// The algorithm updater algorithm_updater: AlgorithmUpdaterV1, /// the da source adapter handle da_source_adapter_handle: DaSourceService, /// The da source channel da_source_channel: Receiver, + /// Buffer of block costs from the DA chain + da_block_costs_buffer: Vec, + /// Storage transaction provider for metadata and unrecorded blocks + storage_tx_provider: StorageTxProvider, } -impl GasPriceServiceV1 +impl GasPriceServiceV1 { + pub(crate) fn update_latest_gas_price(&mut self, block_info: &BlockInfo) { + match block_info { + BlockInfo::GenesisBlock => { + // do nothing + } + BlockInfo::Block { + height, gas_price, .. + } => { + self.latest_gas_price.set(*height, *gas_price); + } + } + } +} + +impl GasPriceServiceV1 where L2: L2BlockSource, - Metadata: MetadataStorage, DA: DaBlockCostsSource, + AtomicStorage: GasPriceServiceAtomicStorage, { - async fn process_l2_block_res( + async fn commit_block_data_to_algorithm( &mut self, l2_block_res: GasPriceResult, ) -> anyhow::Result<()> { tracing::info!("Received L2 block result: {:?}", l2_block_res); let block = l2_block_res?; + self.update_latest_gas_price(&block); tracing::debug!("Updating gas price algorithm"); self.apply_block_info_to_gas_algorithm(block).await?; Ok(()) } - - async fn process_da_block_costs_res( - &mut self, - da_block_costs: Result, - ) -> anyhow::Result<()> { - tracing::info!("Received DA block costs: {:?}", da_block_costs); - let da_block_costs = da_block_costs?; - - tracing::debug!("Updating DA block costs"); - self.apply_da_block_costs_to_gas_algorithm(da_block_costs) - .await?; - Ok(()) - } } -impl GasPriceServiceV1 +impl GasPriceServiceV1 where - Metadata: MetadataStorage, DA: DaBlockCostsSource, + AtomicStorage: GasPriceServiceAtomicStorage, { pub fn new( l2_block_source: L2, - metadata_storage: Metadata, shared_algo: SharedV1Algorithm, + latest_gas_price: LatestGasPrice, algorithm_updater: AlgorithmUpdaterV1, da_source_adapter_handle: DaSourceService, + storage_tx_provider: AtomicStorage, ) -> Self { let da_source_channel = da_source_adapter_handle.shared_data().clone().subscribe(); Self { shared_algo, + latest_gas_price, l2_block_source, - metadata_storage, algorithm_updater, da_source_adapter_handle, da_source_channel, + da_block_costs_buffer: Vec::new(), + storage_tx_provider, } } @@ -131,25 +175,22 @@ where self.shared_algo.clone() } + #[cfg(test)] + pub fn storage_tx_provider(&self) -> &AtomicStorage { + &self.storage_tx_provider + } + async fn update(&mut self, new_algorithm: AlgorithmV1) { self.shared_algo.update(new_algorithm).await; } fn validate_block_gas_capacity( - &self, block_gas_capacity: u64, ) -> anyhow::Result { NonZeroU64::new(block_gas_capacity) .ok_or_else(|| anyhow!("Block gas capacity must be non-zero")) } - async fn set_metadata(&mut self) -> anyhow::Result<()> { - let metadata: UpdaterMetadata = self.algorithm_updater.clone().into(); - self.metadata_storage - .set_metadata(&metadata) - .map_err(|err| anyhow!(err)) - } - async fn handle_normal_block( &mut self, height: u32, @@ -158,7 +199,29 @@ where block_bytes: u64, block_fees: u64, ) -> anyhow::Result<()> { - let capacity = self.validate_block_gas_capacity(block_gas_capacity)?; + let capacity = Self::validate_block_gas_capacity(block_gas_capacity)?; + let mut storage_tx = self.storage_tx_provider.begin_transaction()?; + let prev_height = height.saturating_sub(1); + let mut bundle_id = storage_tx + .get_bundle_id(&BlockHeight::from(prev_height)) + .map_err(|err| anyhow!(err))?; + + for da_block_costs in &self.da_block_costs_buffer { + tracing::debug!("Updating DA block costs: {:?}", da_block_costs); + self.algorithm_updater.update_da_record_data( + &da_block_costs.l2_blocks, + da_block_costs.bundle_size_bytes, + da_block_costs.blob_cost_wei, + &mut storage_tx.as_unrecorded_blocks(), + )?; + bundle_id = Some(da_block_costs.bundle_id); + } + + if let Some(bundle_id) = bundle_id { + storage_tx + .set_bundle_id(&BlockHeight::from(height), bundle_id) + .map_err(|err| anyhow!(err))?; + } self.algorithm_updater.update_l2_block_data( height, @@ -166,23 +229,18 @@ where capacity, block_bytes, block_fees as u128, + &mut storage_tx.as_unrecorded_blocks(), )?; - self.set_metadata().await?; - Ok(()) - } - - async fn handle_da_block_costs( - &mut self, - da_block_costs: DaBlockCosts, - ) -> anyhow::Result<()> { - self.algorithm_updater.update_da_record_data( - &da_block_costs.l2_blocks, - da_block_costs.blob_size_bytes, - da_block_costs.blob_cost_wei, - )?; - - self.set_metadata().await?; + let metadata = self.algorithm_updater.clone().into(); + storage_tx + .set_metadata(&metadata) + .map_err(|err| anyhow!(err))?; + AtomicStorage::commit_transaction(storage_tx)?; + let new_algo = self.algorithm_updater.algorithm(); + self.shared_algo.update(new_algo).await; + // Clear the buffer after committing changes + self.da_block_costs_buffer.clear(); Ok(()) } @@ -192,7 +250,12 @@ where ) -> anyhow::Result<()> { match l2_block { BlockInfo::GenesisBlock => { - self.set_metadata().await?; + let metadata: UpdaterMetadata = self.algorithm_updater.clone().into(); + let mut tx = self.storage_tx_provider.begin_transaction()?; + tx.set_metadata(&metadata).map_err(|err| anyhow!(err))?; + AtomicStorage::commit_transaction(tx)?; + let new_algo = self.algorithm_updater.algorithm(); + self.shared_algo.update(new_algo).await; } BlockInfo::Block { height, @@ -200,6 +263,7 @@ where block_gas_capacity, block_bytes, block_fees, + .. } => { self.handle_normal_block( height, @@ -212,26 +276,16 @@ where } } - self.update(self.algorithm_updater.algorithm()).await; - Ok(()) - } - - async fn apply_da_block_costs_to_gas_algorithm( - &mut self, - da_block_costs: DaBlockCosts, - ) -> anyhow::Result<()> { - self.handle_da_block_costs(da_block_costs).await?; - self.update(self.algorithm_updater.algorithm()).await; Ok(()) } } #[async_trait] -impl RunnableTask for GasPriceServiceV1 +impl RunnableTask for GasPriceServiceV1 where L2: L2BlockSource, - Metadata: MetadataStorage, DA: DaBlockCostsSource, + AtomicStorage: GasPriceServiceAtomicStorage, { async fn run(&mut self, watcher: &mut StateWatcher) -> TaskNextAction { tokio::select! { @@ -241,12 +295,22 @@ where TaskNextAction::Stop } l2_block_res = self.l2_block_source.get_l2_block() => { - let res = self.process_l2_block_res(l2_block_res).await; + tracing::debug!("Received L2 block result: {:?}", l2_block_res); + let res = self.commit_block_data_to_algorithm(l2_block_res).await; TaskNextAction::always_continue(res) } - da_block_costs = self.da_source_channel.recv() => { - let res = self.process_da_block_costs_res(da_block_costs).await; - TaskNextAction::always_continue(res) + da_block_costs_res = self.da_source_channel.recv() => { + tracing::debug!("Received DA block costs: {:?}", da_block_costs_res); + match da_block_costs_res { + Ok(da_block_costs) => { + self.da_block_costs_buffer.push(da_block_costs); + TaskNextAction::Continue + }, + Err(err) => { + let err = anyhow!("Error receiving DA block costs: {:?}", err); + TaskNextAction::ErrorContinue(err) + } + } } } } @@ -258,12 +322,6 @@ where self.apply_block_info_to_gas_algorithm(block).await?; } - while let Ok(da_block_costs) = self.da_source_channel.try_recv() { - tracing::debug!("Updating DA block costs"); - self.apply_da_block_costs_to_gas_algorithm(da_block_costs) - .await?; - } - // run shutdown hooks for internal services self.da_source_adapter_handle.shutdown().await?; @@ -295,20 +353,18 @@ pub fn initialize_algorithm( metadata_storage: &Metadata, ) -> crate::common::utils::Result<(AlgorithmUpdaterV1, SharedV1Algorithm)> where - Metadata: MetadataStorage, + Metadata: GetMetadataStorage, { - let algorithm_updater; - if let Some(updater_metadata) = metadata_storage + let algorithm_updater = if let Some(updater_metadata) = metadata_storage .get_metadata(&latest_block_height.into()) .map_err(|err| { crate::common::utils::Error::CouldNotInitUpdater(anyhow::anyhow!(err)) - })? - { + })? { let v1_metadata = convert_to_v1_metadata(updater_metadata, config)?; - algorithm_updater = v1_algorithm_from_metadata(v1_metadata, config); + v1_algorithm_from_metadata(v1_metadata, config) } else { - algorithm_updater = AlgorithmUpdaterV1::from(config); - } + updater_from_config(config) + }; let shared_algo = SharedGasPriceAlgo::new_with_algorithm(algorithm_updater.algorithm()); @@ -320,8 +376,39 @@ where #[allow(non_snake_case)] #[cfg(test)] mod tests { + use std::{ + num::NonZeroU64, + sync::Arc, + time::Duration, + }; + + use tokio::sync::mpsc; + + use fuel_core_services::{ + RunnableTask, + StateWatcher, + }; + use fuel_core_storage::{ + structured_storage::test::InMemoryStorage, + tables::merkle::DenseMetadataKey::Latest, + transactional::{ + IntoTransaction, + StorageTransaction, + WriteTransaction, + }, + StorageAsMut, + }; + use fuel_core_types::fuel_types::BlockHeight; + use crate::{ common::{ + fuel_core_storage_adapter::storage::{ + BundleIdTable, + GasPriceColumn, + GasPriceColumn::UnrecordedBlocks, + UnrecordedBlocksTable, + }, + gas_price_algorithm::SharedGasPriceAlgo, l2_block_source::L2BlockSource, updater_metadata::UpdaterMetadata, utils::{ @@ -329,31 +416,28 @@ mod tests { Result as GasPriceResult, }, }, - ports::MetadataStorage, + ports::{ + GetMetadataStorage, + SetMetadataStorage, + }, v1::{ da_source_service::{ dummy_costs::DummyDaBlockCosts, service::DaSourceService, DaBlockCosts, }, - metadata::V1AlgorithmConfig, + metadata::{ + updater_from_config, + V1AlgorithmConfig, + }, service::{ initialize_algorithm, GasPriceServiceV1, + LatestGasPrice, }, + uninitialized_task::fuel_storage_unrecorded_blocks::FuelStorageUnrecordedBlocks, }, }; - use fuel_core_services::{ - RunnableTask, - StateWatcher, - }; - use fuel_core_types::fuel_types::BlockHeight; - use std::{ - num::NonZeroU64, - sync::Arc, - time::Duration, - }; - use tokio::sync::mpsc; struct FakeL2BlockSource { l2_block: mpsc::Receiver, @@ -379,7 +463,14 @@ mod tests { } } - impl MetadataStorage for FakeMetadata { + impl SetMetadataStorage for FakeMetadata { + fn set_metadata(&mut self, metadata: &UpdaterMetadata) -> GasPriceResult<()> { + *self.inner.lock().unwrap() = Some(metadata.clone()); + Ok(()) + } + } + + impl GetMetadataStorage for FakeMetadata { fn get_metadata( &self, _: &BlockHeight, @@ -387,11 +478,9 @@ mod tests { let metadata = self.inner.lock().unwrap().clone(); Ok(metadata) } - - fn set_metadata(&mut self, metadata: &UpdaterMetadata) -> GasPriceResult<()> { - *self.inner.lock().unwrap() = Some(metadata.clone()); - Ok(()) - } + } + fn database() -> StorageTransaction> { + InMemoryStorage::default().into_transaction() } #[tokio::test] @@ -404,6 +493,7 @@ mod tests { block_gas_capacity: 100, block_bytes: 100, block_fees: 100, + gas_price: 100, }; let (l2_block_sender, l2_block_receiver) = mpsc::channel(1); @@ -427,8 +517,8 @@ mod tests { capped_range_size: 100, decrease_range_size: 4, block_activity_threshold: 20, - unrecorded_blocks: vec![], }; + let inner = database(); let (algo_updater, shared_algo) = initialize_algorithm(&config, l2_block_height, &metadata_storage).unwrap(); @@ -440,13 +530,15 @@ mod tests { ), None, ); + let latest_gas_price = LatestGasPrice::new(0, 0); let mut service = GasPriceServiceV1::new( l2_block_source, - metadata_storage, shared_algo, + latest_gas_price, algo_updater, dummy_da_source, + inner, ); let read_algo = service.next_block_algorithm(); let mut watcher = StateWatcher::default(); @@ -465,13 +557,14 @@ mod tests { #[tokio::test] async fn run__updates_gas_price_with_da_block_cost_source() { // given - let block_height = 1; - let l2_block = BlockInfo::Block { + let block_height = 2; + let l2_block_2 = BlockInfo::Block { height: block_height, gas_used: 60, block_gas_capacity: 100, block_bytes: 100, block_fees: 100, + gas_price: 100, }; let (l2_block_sender, l2_block_receiver) = mpsc::channel(1); @@ -480,45 +573,58 @@ mod tests { }; let metadata_storage = FakeMetadata::empty(); + // Configured so exec gas price doesn't change, only da gas price let config = V1AlgorithmConfig { new_exec_gas_price: 100, min_exec_gas_price: 50, - exec_gas_price_change_percent: 20, + exec_gas_price_change_percent: 0, l2_block_fullness_threshold_percent: 20, gas_price_factor: NonZeroU64::new(10).unwrap(), - min_da_gas_price: 100, - max_da_gas_price_change_percent: 50, + min_da_gas_price: 0, + max_da_gas_price_change_percent: 100, da_p_component: 4, da_d_component: 2, normal_range_size: 10, capped_range_size: 100, decrease_range_size: 4, block_activity_threshold: 20, - unrecorded_blocks: vec![(1, 100)], }; - let (algo_updater, shared_algo) = - initialize_algorithm(&config, block_height, &metadata_storage).unwrap(); + let mut inner = database(); + let mut tx = inner.write_transaction(); + tx.storage_as_mut::() + .insert(&BlockHeight::from(1), &100) + .unwrap(); + tx.commit().unwrap(); + let mut algo_updater = updater_from_config(&config); + let shared_algo = + SharedGasPriceAlgo::new_with_algorithm(algo_updater.algorithm()); + algo_updater.l2_block_height = block_height - 1; + algo_updater.last_profit = 10_000; + algo_updater.new_scaled_da_gas_price = 10_000_000; let notifier = Arc::new(tokio::sync::Notify::new()); let da_source = DaSourceService::new( DummyDaBlockCosts::new( Ok(DaBlockCosts { + bundle_id: 1, l2_blocks: (1..2).collect(), blob_cost_wei: 9000, - blob_size_bytes: 3000, + bundle_size_bytes: 3000, }), notifier.clone(), ), Some(Duration::from_millis(1)), ); - let mut watcher = StateWatcher::default(); + let mut watcher = StateWatcher::started(); + let latest_gas_price = LatestGasPrice::new(0, 0); let mut service = GasPriceServiceV1::new( l2_block_source, - metadata_storage, shared_algo, + latest_gas_price, algo_updater, da_source, + inner, ); let read_algo = service.next_block_algorithm(); let initial_price = read_algo.next_gas_price(); @@ -532,6 +638,10 @@ mod tests { .run(&mut da_source_watcher) .await; + service.run(&mut watcher).await; + tokio::time::sleep(Duration::from_millis(100)).await; + l2_block_sender.send(l2_block_2).await.unwrap(); + // when service.run(&mut watcher).await; tokio::time::sleep(Duration::from_millis(100)).await; @@ -541,4 +651,113 @@ mod tests { let actual_price = read_algo.next_gas_price(); assert_ne!(initial_price, actual_price); } + + fn arbitrary_v1_algorithm_config() -> V1AlgorithmConfig { + V1AlgorithmConfig { + new_exec_gas_price: 100, + min_exec_gas_price: 50, + exec_gas_price_change_percent: 0, + l2_block_fullness_threshold_percent: 20, + gas_price_factor: NonZeroU64::new(10).unwrap(), + min_da_gas_price: 0, + max_da_gas_price_change_percent: 100, + da_p_component: 4, + da_d_component: 2, + normal_range_size: 10, + capped_range_size: 100, + decrease_range_size: 4, + block_activity_threshold: 20, + } + } + + #[tokio::test] + async fn run__responses_from_da_service_update_bundle_id_in_storage() { + // given + let bundle_id = 1234; + let block_height = 2; + let l2_block_2 = BlockInfo::Block { + height: block_height, + gas_used: 60, + block_gas_capacity: 100, + block_bytes: 100, + block_fees: 100, + gas_price: 100, + }; + + let (l2_block_sender, l2_block_receiver) = mpsc::channel(1); + let l2_block_source = FakeL2BlockSource { + l2_block: l2_block_receiver, + }; + + let metadata_storage = FakeMetadata::empty(); + // Configured so exec gas price doesn't change, only da gas price + let config = arbitrary_v1_algorithm_config(); + let mut inner = database(); + let mut tx = inner.write_transaction(); + tx.storage_as_mut::() + .insert(&BlockHeight::from(1), &100) + .unwrap(); + tx.commit().unwrap(); + let mut algo_updater = updater_from_config(&config); + let shared_algo = + SharedGasPriceAlgo::new_with_algorithm(algo_updater.algorithm()); + algo_updater.l2_block_height = block_height - 1; + algo_updater.last_profit = 10_000; + algo_updater.new_scaled_da_gas_price = 10_000_000; + + let notifier = Arc::new(tokio::sync::Notify::new()); + let da_source = DaSourceService::new( + DummyDaBlockCosts::new( + Ok(DaBlockCosts { + bundle_id, + l2_blocks: (1..2).collect(), + blob_cost_wei: 9000, + bundle_size_bytes: 3000, + }), + notifier.clone(), + ), + Some(Duration::from_millis(1)), + ); + let mut watcher = StateWatcher::started(); + let latest_gas_price = LatestGasPrice::new(0, 0); + + let mut service = GasPriceServiceV1::new( + l2_block_source, + shared_algo, + latest_gas_price, + algo_updater, + da_source, + inner, + ); + let read_algo = service.next_block_algorithm(); + let initial_price = read_algo.next_gas_price(); + + // the RunnableTask depends on the handle passed to it for the da block cost source to already be running, + // which is the responsibility of the UninitializedTask in the `into_task` method of the RunnableService + // here we mimic that behaviour by running the da block cost service. + let mut da_source_watcher = StateWatcher::started(); + service + .da_source_adapter_handle + .run(&mut da_source_watcher) + .await; + + service.run(&mut watcher).await; + tokio::time::sleep(Duration::from_millis(100)).await; + l2_block_sender.send(l2_block_2).await.unwrap(); + + // when + service.run(&mut watcher).await; + tokio::time::sleep(Duration::from_millis(100)).await; + + // then + let latest_bundle_id = service + .storage_tx_provider + .storage::() + .get(&BlockHeight::from(block_height)) + .unwrap() + .unwrap(); + assert_eq!(*latest_bundle_id, bundle_id); + + service.shutdown().await.unwrap(); + } } diff --git a/crates/services/gas_price_service/src/v1/tests.rs b/crates/services/gas_price_service/src/v1/tests.rs index e5188a9c869..edbbbe9d6b8 100644 --- a/crates/services/gas_price_service/src/v1/tests.rs +++ b/crates/services/gas_price_service/src/v1/tests.rs @@ -2,6 +2,10 @@ use crate::{ common::{ fuel_core_storage_adapter::{ + storage::{ + GasPriceColumn, + GasPriceMetadata, + }, GasPriceSettings, GasPriceSettingsProvider, }, @@ -15,10 +19,15 @@ use crate::{ }, ports::{ GasPriceData, + GasPriceServiceAtomicStorage, + GetDaBundleId, + GetMetadataStorage, L2Data, - MetadataStorage, + SetDaBundleId, + SetMetadataStorage, }, v1::{ + algorithm::SharedV1Algorithm, da_source_service::{ service::{ DaBlockCostsSource, @@ -27,17 +36,25 @@ use crate::{ DaBlockCosts, }, metadata::{ + updater_from_config, V1AlgorithmConfig, V1Metadata, }, service::{ initialize_algorithm, GasPriceServiceV1, + LatestGasPrice, + }, + uninitialized_task::{ + fuel_storage_unrecorded_blocks::AsUnrecordedBlocks, + UninitializedTask, }, - uninitialized_task::UninitializedTask, }, }; -use anyhow::anyhow; +use anyhow::{ + anyhow, + Result, +}; use fuel_core_services::{ stream::{ BoxStream, @@ -47,14 +64,23 @@ use fuel_core_services::{ StateWatcher, }; use fuel_core_storage::{ - transactional::AtomicView, + iter::IteratorOverTable, + structured_storage::test::InMemoryStorage, + transactional::{ + AtomicView, + IntoTransaction, + StorageTransaction, + WriteTransaction, + }, Result as StorageResult, + StorageAsMut, }; use fuel_core_types::{ blockchain::{ block::Block, header::ConsensusParametersVersion, }, + fuel_asm::op::exp, fuel_tx::Transaction, fuel_types::BlockHeight, services::block_importer::{ @@ -62,11 +88,20 @@ use fuel_core_types::{ SharedImportResult, }, }; -use fuel_gas_price_algorithm::v1::AlgorithmUpdaterV1; +use fuel_gas_price_algorithm::v1::{ + AlgorithmUpdaterV1, + Bytes, + Error, + Height, + UnrecordedBlocks, +}; use std::{ num::NonZeroU64, ops::Deref, - sync::Arc, + sync::{ + Arc, + Mutex, + }, }; use tokio::sync::mpsc::Receiver; @@ -94,27 +129,23 @@ impl FakeMetadata { } } -impl MetadataStorage for FakeMetadata { - fn get_metadata(&self, _: &BlockHeight) -> GasPriceResult> { - let metadata = self.inner.lock().unwrap().clone(); - Ok(metadata) - } - +impl SetMetadataStorage for FakeMetadata { fn set_metadata(&mut self, metadata: &UpdaterMetadata) -> GasPriceResult<()> { *self.inner.lock().unwrap() = Some(metadata.clone()); Ok(()) } } -struct ErroringMetadata; - -impl MetadataStorage for ErroringMetadata { +impl GetMetadataStorage for FakeMetadata { fn get_metadata(&self, _: &BlockHeight) -> GasPriceResult> { - Err(GasPriceError::CouldNotFetchMetadata { - source_error: anyhow!("boo!"), - }) + let metadata = self.inner.lock().unwrap().clone(); + Ok(metadata) } +} + +struct ErroringPersistedData; +impl SetMetadataStorage for ErroringPersistedData { fn set_metadata(&mut self, _: &UpdaterMetadata) -> GasPriceResult<()> { Err(GasPriceError::CouldNotSetMetadata { block_height: Default::default(), @@ -123,8 +154,88 @@ impl MetadataStorage for ErroringMetadata { } } +impl GetMetadataStorage for ErroringPersistedData { + fn get_metadata(&self, _: &BlockHeight) -> GasPriceResult> { + Err(GasPriceError::CouldNotFetchMetadata { + source_error: anyhow!("boo!"), + }) + } +} + +impl GetDaBundleId for ErroringPersistedData { + fn get_bundle_id(&self, _block_height: &BlockHeight) -> GasPriceResult> { + Err(GasPriceError::CouldNotFetchDARecord(anyhow!("boo!"))) + } +} + +struct UnimplementedStorageTx; + +impl GasPriceServiceAtomicStorage for ErroringPersistedData { + type Transaction<'a> = UnimplementedStorageTx; + + fn begin_transaction(&mut self) -> GasPriceResult> { + todo!() + } + + fn commit_transaction(_transaction: Self::Transaction<'_>) -> GasPriceResult<()> { + todo!() + } +} + +impl SetMetadataStorage for UnimplementedStorageTx { + fn set_metadata(&mut self, _metadata: &UpdaterMetadata) -> GasPriceResult<()> { + unimplemented!() + } +} + +impl GetMetadataStorage for UnimplementedStorageTx { + fn get_metadata( + &self, + _block_height: &BlockHeight, + ) -> GasPriceResult> { + unimplemented!() + } +} + +impl UnrecordedBlocks for UnimplementedStorageTx { + fn insert(&mut self, _height: Height, _bytes: Bytes) -> Result<(), String> { + unimplemented!() + } + + fn remove(&mut self, _height: &Height) -> Result, String> { + unimplemented!() + } +} + +impl SetDaBundleId for UnimplementedStorageTx { + fn set_bundle_id( + &mut self, + _block_height: &BlockHeight, + _bundle_id: u32, + ) -> GasPriceResult<()> { + unimplemented!() + } +} + +impl GetDaBundleId for UnimplementedStorageTx { + fn get_bundle_id(&self, _block_height: &BlockHeight) -> GasPriceResult> { + unimplemented!() + } +} + +impl AsUnrecordedBlocks for UnimplementedStorageTx { + type Wrapper<'a> = UnimplementedStorageTx + where + Self: 'a; + + fn as_unrecorded_blocks(&mut self) -> Self::Wrapper<'_> { + UnimplementedStorageTx + } +} + struct FakeDABlockCost { da_block_costs: Receiver, + bundle_id: Arc>>, } impl FakeDABlockCost { @@ -132,20 +243,39 @@ impl FakeDABlockCost { let (_sender, receiver) = tokio::sync::mpsc::channel(1); Self { da_block_costs: receiver, + bundle_id: Arc::new(Mutex::new(None)), } } fn new(da_block_costs: Receiver) -> Self { - Self { da_block_costs } + Self { + da_block_costs, + bundle_id: Arc::new(Mutex::new(None)), + } + } + + fn never_returns_with_handle_to_bundle_id() -> (Self, Arc>>) { + let (_sender, receiver) = tokio::sync::mpsc::channel(1); + let bundle_id = Arc::new(Mutex::new(None)); + let service = Self { + da_block_costs: receiver, + bundle_id: bundle_id.clone(), + }; + (service, bundle_id) } } #[async_trait::async_trait] impl DaBlockCostsSource for FakeDABlockCost { - async fn request_da_block_cost(&mut self) -> anyhow::Result { + async fn request_da_block_cost(&mut self) -> Result { let costs = self.da_block_costs.recv().await.unwrap(); Ok(costs) } + + async fn set_last_value(&mut self, bundle_id: u32) -> Result<()> { + self.bundle_id.lock().unwrap().replace(bundle_id); + Ok(()) + } } fn zero_threshold_arbitrary_config() -> V1AlgorithmConfig { @@ -163,7 +293,6 @@ fn zero_threshold_arbitrary_config() -> V1AlgorithmConfig { capped_range_size: 0, decrease_range_size: 0, block_activity_threshold: 0, - unrecorded_blocks: vec![], } } @@ -178,7 +307,7 @@ fn arbitrary_metadata() -> V1Metadata { last_profit: 0, second_to_last_profit: 0, latest_da_cost_per_byte: 0, - unrecorded_blocks: vec![], + unrecorded_block_bytes: 0, } } @@ -197,10 +326,27 @@ fn different_arb_config() -> V1AlgorithmConfig { capped_range_size: 0, decrease_range_size: 0, block_activity_threshold: 0, - unrecorded_blocks: vec![], } } +fn database() -> StorageTransaction> { + InMemoryStorage::default().into_transaction() +} + +fn database_with_metadata( + metadata: &V1Metadata, +) -> StorageTransaction> { + let mut db = database(); + let mut tx = db.write_transaction(); + let height = metadata.l2_block_height.into(); + let metadata = UpdaterMetadata::V1(metadata.clone()); + tx.storage_as_mut::() + .insert(&height, &metadata) + .unwrap(); + tx.commit().unwrap(); + db +} + #[tokio::test] async fn next_gas_price__affected_by_new_l2_block() { // given @@ -210,6 +356,7 @@ async fn next_gas_price__affected_by_new_l2_block() { block_gas_capacity: 100, block_bytes: 100, block_fees: 100, + gas_price: 100, }; let (l2_block_sender, l2_block_receiver) = tokio::sync::mpsc::channel(1); let l2_block_source = FakeL2BlockSource { @@ -219,16 +366,19 @@ async fn next_gas_price__affected_by_new_l2_block() { let config = zero_threshold_arbitrary_config(); let height = 0; + let inner = database(); let (algo_updater, shared_algo) = initialize_algorithm(&config, height, &metadata_storage).unwrap(); let da_source = FakeDABlockCost::never_returns(); let da_source_service = DaSourceService::new(da_source, None); + let latest_gas_price = LatestGasPrice::new(0, 0); let mut service = GasPriceServiceV1::new( l2_block_source, - metadata_storage, shared_algo, + latest_gas_price, algo_updater, da_source_service, + inner, ); let read_algo = service.next_block_algorithm(); @@ -248,45 +398,99 @@ async fn next_gas_price__affected_by_new_l2_block() { #[tokio::test] async fn run__new_l2_block_saves_old_metadata() { // given + let height = 1; let l2_block = BlockInfo::Block { - height: 1, + height, gas_used: 60, block_gas_capacity: 100, block_bytes: 100, block_fees: 100, + gas_price: 100, }; let (l2_block_sender, l2_block_receiver) = tokio::sync::mpsc::channel(1); let l2_block_source = FakeL2BlockSource { l2_block: l2_block_receiver, }; - let metadata_inner = Arc::new(std::sync::Mutex::new(None)); - let metadata_storage = FakeMetadata { - inner: metadata_inner.clone(), - }; let config = zero_threshold_arbitrary_config(); - let height = 0; - let (algo_updater, shared_algo) = - initialize_algorithm(&config, height, &metadata_storage).unwrap(); + let inner = database(); + let algo_updater = updater_from_config(&config); + let shared_algo = SharedV1Algorithm::new_with_algorithm(algo_updater.algorithm()); let da_source = FakeDABlockCost::never_returns(); let da_source_service = DaSourceService::new(da_source, None); + let latest_gas_price = LatestGasPrice::new(0, 0); let mut service = GasPriceServiceV1::new( l2_block_source, - metadata_storage, shared_algo, + latest_gas_price, algo_updater, da_source_service, + inner, ); - let mut watcher = StateWatcher::default(); + let mut watcher = StateWatcher::started(); // when - service.run(&mut watcher).await; l2_block_sender.send(l2_block).await.unwrap(); + service.run(&mut watcher).await; + + // then + let metadata_is_some = service + .storage_tx_provider() + .get_metadata(&height.into()) + .unwrap() + .is_some(); + assert!(metadata_is_some); + + // cleanup service.shutdown().await.unwrap(); +} + +#[tokio::test] +async fn run__new_l2_block_updates_latest_gas_price_arc() { + // given + let height = 1; + let gas_price = 40; + let l2_block = BlockInfo::Block { + height, + gas_used: 60, + block_gas_capacity: 100, + block_bytes: 100, + block_fees: 100, + gas_price, + }; + let (l2_block_sender, l2_block_receiver) = tokio::sync::mpsc::channel(1); + let l2_block_source = FakeL2BlockSource { + l2_block: l2_block_receiver, + }; + + let config = zero_threshold_arbitrary_config(); + let inner = database(); + let algo_updater = updater_from_config(&config); + let shared_algo = SharedV1Algorithm::new_with_algorithm(algo_updater.algorithm()); + let da_source = FakeDABlockCost::never_returns(); + let da_source_service = DaSourceService::new(da_source, None); + let latest_gas_price = LatestGasPrice::new(0, 0); + let mut service = GasPriceServiceV1::new( + l2_block_source, + shared_algo, + latest_gas_price.clone(), + algo_updater, + da_source_service, + inner, + ); + let mut watcher = StateWatcher::started(); + + // when + l2_block_sender.send(l2_block).await.unwrap(); + service.run(&mut watcher).await; // then - let metadata_is_some = metadata_inner.lock().unwrap().is_some(); - assert!(metadata_is_some) + let expected = (height, gas_price); + let actual = latest_gas_price.get(); + assert_eq!(expected, actual); + + // cleanup + service.shutdown().await.unwrap(); } #[derive(Clone)] @@ -302,12 +506,26 @@ impl GasPriceSettingsProvider for FakeSettings { } #[derive(Clone)] -struct FakeGasPriceDb; +struct FakeGasPriceDb { + height: Option, +} + +impl FakeGasPriceDb { + fn new(height: u32) -> Self { + Self { + height: Some(height.into()), + } + } + + fn empty() -> Self { + Self { height: None } + } +} // GasPriceData + Modifiable + KeyValueInspect impl GasPriceData for FakeGasPriceDb { fn latest_height(&self) -> Option { - unimplemented!() + self.height } } @@ -343,7 +561,7 @@ impl L2Data for FakeL2Data { &self, _height: &BlockHeight, ) -> StorageResult>> { - unimplemented!() + Ok(None) } } impl AtomicView for FakeOnChainDb { @@ -373,22 +591,20 @@ async fn uninitialized_task__new__if_exists_already_reload_old_values_with_overr let descaleed_exec_price = original_metadata.new_scaled_exec_price / original_metadata.gas_price_factor; assert_ne!(different_config.new_exec_gas_price, descaleed_exec_price); - let different_l2_block = 1231; - assert_ne!(different_l2_block, original_metadata.l2_block_height); + let different_l2_block = 0; let settings = FakeSettings; let block_stream = empty_block_stream(); - let gas_price_db = FakeGasPriceDb; let on_chain_db = FakeOnChainDb::new(different_l2_block); let da_cost_source = FakeDABlockCost::never_returns(); - + let inner = database_with_metadata(&original_metadata); // when let service = UninitializedTask::new( - different_config, + different_config.clone(), + None, 0.into(), settings, block_stream, - gas_price_db, - metadata_storage, + inner, da_cost_source, on_chain_db, ) @@ -396,11 +612,15 @@ async fn uninitialized_task__new__if_exists_already_reload_old_values_with_overr // then let UninitializedTask { algo_updater, .. } = service; - algo_updater_matches_values_from_old_metadata(algo_updater, original_metadata); + algo_updater_matches_values_from_old_metadata( + algo_updater.clone(), + original_metadata.clone(), + ); + algo_updater_override_values_match(algo_updater, different_config); } fn algo_updater_matches_values_from_old_metadata( - algo_updater: AlgorithmUpdaterV1, + mut algo_updater: AlgorithmUpdaterV1, original_metadata: V1Metadata, ) { let V1Metadata { @@ -413,7 +633,7 @@ fn algo_updater_matches_values_from_old_metadata( last_profit: original_last_profit, second_to_last_profit: original_second_to_last_profit, latest_da_cost_per_byte: original_latest_da_cost_per_byte, - unrecorded_blocks: original_unrecorded_blocks, + unrecorded_block_bytes: original_unrecorded_block_bytes, } = original_metadata; assert_eq!( algo_updater.new_scaled_exec_price, @@ -443,34 +663,53 @@ fn algo_updater_matches_values_from_old_metadata( original_latest_da_cost_per_byte ); assert_eq!( - algo_updater - .unrecorded_blocks - .into_iter() - .collect::>(), - original_unrecorded_blocks.into_iter().collect::>() + algo_updater.unrecorded_blocks_bytes, + original_unrecorded_block_bytes ); } +fn algo_updater_override_values_match( + algo_updater: AlgorithmUpdaterV1, + config: V1AlgorithmConfig, +) { + assert_eq!(algo_updater.min_exec_gas_price, config.min_exec_gas_price); + assert_eq!( + algo_updater.exec_gas_price_change_percent, + config.exec_gas_price_change_percent + ); + assert_eq!( + algo_updater.l2_block_fullness_threshold_percent, + config.l2_block_fullness_threshold_percent.into() + ); + assert_eq!(algo_updater.gas_price_factor, config.gas_price_factor); + assert_eq!(algo_updater.min_da_gas_price, config.min_da_gas_price); + assert_eq!( + algo_updater.max_da_gas_price_change_percent, + config.max_da_gas_price_change_percent + ); + assert_eq!(algo_updater.da_p_component, config.da_p_component); + assert_eq!(algo_updater.da_d_component, config.da_d_component); +} + #[tokio::test] async fn uninitialized_task__new__should_fail_if_cannot_fetch_metadata() { // given let config = zero_threshold_arbitrary_config(); let different_l2_block = 1231; - let metadata_storage = ErroringMetadata; + let erroring_persisted_data = ErroringPersistedData; let settings = FakeSettings; let block_stream = empty_block_stream(); - let gas_price_db = FakeGasPriceDb; let on_chain_db = FakeOnChainDb::new(different_l2_block); let da_cost_source = FakeDABlockCost::never_returns(); // when let res = UninitializedTask::new( config, + None, 0.into(), settings, block_stream, - gas_price_db, - metadata_storage, + erroring_persisted_data, da_cost_source, on_chain_db, ); @@ -479,3 +718,45 @@ async fn uninitialized_task__new__should_fail_if_cannot_fetch_metadata() { let is_err = res.is_err(); assert!(is_err); } + +#[tokio::test] +async fn uninitialized_task__init__starts_da_service_with_bundle_id_in_storage() { + // given + let block_height = 1; + let bundle_id: u32 = 123; + let original_metadata = arbitrary_metadata(); + + let different_config = different_arb_config(); + let descaleed_exec_price = + original_metadata.new_scaled_exec_price / original_metadata.gas_price_factor; + assert_ne!(different_config.new_exec_gas_price, descaleed_exec_price); + let different_l2_block = 0; + let settings = FakeSettings; + let block_stream = empty_block_stream(); + let on_chain_db = FakeOnChainDb::new(different_l2_block); + let (da_cost_source, bundle_id_handle) = + FakeDABlockCost::never_returns_with_handle_to_bundle_id(); + let mut inner = database_with_metadata(&original_metadata); + let mut tx = inner.begin_transaction().unwrap(); + tx.set_bundle_id(&block_height.into(), bundle_id).unwrap(); + StorageTransaction::commit_transaction(tx).unwrap(); + let service = UninitializedTask::new( + different_config.clone(), + Some(block_height.into()), + 0.into(), + settings, + block_stream, + inner, + da_cost_source, + on_chain_db, + ) + .unwrap(); + + // when + service.init().await.unwrap(); + + // then + let actual = bundle_id_handle.lock().unwrap(); + let expected = Some(bundle_id); + assert_eq!(*actual, expected); +} diff --git a/crates/services/gas_price_service/src/v1/uninitialized_task.rs b/crates/services/gas_price_service/src/v1/uninitialized_task.rs index 2f5699c41f7..571f3e9f84c 100644 --- a/crates/services/gas_price_service/src/v1/uninitialized_task.rs +++ b/crates/services/gas_price_service/src/v1/uninitialized_task.rs @@ -18,15 +18,22 @@ use crate::{ }, ports::{ GasPriceData, + GasPriceServiceAtomicStorage, GasPriceServiceConfig, + GetDaBundleId, + GetMetadataStorage, L2Data, - MetadataStorage, + SetDaBundleId, + SetMetadataStorage, }, v1::{ algorithm::SharedV1Algorithm, - da_source_service::service::{ - DaBlockCostsSource, - DaSourceService, + da_source_service::{ + block_committer_costs::BlockCommitterDaBlockCosts, + service::{ + DaBlockCostsSource, + DaSourceService, + }, }, metadata::{ v1_algorithm_from_metadata, @@ -36,6 +43,11 @@ use crate::{ service::{ initialize_algorithm, GasPriceServiceV1, + LatestGasPrice, + }, + uninitialized_task::fuel_storage_unrecorded_blocks::{ + AsUnrecordedBlocks, + FuelStorageUnrecordedBlocks, }, }, }; @@ -47,53 +59,61 @@ use fuel_core_services::{ StateWatcher, }; use fuel_core_storage::{ + kv_store::{ + KeyValueInspect, + KeyValueMutate, + }, not_found, - transactional::AtomicView, + transactional::{ + AtomicView, + Modifiable, + StorageTransaction, + }, }; use fuel_core_types::{ fuel_tx::field::MintAmount, fuel_types::BlockHeight, services::block_importer::SharedImportResult, }; -use fuel_gas_price_algorithm::v1::AlgorithmUpdaterV1; +use fuel_gas_price_algorithm::v1::{ + AlgorithmUpdaterV1, + UnrecordedBlocks, +}; +use std::sync::Arc; -pub struct UninitializedTask< - L2DataStoreView, - GasPriceStore, - Metadata, - DA, - SettingsProvider, -> { +pub mod fuel_storage_unrecorded_blocks; + +pub struct UninitializedTask { pub config: V1AlgorithmConfig, + pub gas_metadata_height: Option, pub genesis_block_height: BlockHeight, pub settings: SettingsProvider, pub gas_price_db: GasPriceStore, pub on_chain_db: L2DataStoreView, pub block_stream: BoxStream, pub(crate) shared_algo: SharedV1Algorithm, + pub(crate) latest_gas_price: LatestGasPrice, pub(crate) algo_updater: AlgorithmUpdaterV1, - pub(crate) metadata_storage: Metadata, pub(crate) da_source: DA, } -impl - UninitializedTask +impl + UninitializedTask where L2DataStore: L2Data, L2DataStoreView: AtomicView, - GasPriceStore: GasPriceData, - Metadata: MetadataStorage, + AtomicStorage: GasPriceServiceAtomicStorage, DA: DaBlockCostsSource, SettingsProvider: GasPriceSettingsProvider, { #[allow(clippy::too_many_arguments)] pub fn new( config: V1AlgorithmConfig, + gas_metadata_height: Option, genesis_block_height: BlockHeight, settings: SettingsProvider, block_stream: BoxStream, - gas_price_db: GasPriceStore, - metadata_storage: Metadata, + gas_price_db: AtomicStorage, da_source: DA, on_chain_db: L2DataStoreView, ) -> anyhow::Result { @@ -102,29 +122,40 @@ where .latest_height() .unwrap_or(genesis_block_height) .into(); + let latest_gas_price = on_chain_db + .latest_view()? + .get_block(&latest_block_height.into())? + .and_then(|block| { + let (_, gas_price) = mint_values(&block).ok()?; + Some(gas_price) + }) + .unwrap_or(0); let (algo_updater, shared_algo) = - initialize_algorithm(&config, latest_block_height, &metadata_storage)?; + initialize_algorithm(&config, latest_block_height, &gas_price_db)?; + + let latest_gas_price = LatestGasPrice::new(latest_block_height, latest_gas_price); let task = Self { config, + gas_metadata_height, genesis_block_height, settings, gas_price_db, on_chain_db, block_stream, algo_updater, + latest_gas_price, shared_algo, - metadata_storage, da_source, }; Ok(task) } - pub fn init( + pub async fn init( mut self, ) -> anyhow::Result< - GasPriceServiceV1, Metadata, DA>, + GasPriceServiceV1, DA, AtomicStorage>, > { let mut first_run = false; let latest_block_height: u32 = self @@ -134,7 +165,7 @@ where .unwrap_or(self.genesis_block_height) .into(); - let maybe_metadata_height = self.gas_price_db.latest_height(); + let maybe_metadata_height = self.gas_metadata_height; let metadata_height = if let Some(metadata_height) = maybe_metadata_height { metadata_height.into() } else { @@ -151,6 +182,11 @@ where // TODO: Add to config // https://github.com/FuelLabs/fuel-core/issues/2140 let poll_interval = None; + if let Some(bundle_id) = + self.gas_price_db.get_bundle_id(&metadata_height.into())? + { + self.da_source.set_last_value(bundle_id).await?; + } let da_service = DaSourceService::new(self.da_source, poll_interval); if BlockHeight::from(latest_block_height) == self.genesis_block_height @@ -158,10 +194,11 @@ where { let service = GasPriceServiceV1::new( l2_block_source, - self.metadata_storage, self.shared_algo, + self.latest_gas_price, self.algo_updater, da_service, + self.gas_price_db, ); Ok(service) } else { @@ -169,19 +206,20 @@ where sync_gas_price_db_with_on_chain_storage( &self.settings, &self.config, - &mut self.metadata_storage, &self.on_chain_db, metadata_height, latest_block_height, + &mut self.gas_price_db, )?; } let service = GasPriceServiceV1::new( l2_block_source, - self.metadata_storage, self.shared_algo, + self.latest_gas_price, self.algo_updater, da_service, + self.gas_price_db, ); Ok(service) } @@ -189,24 +227,22 @@ where } #[async_trait::async_trait] -impl - RunnableService - for UninitializedTask +impl RunnableService + for UninitializedTask where L2DataStore: L2Data, L2DataStoreView: AtomicView, - GasPriceStore: GasPriceData, - Metadata: MetadataStorage, - DA: DaBlockCostsSource, - SettingsProvider: GasPriceSettingsProvider, + AtomicStorage: GasPriceServiceAtomicStorage + GasPriceData, + DA: DaBlockCostsSource + 'static, + SettingsProvider: GasPriceSettingsProvider + 'static, { const NAME: &'static str = "GasPriceServiceV1"; - type SharedData = SharedV1Algorithm; - type Task = GasPriceServiceV1, Metadata, DA>; + type SharedData = (SharedV1Algorithm, LatestGasPrice); + type Task = GasPriceServiceV1, DA, AtomicStorage>; type TaskParams = (); fn shared_data(&self) -> Self::SharedData { - self.shared_algo.clone() + (self.shared_algo.clone(), self.latest_gas_price.clone()) } async fn into_task( @@ -214,30 +250,30 @@ where _state_watcher: &StateWatcher, _params: Self::TaskParams, ) -> anyhow::Result { - UninitializedTask::init(self) + UninitializedTask::init(self).await } } fn sync_gas_price_db_with_on_chain_storage< L2DataStore, L2DataStoreView, - Metadata, SettingsProvider, + AtomicStorage, >( settings: &SettingsProvider, config: &V1AlgorithmConfig, - metadata_storage: &mut Metadata, on_chain_db: &L2DataStoreView, metadata_height: u32, latest_block_height: u32, + persisted_data: &mut AtomicStorage, ) -> anyhow::Result<()> where L2DataStore: L2Data, L2DataStoreView: AtomicView, - Metadata: MetadataStorage, SettingsProvider: GasPriceSettingsProvider, + AtomicStorage: GasPriceServiceAtomicStorage, { - let metadata = metadata_storage + let metadata = persisted_data .get_metadata(&metadata_height.into())? .ok_or(anyhow::anyhow!( "Expected metadata to exist for height: {metadata_height}" @@ -257,28 +293,29 @@ where metadata_height, latest_block_height, &mut algo_updater, - metadata_storage, + persisted_data, )?; Ok(()) } -fn sync_v1_metadata( +fn sync_v1_metadata( settings: &SettingsProvider, on_chain_db: &L2DataStoreView, metadata_height: u32, latest_block_height: u32, updater: &mut AlgorithmUpdaterV1, - metadata_storage: &mut Metadata, + da_storage: &mut AtomicStorage, ) -> anyhow::Result<()> where L2DataStore: L2Data, L2DataStoreView: AtomicView, - Metadata: MetadataStorage, SettingsProvider: GasPriceSettingsProvider, + AtomicStorage: GasPriceServiceAtomicStorage, { let first = metadata_height.saturating_add(1); let view = on_chain_db.latest_view()?; + let mut tx = da_storage.begin_transaction()?; for height in first..=latest_block_height { let block = view .get_block(&height.into())? @@ -307,51 +344,43 @@ where block_gas_capacity, block_bytes, fee_wei.into(), + &mut tx.as_unrecorded_blocks(), )?; let metadata: UpdaterMetadata = updater.clone().into(); - metadata_storage.set_metadata(&metadata)?; + tx.set_metadata(&metadata)?; } + AtomicStorage::commit_transaction(tx)?; Ok(()) } #[allow(clippy::type_complexity)] -#[allow(clippy::too_many_arguments)] -pub fn new_gas_price_service_v1< - L2DataStore, - GasPriceStore, - Metadata, - DA, - SettingsProvider, ->( +pub fn new_gas_price_service_v1( v1_config: V1AlgorithmConfig, genesis_block_height: BlockHeight, settings: SettingsProvider, block_stream: BoxStream, - gas_price_db: GasPriceStore, - metadata: Metadata, + gas_price_db: AtomicStorage, da_source: DA, on_chain_db: L2DataStore, ) -> anyhow::Result< - ServiceRunner< - UninitializedTask, - >, + ServiceRunner>, > where L2DataStore: AtomicView, L2DataStore::LatestView: L2Data, - GasPriceStore: GasPriceData, + AtomicStorage: GasPriceServiceAtomicStorage + GasPriceData, SettingsProvider: GasPriceSettingsProvider, - Metadata: MetadataStorage, DA: DaBlockCostsSource, { + let metadata_height = gas_price_db.latest_height(); let gas_price_init = UninitializedTask::new( v1_config, + metadata_height, genesis_block_height, settings, block_stream, gas_price_db, - metadata, da_source, on_chain_db, )?; diff --git a/crates/services/gas_price_service/src/v1/uninitialized_task/fuel_storage_unrecorded_blocks.rs b/crates/services/gas_price_service/src/v1/uninitialized_task/fuel_storage_unrecorded_blocks.rs new file mode 100644 index 00000000000..61d6bae5528 --- /dev/null +++ b/crates/services/gas_price_service/src/v1/uninitialized_task/fuel_storage_unrecorded_blocks.rs @@ -0,0 +1,123 @@ +use crate::common::fuel_core_storage_adapter::storage::{ + GasPriceColumn, + UnrecordedBlocksTable, +}; +use fuel_core_storage::{ + kv_store::{ + KeyValueInspect, + KeyValueMutate, + }, + transactional::{ + Modifiable, + WriteTransaction, + }, + Error as StorageError, + StorageAsMut, + StorageAsRef, + StorageMutate, +}; +use fuel_core_types::fuel_merkle::storage::StorageMutateInfallible; +use fuel_gas_price_algorithm::{ + v1, + v1::UnrecordedBlocks, +}; + +pub trait AsUnrecordedBlocks { + type Wrapper<'a>: UnrecordedBlocks + where + Self: 'a; + + fn as_unrecorded_blocks(&mut self) -> Self::Wrapper<'_>; +} + +impl AsUnrecordedBlocks for S +where + S: StorageMutate, +{ + type Wrapper<'a> = FuelStorageUnrecordedBlocks<&'a mut Self> + where + Self: 'a; + + fn as_unrecorded_blocks(&mut self) -> Self::Wrapper<'_> { + FuelStorageUnrecordedBlocks::new(self) + } +} + +#[derive(Debug, Clone)] +pub struct FuelStorageUnrecordedBlocks { + inner: Storage, +} + +impl FuelStorageUnrecordedBlocks { + pub fn new(inner: Storage) -> Self { + Self { inner } + } +} + +impl UnrecordedBlocks for FuelStorageUnrecordedBlocks +where + S: StorageMutate, +{ + fn insert(&mut self, height: v1::Height, bytes: v1::Bytes) -> Result<(), String> { + self.inner + .storage_as_mut::() + .insert(&height.into(), &bytes) + .map_err(|err| format!("Error: {:?}", err))?; + Ok(()) + } + + fn remove(&mut self, height: &v1::Height) -> Result, String> { + let bytes = self + .inner + .storage_as_mut::() + .take(&(*height).into()) + .map_err(|err| format!("Error: {:?}", err))?; + Ok(bytes) + } +} + +#[allow(non_snake_case)] +#[cfg(test)] +mod tests { + use super::*; + use fuel_core_storage::{ + structured_storage::test::InMemoryStorage, + transactional::{ + IntoTransaction, + StorageTransaction, + }, + }; + + fn database() -> StorageTransaction> { + InMemoryStorage::default().into_transaction() + } + + #[test] + fn insert__remove__round_trip() { + // given + let mut storage = FuelStorageUnrecordedBlocks::new(database()); + let height = 8; + let bytes = 100; + + // when + storage.insert(height, bytes).unwrap(); + let actual = storage.remove(&height).unwrap(); + + // then + let expected = Some(bytes); + assert_eq!(expected, actual); + } + + #[test] + fn remove__if_not_inserted_returns_none() { + // given + let mut storage = FuelStorageUnrecordedBlocks::new(database()); + let height = 8; + + // when + let maybe_value = storage.remove(&height).unwrap(); + + // then + assert!(maybe_value.is_none()); + } +} diff --git a/crates/services/p2p/src/codecs/postcard.rs b/crates/services/p2p/src/codecs/postcard.rs index e2b7a97f9b1..347f5a4cbfc 100644 --- a/crates/services/p2p/src/codecs/postcard.rs +++ b/crates/services/p2p/src/codecs/postcard.rs @@ -307,7 +307,7 @@ mod tests { } #[tokio::test] - async fn codec__serialzation_roundtrip_using_v1_on_error_response_returns_predefined_error_code( + async fn codec__serialization_roundtrip_using_v1_on_error_response_returns_predefined_error_code( ) { // Given let response = V2ResponseMessage::SealedHeaders(Err( diff --git a/crates/services/shared-sequencer/Cargo.toml b/crates/services/shared-sequencer/Cargo.toml new file mode 100644 index 00000000000..f10f592dcf5 --- /dev/null +++ b/crates/services/shared-sequencer/Cargo.toml @@ -0,0 +1,33 @@ +[package] +authors = { workspace = true } +categories = ["cryptography::cryptocurrencies"] +description = "The service responsible for communication with the shared sequencer." +edition = { workspace = true } +homepage = { workspace = true } +keywords = ["blockchain", "cryptocurrencies", "fuel-client", "fuel-core"] +license = { workspace = true } +name = "fuel-core-shared-sequencer" +repository = { workspace = true } +version = { workspace = true } + +[dependencies] +anyhow = { workspace = true } +async-trait = { workspace = true } +base64 = "0.22" +cosmos-sdk-proto = { version = "0.26", features = ["grpc"] } +cosmrs = "0.21" +fuel-core-services = { workspace = true } +fuel-core-types = { workspace = true, features = ["std", "serde"] } +fuel-sequencer-proto = { version = "0.1.0" } +futures = { workspace = true } +postcard = { workspace = true } +prost = "0.12" +reqwest = { version = "0.12", features = ["json"], default-features = false } +serde = { workspace = true, features = ["derive"] } +serde_json = "1.0" +tendermint-rpc = { version = "0.36", features = ["http-client"] } +tokio = { workspace = true } +tracing = { workspace = true } + +[features] +test-helpers = [] diff --git a/crates/services/shared-sequencer/src/config.rs b/crates/services/shared-sequencer/src/config.rs new file mode 100644 index 00000000000..fb3eb767d5a --- /dev/null +++ b/crates/services/shared-sequencer/src/config.rs @@ -0,0 +1,37 @@ +use std::time::Duration; + +/// Endpoints for the shared sequencer client. +#[derive(Debug, Clone)] +pub struct Endpoints { + /// The RPC address of the sequencer chain tendermint API + /// (e.g. "http://127.0.0.1:26657") + pub tendermint_rpc_api: String, + /// The REST address of the sequencer chain tendermint API + /// (e.g. "http://127.0.0.1:1317") + pub blockchain_rest_api: String, +} + +/// Configuration for the shared sequencer client +#[derive(Debug, Clone)] +pub struct Config { + /// Whether the sequencer is enabled. + pub enabled: bool, + /// The frequency at which to post blocks to the shared sequencer. + pub block_posting_frequency: Duration, + /// Endpoints for the shared sequencer client. + pub endpoints: Option, + /// Topic to post blocks to + pub topic: [u8; 32], +} + +impl Config { + /// Default configuration for locally running shared sequencer node + pub fn local_node() -> Self { + Self { + enabled: false, + block_posting_frequency: Duration::from_secs(12), + endpoints: None, + topic: [0u8; 32], + } + } +} diff --git a/crates/services/shared-sequencer/src/error.rs b/crates/services/shared-sequencer/src/error.rs new file mode 100644 index 00000000000..78ef1c0d10d --- /dev/null +++ b/crates/services/shared-sequencer/src/error.rs @@ -0,0 +1,14 @@ +use core::fmt; + +#[derive(Debug)] +pub struct PostBlobError { + pub message: String, +} + +impl fmt::Display for PostBlobError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "PostBlobError: {:?}", self.message) + } +} + +impl std::error::Error for PostBlobError {} diff --git a/crates/services/shared-sequencer/src/http_api.rs b/crates/services/shared-sequencer/src/http_api.rs new file mode 100644 index 00000000000..96233f01895 --- /dev/null +++ b/crates/services/shared-sequencer/src/http_api.rs @@ -0,0 +1,189 @@ +use anyhow::Context; +use base64::prelude::*; +use cosmrs::AccountId; + +mod api_types { + use serde::Deserialize; + + #[derive(Debug, Deserialize)] + pub struct AccountResponse { + pub account: AccountInfo, + } + + #[derive(Debug, Deserialize)] + pub struct AccountInfo { + pub account_number: String, + pub sequence: String, + } + + #[derive(Debug, Deserialize)] + pub struct AccountPrefix { + pub bech32_prefix: String, + } + + #[derive(Debug, Deserialize)] + pub struct NodeInfo { + pub default_node_info: DefaultNodeInfo, + } + + #[derive(Debug, Deserialize)] + pub struct DefaultNodeInfo { + pub network: String, + } + + #[derive(Debug, Deserialize)] + pub struct StakingParams { + pub params: StakingParamsInner, + } + + #[derive(Debug, Deserialize)] + pub struct StakingParamsInner { + pub bond_denom: String, + } + + #[derive(Debug, Deserialize)] + pub struct TopicResponse { + pub topic: TopicInfo, + } + + #[derive(Debug, Deserialize)] + pub struct TopicInfo { + pub owner: String, + pub order: String, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct SimulateResponse { + pub gas_info: GasInfo, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct GasInfo { + pub gas_used: String, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Config { + pub minimum_gas_price: String, + } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct AccountMetadata { + pub account_number: u64, + pub sequence: u64, +} + +#[derive(Clone, Debug, serde::Serialize)] +pub struct SimulateRequest { + pub tx_bytes: String, +} + +pub async fn estimate_transaction( + api_url: &str, + tx_bytes: Vec, +) -> anyhow::Result { + let tx_bytes = BASE64_STANDARD.encode(&tx_bytes); + let request = SimulateRequest { + tx_bytes: tx_bytes.to_string(), + }; + let r = reqwest::Client::new() + .post(format!("{api_url}/cosmos/tx/v1beta1/simulate")) + .json(&request) + .send() + .await?; + let text = r.text().await?; + let resp: api_types::SimulateResponse = + serde_json::from_str(&text).with_context(|| format!("response text {text}"))?; + Ok(resp.gas_info.gas_used.parse()?) +} + +pub async fn get_account_prefix(api_url: &str) -> anyhow::Result { + let r = reqwest::get(format!("{api_url}/cosmos/auth/v1beta1/bech32")).await?; + let text = r.text().await?; + let resp: api_types::AccountPrefix = + serde_json::from_str(&text).with_context(|| format!("response text {text}"))?; + Ok(resp.bech32_prefix) +} + +pub async fn chain_id(api_url: &str) -> anyhow::Result { + let r = reqwest::get(format!( + "{api_url}/cosmos/base/tendermint/v1beta1/node_info" + )) + .await?; + let text = r.text().await?; + let resp: api_types::NodeInfo = + serde_json::from_str(&text).with_context(|| format!("response text {text}"))?; + Ok(resp.default_node_info.network) +} + +pub async fn config(api_url: &str) -> anyhow::Result { + let r = reqwest::get(format!("{api_url}/cosmos/base/node/v1beta1/config")).await?; + let text = r.text().await?; + let resp: api_types::Config = + serde_json::from_str(&text).with_context(|| format!("response text {text}"))?; + Ok(resp) +} + +pub async fn coin_denom(api_url: &str) -> anyhow::Result { + let r = reqwest::get(format!("{api_url}/cosmos/staking/v1beta1/params")).await?; + let text = r.text().await?; + let resp: api_types::StakingParams = + serde_json::from_str(&text).with_context(|| format!("response text {text}"))?; + Ok(resp.params.bond_denom) +} + +pub async fn get_account( + api_url: &str, + id: AccountId, +) -> anyhow::Result { + let r = reqwest::get(format!("{api_url}/cosmos/auth/v1beta1/accounts/{id}")).await?; + let text = r.text().await?; + let resp: api_types::AccountResponse = + serde_json::from_str(&text).with_context(|| format!("response text {text}"))?; + let account_number = resp + .account + .account_number + .parse() + .map_err(|_| anyhow::anyhow!("Invalid account_number"))?; + let sequence = resp + .account + .sequence + .parse() + .map_err(|_| anyhow::anyhow!("Invalid sequence"))?; + Ok(AccountMetadata { + account_number, + sequence, + }) +} + +#[derive(Debug)] +pub struct TopicInfo { + pub owner: AccountId, + pub order: u64, +} + +pub async fn get_topic(api_url: &str, id: [u8; 32]) -> anyhow::Result> { + let id_b64 = BASE64_STANDARD.encode(id); + let r = reqwest::get(format!( + "{api_url}/fuelsequencer/sequencing/v1/topic/{id_b64}" + )) + .await?; + if r.status() == 404 { + return Ok(None); + } + let text = r.text().await?; + let resp: api_types::TopicResponse = + serde_json::from_str(&text).with_context(|| format!("response text {text}"))?; + let owner = resp + .topic + .owner + .parse() + .map_err(|_| anyhow::anyhow!("Invalid owner"))?; + let order = resp + .topic + .order + .parse() + .map_err(|_| anyhow::anyhow!("Invalid order"))?; + Ok(Some(TopicInfo { owner, order })) +} diff --git a/crates/services/shared-sequencer/src/lib.rs b/crates/services/shared-sequencer/src/lib.rs new file mode 100644 index 00000000000..8dd3b488602 --- /dev/null +++ b/crates/services/shared-sequencer/src/lib.rs @@ -0,0 +1,269 @@ +//! Shared sequencer client + +#![deny(clippy::arithmetic_side_effects)] +#![deny(clippy::cast_possible_truncation)] +#![deny(unused_crate_dependencies)] +#![deny(missing_docs)] + +use anyhow::anyhow; +use cosmrs::{ + tendermint::chain::Id, + tx::{ + self, + Fee, + MessageExt, + SignDoc, + SignerInfo, + }, + AccountId, + Coin, + Denom, +}; +use error::PostBlobError; +use fuel_sequencer_proto::protos::fuelsequencer::sequencing::v1::MsgPostBlob; +use http_api::{ + AccountMetadata, + TopicInfo, +}; +use ports::Signer; +use prost::Message; +use tendermint_rpc::Client as _; + +// Re-exports +pub use config::{ + Config, + Endpoints, +}; +pub use prost::bytes::Bytes; + +mod config; +mod error; +mod http_api; +pub mod ports; +pub mod service; + +/// Shared sequencer client +pub struct Client { + endpoints: Endpoints, + topic: [u8; 32], + ss_chain_id: Id, + gas_price: u128, + coin_denom: Denom, + account_prefix: String, +} + +impl Client { + /// Create a new shared sequencer client from config. + pub async fn new(endpoints: Endpoints, topic: [u8; 32]) -> anyhow::Result { + let coin_denom = http_api::coin_denom(&endpoints.blockchain_rest_api) + .await? + .parse() + .map_err(|e| anyhow::anyhow!("{e:?}"))?; + let account_prefix = + http_api::get_account_prefix(&endpoints.blockchain_rest_api).await?; + let ss_chain_id = http_api::chain_id(&endpoints.blockchain_rest_api) + .await? + .parse() + .map_err(|e| anyhow::anyhow!("{e:?}"))?; + let ss_config = http_api::config(&endpoints.blockchain_rest_api).await?; + + let mut minimum_gas_price = ss_config.minimum_gas_price; + + if let Some(index) = minimum_gas_price.find('.') { + minimum_gas_price.truncate(index); + } + let gas_price: u128 = minimum_gas_price.parse()?; + // Ceil the gas price to the next integer. + let gas_price = gas_price.saturating_add(1); + + Ok(Self { + topic, + endpoints, + account_prefix, + coin_denom, + ss_chain_id, + gas_price, + }) + } + + /// Returns the Cosmos account ID of the sender. + pub fn sender_account_id(&self, signer: &S) -> anyhow::Result { + let sender_public_key = signer.public_key(); + let sender_account_id = sender_public_key + .account_id(&self.account_prefix) + .map_err(|err| anyhow!("{err:?}"))?; + + Ok(sender_account_id) + } + + fn tendermint(&self) -> anyhow::Result { + Ok(tendermint_rpc::HttpClient::new( + &*self.endpoints.tendermint_rpc_api, + )?) + } + + /// Retrieve latest block height + pub async fn latest_block_height(&self) -> anyhow::Result { + Ok(self + .tendermint()? + .abci_info() + .await? + .last_block_height + .value() + .try_into()?) + } + + /// Retrieve account metadata by its ID + pub async fn get_account_meta( + &self, + signer: &S, + ) -> anyhow::Result { + let sender_account_id = self.sender_account_id(signer)?; + http_api::get_account(&self.endpoints.blockchain_rest_api, sender_account_id) + .await + } + + /// Retrieve the topic info, if it exists + pub async fn get_topic(&self) -> anyhow::Result> { + http_api::get_topic(&self.endpoints.blockchain_rest_api, self.topic).await + } + + /// Post a sealed block to the sequencer chain using some + /// reasonable defaults and the config. + /// This is a convenience wrapper for `send_raw`. + pub async fn send( + &self, + signer: &S, + account: AccountMetadata, + order: u64, + blob: Vec, + ) -> anyhow::Result<()> { + let latest_height = self.latest_block_height().await?; + + self.send_raw( + // We don't want our transactions to be stay in the mempool for a long time, + // so we set the timeout height to be 64 blocks ahead of the latest block height. + // The 64 is an arbitrary number. + latest_height.saturating_add(64), + signer, + account, + order, + self.topic, + Bytes::from(blob), + ) + .await + } + + /// Post a blob of raw data to the sequencer chain + #[allow(clippy::too_many_arguments)] + pub async fn send_raw( + &self, + timeout_height: u32, + signer: &S, + account: AccountMetadata, + order: u64, + topic: [u8; 32], + data: Bytes, + ) -> anyhow::Result<()> { + // We want to estimate the transaction to know what amount and fee to use. + // We use a dummy amount and fee to estimate the gas, and based on the result + // we calculate the actual amount and fee to use in real transaction. + let dummy_amount = Coin { + amount: 0, + denom: self.coin_denom.clone(), + }; + + let dummy_fee = Fee::from_amount_and_gas(dummy_amount, 0u64); + + let dummy_payload = self + .make_payload( + timeout_height, + dummy_fee, + signer, + account, + order, + topic, + data.clone(), + ) + .await?; + + let used_gas = http_api::estimate_transaction( + &self.endpoints.blockchain_rest_api, + dummy_payload, + ) + .await?; + + let used_gas = used_gas.saturating_mul(2); // Add some buffer + + let amount = Coin { + amount: self.gas_price.saturating_mul(used_gas as u128), + denom: self.coin_denom.clone(), + }; + + let fee = Fee::from_amount_and_gas(amount, used_gas); + let payload = self + .make_payload(timeout_height, fee, signer, account, order, topic, data) + .await?; + + let r = self.tendermint()?.broadcast_tx_sync(payload).await?; + if r.code.is_err() { + return Err(PostBlobError { message: r.log }.into()); + } + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + async fn make_payload( + &self, + timeout_height: u32, + fee: Fee, + signer: &S, + account: AccountMetadata, + order: u64, + topic: [u8; 32], + data: Bytes, + ) -> anyhow::Result> { + let sender_account_id = self.sender_account_id(signer)?; + + let msg = MsgPostBlob { + from: sender_account_id.to_string(), + order: order.to_string(), + topic: Bytes::from(topic.to_vec()), + data, + }; + let any_msg = cosmrs::Any { + type_url: "/fuelsequencer.sequencing.v1.MsgPostBlob".to_owned(), + value: msg.encode_to_vec(), + }; + let tx_body = tx::Body::new(vec![any_msg], "", timeout_height); + + let sender_public_key = signer.public_key(); + let signer_info = + SignerInfo::single_direct(Some(sender_public_key), account.sequence); + let auth_info = signer_info.auth_info(fee); + let sign_doc = SignDoc::new( + &tx_body, + &auth_info, + &self.ss_chain_id, + account.account_number, + ) + .map_err(|err| anyhow!("{err:?}"))?; + + let sign_doc_bytes = sign_doc + .clone() + .into_bytes() + .map_err(|err| anyhow!("{err:?}"))?; + let signature = signer.sign(&sign_doc_bytes).await?; + + // Convert the signature to non-normalized form + let mut signature_bytes = *signature; + signature_bytes[32] &= 0x7f; + + Ok(cosmos_sdk_proto::cosmos::tx::v1beta1::TxRaw { + body_bytes: sign_doc.body_bytes, + auth_info_bytes: sign_doc.auth_info_bytes, + signatures: vec![signature_bytes.to_vec()], + } + .to_bytes()?) + } +} diff --git a/crates/services/shared-sequencer/src/ports.rs b/crates/services/shared-sequencer/src/ports.rs new file mode 100644 index 00000000000..2fb3ea08e84 --- /dev/null +++ b/crates/services/shared-sequencer/src/ports.rs @@ -0,0 +1,25 @@ +//! Ports used by the shared sequencer to access the outside world + +use cosmrs::crypto::PublicKey; +use fuel_core_services::stream::BoxStream; +use fuel_core_types::{ + fuel_crypto::Signature, + services::block_importer::SharedImportResult, +}; + +/// A signer that can sign arbitrary data +#[async_trait::async_trait] +pub trait Signer: Send + Sync { + /// Sign data using a key + async fn sign(&self, data: &[u8]) -> anyhow::Result; + /// Get the public key of the signer. Panics if the key is not available. + fn public_key(&self) -> PublicKey; + /// Check if the signer is available + fn is_available(&self) -> bool; +} + +/// Provider of the blocks. +pub trait BlocksProvider { + /// Subscribe to new blocks. + fn subscribe(&self) -> BoxStream; +} diff --git a/crates/services/shared-sequencer/src/service.rs b/crates/services/shared-sequencer/src/service.rs new file mode 100644 index 00000000000..7b632f35e5b --- /dev/null +++ b/crates/services/shared-sequencer/src/service.rs @@ -0,0 +1,250 @@ +//! Defines the logic how to interact with the shared sequencer. + +use crate::{ + http_api::AccountMetadata, + ports::{ + BlocksProvider, + Signer, + }, + Client, + Config, +}; +use async_trait::async_trait; +use core::time::Duration; +use fuel_core_services::{ + stream::BoxStream, + EmptyShared, + RunnableService, + RunnableTask, + ServiceRunner, + StateWatcher, + TaskNextAction, +}; +use fuel_core_types::services::{ + block_importer::SharedImportResult, + shared_sequencer::{ + SSBlob, + SSBlobs, + }, +}; +use futures::StreamExt; +use std::sync::Arc; + +/// Non-initialized shared sequencer task. +pub struct NonInitializedTask { + config: Config, + signer: Arc, + blocks_events: BoxStream, +} + +/// Initialized shared sequencer task. +pub struct Task { + /// The client that communicates with shared sequencer. + shared_sequencer_client: Option, + config: Config, + signer: Arc, + account_metadata: Option, + prev_order: Option, + blobs: Arc>, + interval: tokio::time::Interval, +} + +impl NonInitializedTask { + /// Create a new shared sequencer task. + fn new( + config: Config, + blocks_events: BoxStream, + signer: Arc, + ) -> anyhow::Result { + if config.enabled && config.endpoints.is_none() { + return Err(anyhow::anyhow!( + "Shared sequencer is enabled but no endpoints are set" + )); + } + + Ok(Self { + config, + blocks_events, + signer, + }) + } +} + +#[async_trait] +impl RunnableService for NonInitializedTask +where + S: Signer + 'static, +{ + const NAME: &'static str = "SharedSequencer"; + + type SharedData = EmptyShared; + type Task = Task; + type TaskParams = (); + + fn shared_data(&self) -> Self::SharedData { + EmptyShared + } + + async fn into_task( + mut self, + _: &StateWatcher, + _: Self::TaskParams, + ) -> anyhow::Result { + let shared_sequencer_client = if let Some(endpoints) = &self.config.endpoints { + let ss = Client::new(endpoints.clone(), self.config.topic).await?; + + if self.signer.is_available() { + let cosmos_public_address = ss.sender_account_id(self.signer.as_ref())?; + + tracing::info!( + "Shared sequencer uses account ID: {}", + cosmos_public_address + ); + } + + Some(ss) + } else { + None + }; + + let blobs = Arc::new(tokio::sync::Mutex::new(SSBlobs::new())); + + if self.config.enabled { + let mut block_events = self.blocks_events; + + tokio::task::spawn({ + let blobs = blobs.clone(); + async move { + while let Some(block) = block_events.next().await { + let blob = SSBlob { + block_height: *block.sealed_block.entity.header().height(), + block_id: block.sealed_block.entity.id(), + }; + blobs.lock().await.push(blob); + } + } + }); + } + + Ok(Task { + interval: tokio::time::interval(self.config.block_posting_frequency), + shared_sequencer_client, + config: self.config, + signer: self.signer, + account_metadata: None, + prev_order: None, + blobs, + }) + } +} + +impl Task +where + S: Signer, +{ + /// Fetch latest account metadata if it's not set + async fn ensure_account_metadata(&mut self) -> anyhow::Result<()> { + if self.account_metadata.is_some() { + return Ok(()); + } + let ss = self + .shared_sequencer_client + .as_ref() + .expect("Shared sequencer client is not set"); + self.account_metadata = Some(ss.get_account_meta(self.signer.as_ref()).await?); + Ok(()) + } + + /// Fetch previous order in the topic if it's not set + async fn ensure_prev_order(&mut self) -> anyhow::Result<()> { + if self.prev_order.is_some() { + return Ok(()); + } + let ss = self + .shared_sequencer_client + .as_ref() + .expect("Shared sequencer client is not set"); + self.prev_order = ss.get_topic().await?.map(|f| f.order); + Ok(()) + } +} + +#[async_trait] +impl RunnableTask for Task +where + S: Signer + 'static, +{ + async fn run(&mut self, watcher: &mut StateWatcher) -> TaskNextAction { + if !self.config.enabled { + let _ = watcher.while_started().await; + return TaskNextAction::Stop; + } + + if let Err(err) = self.ensure_account_metadata().await { + // We don't want to spam the RPC endpoint with a lot of queries, + // so wait for one second before sending the next one. + tokio::time::sleep(Duration::from_secs(1)).await; + return TaskNextAction::ErrorContinue(err) + } + if let Err(err) = self.ensure_prev_order().await { + return TaskNextAction::ErrorContinue(err) + }; + + tokio::select! { + biased; + _ = watcher.while_started() => { + TaskNextAction::Stop + }, + _ = self.interval.tick() => { + let blobs = { + let mut lock = self.blobs.lock().await; + core::mem::take(&mut *lock) + }; + if blobs.is_empty() { + tokio::time::sleep(Duration::from_secs(1)).await; + return TaskNextAction::Continue; + }; + + let mut account = self.account_metadata.take().expect("Account metadata is not set"); + let next_order = self.prev_order.map(|prev| prev.wrapping_add(1)).unwrap_or(0); + let ss = self.shared_sequencer_client + .as_ref().expect("Shared sequencer client is not set"); + let blobs_bytes = postcard::to_allocvec(&blobs).expect("Failed to serialize SSBlob"); + + if let Err(err) = ss.send(self.signer.as_ref(), account, next_order, blobs_bytes).await { + return TaskNextAction::ErrorContinue(err); + } + + tracing::info!("Posted block to shared sequencer {blobs:?}"); + account.sequence = account.sequence.saturating_add(1); + self.prev_order = Some(next_order); + self.account_metadata = Some(account); + TaskNextAction::Continue + }, + } + } + + async fn shutdown(self) -> anyhow::Result<()> { + // Nothing to shut down because we don't have any temporary state that should be dumped, + // and we don't spawn any sub-tasks that we need to finish or await. + Ok(()) + } +} + +/// Creates an instance of runnable shared sequencer service. +pub fn new_service( + block_provider: B, + config: Config, + signer: Arc, +) -> anyhow::Result>> +where + B: BlocksProvider, + S: Signer, +{ + let blocks_events = block_provider.subscribe(); + Ok(ServiceRunner::new(NonInitializedTask::new( + config, + blocks_events, + signer, + )?)) +} diff --git a/crates/services/txpool_v2/src/tests/mocks.rs b/crates/services/txpool_v2/src/tests/mocks.rs index 1dca65bc9d3..eed1bf5f84f 100644 --- a/crates/services/txpool_v2/src/tests/mocks.rs +++ b/crates/services/txpool_v2/src/tests/mocks.rs @@ -126,16 +126,27 @@ impl StorageRead for MockDb { fn read( &self, key: &::Key, + offset: usize, buf: &mut [u8], ) -> Result, Self::Error> { let table = self.data.lock().unwrap(); let bytes = table.blobs.get(key); - let len = bytes.map(|bytes| { - buf.copy_from_slice(bytes.0.as_slice()); - bytes.0.len() - }); - Ok(len) + bytes + .map(|bytes| { + let bytes_len = bytes.as_ref().len(); + let start = offset; + let end = offset.saturating_add(buf.len()); + + if end > bytes_len { + return Err(()); + } + + let starting_from_offset = &bytes.as_ref()[start..end]; + buf[..].copy_from_slice(starting_from_offset); + Ok(buf.len()) + }) + .transpose() } fn read_alloc( diff --git a/crates/services/upgradable-executor/src/executor.rs b/crates/services/upgradable-executor/src/executor.rs index 2b8c72fa5af..82d8156b8c7 100644 --- a/crates/services/upgradable-executor/src/executor.rs +++ b/crates/services/upgradable-executor/src/executor.rs @@ -155,7 +155,9 @@ impl Executor { ("0-37-1", 13), ("0-38-0", 14), ("0-39-0", 15), - ("0-40-0", LATEST_STATE_TRANSITION_VERSION), + ("0-40-0", 16), + ("0-40-1", 17), + ("0-40-2", LATEST_STATE_TRANSITION_VERSION), ]; pub fn new( diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml index 8750f3ae8b0..4d5ab6b2265 100644 --- a/crates/storage/Cargo.toml +++ b/crates/storage/Cargo.toml @@ -24,7 +24,7 @@ fuel-core-types = { workspace = true, default-features = false, features = [ "serde", ] } fuel-vm-private = { workspace = true, default-features = false } -impl-tools = "0.10" +impl-tools = "0.10.3" itertools = { workspace = true, features = ["use_alloc"] } mockall = { workspace = true, optional = true } num_enum = { workspace = true } diff --git a/crates/storage/src/kv_store.rs b/crates/storage/src/kv_store.rs index 3e7c1359114..a7029db8d20 100644 --- a/crates/storage/src/kv_store.rs +++ b/crates/storage/src/kv_store.rs @@ -1,9 +1,6 @@ //! The module provides plain abstract definition of the key-value store. -use crate::{ - Error as StorageError, - Result as StorageResult, -}; +use crate::Result as StorageResult; #[cfg(feature = "alloc")] use alloc::{ @@ -67,18 +64,26 @@ pub trait KeyValueInspect { &self, key: &[u8], column: Self::Column, + offset: usize, buf: &mut [u8], ) -> StorageResult> { self.get(key, column)? .map(|value| { - let read = value.len(); - if read != buf.len() { - return Err(StorageError::Other(anyhow::anyhow!( - "Buffer size is not equal to the value size" - ))); + let bytes_len = value.as_ref().len(); + let start = offset; + let end = offset.saturating_add(buf.len()); + + if end > bytes_len { + return Err(anyhow::anyhow!( + "Offset `{offset}` is out of bounds `{bytes_len}` for key `{:?}`", + key + ) + .into()); } - buf.copy_from_slice(value.as_ref()); - Ok(read) + + let starting_from_offset = &value.as_ref()[start..end]; + buf[..].copy_from_slice(starting_from_offset); + Ok(buf.len()) }) .transpose() } @@ -111,9 +116,10 @@ where &self, key: &[u8], column: Self::Column, + offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.deref().read(key, column, buf) + self.deref().read(key, column, offset, buf) } } diff --git a/crates/storage/src/structured_storage.rs b/crates/storage/src/structured_storage.rs index 122792cbf8f..4e1239c5982 100644 --- a/crates/storage/src/structured_storage.rs +++ b/crates/storage/src/structured_storage.rs @@ -147,9 +147,10 @@ where &self, key: &[u8], column: Self::Column, + offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.inner.read(key, column, buf) + self.inner.read(key, column, offset, buf) } } @@ -358,6 +359,7 @@ where fn read( &self, key: &::Key, + offset: usize, buf: &mut [u8], ) -> Result, Self::Error> { let key_encoder = @@ -365,8 +367,12 @@ where key, ); let key_bytes = key_encoder.as_bytes(); - self.inner - .read(key_bytes.as_ref(), ::column(), buf) + self.inner.read( + key_bytes.as_ref(), + ::column(), + offset, + buf, + ) } fn read_alloc( diff --git a/crates/storage/src/transactional.rs b/crates/storage/src/transactional.rs index 4494393ad33..e5e568dd3ea 100644 --- a/crates/storage/src/transactional.rs +++ b/crates/storage/src/transactional.rs @@ -392,6 +392,7 @@ where &self, key: &[u8], column: Self::Column, + offset: usize, buf: &mut [u8], ) -> StorageResult> { if let Some(operation) = self.get_from_changes(key, column) { @@ -409,7 +410,7 @@ where WriteOperation::Remove => Ok(None), } } else { - self.storage.read(key, column, buf) + self.storage.read(key, column, offset, buf) } } } diff --git a/crates/storage/src/vm_storage.rs b/crates/storage/src/vm_storage.rs index 92ab65ffdaf..e70b42d6591 100644 --- a/crates/storage/src/vm_storage.rs +++ b/crates/storage/src/vm_storage.rs @@ -190,8 +190,13 @@ impl StorageRead for VmStorage where D: StorageRead, { - fn read(&self, key: &M::Key, buf: &mut [u8]) -> Result, Self::Error> { - StorageRead::::read(&self.database, key, buf) + fn read( + &self, + key: &M::Key, + offset: usize, + buf: &mut [u8], + ) -> Result, Self::Error> { + StorageRead::::read(&self.database, key, offset, buf) } fn read_alloc( @@ -344,11 +349,13 @@ where let mut state_key = Bytes32::zeroed(); let mut results = Vec::new(); - for _ in 0..range { + for i in 0..range { + if i != 0 { + key.increase()?; + } key.to_big_endian(state_key.as_mut()); let multikey = ContractsStateKey::new(contract_id, &state_key); results.push(self.database.storage::().get(&multikey)?); - key.increase()?; } Ok(results) } @@ -367,12 +374,15 @@ where // verify key is in range current_key - .checked_add(U256::from(values.len())) + .checked_add(U256::from(values.len().saturating_sub(1))) .ok_or_else(|| anyhow!("range op exceeded available keyspace"))?; let mut key_bytes = Bytes32::zeroed(); let mut found_unset = 0u32; - for value in values { + for (idx, value) in values.iter().enumerate() { + if idx != 0 { + current_key.increase()?; + } current_key.to_big_endian(key_bytes.as_mut()); let option = self @@ -385,8 +395,6 @@ where .checked_add(1) .expect("We've checked it above via `values.len()`"); } - - current_key.increase()?; } Ok(found_unset as usize) @@ -403,7 +411,10 @@ where let mut current_key = U256::from_big_endian(start_key.as_ref()); let mut key_bytes = Bytes32::zeroed(); - for _ in 0..range { + for i in 0..range { + if i != 0 { + current_key.increase()?; + } current_key.to_big_endian(key_bytes.as_mut()); let option = self @@ -412,8 +423,6 @@ where .take(&(contract_id, &key_bytes).into())?; found_unset |= option.is_none(); - - current_key.increase()?; } if found_unset { diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index 897869db7c1..cbb69a1beda 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -18,12 +18,16 @@ version = { workspace = true } [dependencies] anyhow = { workspace = true } +aws-sdk-kms = { workspace = true, optional = true } bs58 = { version = "0.5", optional = true } -derivative = { version = "2" } +derivative = { version = "2", default-features = false, optional = true, features = [ + "use_core", +] } derive_more = { version = "0.99" } fuel-vm-private = { workspace = true, default-features = false, features = [ "alloc", ] } +k256 = { version = "0.13", default-features = false, features = ["ecdsa"] } rand = { workspace = true, optional = true } secrecy = "0.8" serde = { workspace = true, features = ["derive"], optional = true } @@ -31,11 +35,16 @@ serde = { workspace = true, features = ["derive"], optional = true } tai64 = { version = "=4.0.0", features = ["serde"] } zeroize = "1.5" +[dev-dependencies] +aws-config = { version = "1.1.7", features = ["behavior-version-latest"] } +tokio = { workspace = true, features = ["macros"] } + [features] default = ["std"] -alloc = ["fuel-vm-private/alloc"] +alloc = ["fuel-vm-private/alloc", "derivative"] serde = ["dep:serde", "fuel-vm-private/serde"] da-compression = ["fuel-vm-private/da-compression"] std = ["alloc", "fuel-vm-private/std", "bs58"] random = ["dep:rand", "fuel-vm-private/random"] test-helpers = ["random", "fuel-vm-private/test-helpers"] +aws-kms = ["dep:aws-sdk-kms"] diff --git a/crates/types/src/blockchain/header.rs b/crates/types/src/blockchain/header.rs index 65aba0f1399..fe40de48b89 100644 --- a/crates/types/src/blockchain/header.rs +++ b/crates/types/src/blockchain/header.rs @@ -166,7 +166,7 @@ pub type ConsensusParametersVersion = u32; pub type StateTransitionBytecodeVersion = u32; /// The latest version of the state transition bytecode. -pub const LATEST_STATE_TRANSITION_VERSION: StateTransitionBytecodeVersion = 16; +pub const LATEST_STATE_TRANSITION_VERSION: StateTransitionBytecodeVersion = 18; #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/crates/types/src/blockchain/primitives.rs b/crates/types/src/blockchain/primitives.rs index f05826e186e..066ab8e9335 100644 --- a/crates/types/src/blockchain/primitives.rs +++ b/crates/types/src/blockchain/primitives.rs @@ -172,6 +172,12 @@ impl From<[u8; 32]> for BlockId { } } +impl AsRef for SecretKeyWrapper { + fn as_ref(&self) -> &SecretKey { + &self.0 + } +} + impl TryFrom<&'_ [u8]> for BlockId { type Error = TryFromSliceError; diff --git a/crates/types/src/entities/relayer/message.rs b/crates/types/src/entities/relayer/message.rs index 5be4cbda490..6e8e39b53c0 100644 --- a/crates/types/src/entities/relayer/message.rs +++ b/crates/types/src/entities/relayer/message.rs @@ -249,6 +249,7 @@ pub struct MerkleProof { pub proof_index: u64, } +#[derive(Debug, PartialEq, Eq)] /// Proves to da layer that this message was included in a Fuel block. pub struct MessageProof { /// Proof that message is contained within the provided block header. diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 7bf1d2b24ec..41bfbe42079 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -31,6 +31,7 @@ pub use tai64; pub mod blockchain; pub mod entities; pub mod services; +pub mod signer; /// Re-export of some fuel-vm types pub mod fuel_vm { diff --git a/crates/types/src/services.rs b/crates/types/src/services.rs index 948e804045f..2ed0a646263 100644 --- a/crates/types/src/services.rs +++ b/crates/types/src/services.rs @@ -7,6 +7,7 @@ pub mod graphql_api; #[cfg(feature = "std")] pub mod p2p; pub mod relayer; +pub mod shared_sequencer; #[cfg(feature = "std")] pub mod txpool; diff --git a/crates/types/src/services/shared_sequencer.rs b/crates/types/src/services/shared_sequencer.rs new file mode 100644 index 00000000000..e7e5bb2212d --- /dev/null +++ b/crates/types/src/services/shared_sequencer.rs @@ -0,0 +1,19 @@ +//! Module defines types for shared sequencer. + +use crate::{ + blockchain::primitives::BlockId, + fuel_types::BlockHeight, +}; + +/// The blob posted to the shared sequencer. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct SSBlob { + /// The Fuel block height. + pub block_height: BlockHeight, + /// The block ID that corresponds to the block height. + pub block_id: BlockId, +} + +/// The blobs posted to the shared sequencer. +pub type SSBlobs = alloc::vec::Vec; diff --git a/crates/services/consensus_module/poa/src/signer.rs b/crates/types/src/signer.rs similarity index 82% rename from crates/services/consensus_module/poa/src/signer.rs rename to crates/types/src/signer.rs index 876a9d6b5a7..221ab351472 100644 --- a/crates/services/consensus_module/poa/src/signer.rs +++ b/crates/types/src/signer.rs @@ -1,24 +1,11 @@ -use anyhow::anyhow; -#[cfg(feature = "aws-kms")] -use aws_sdk_kms::{ - primitives::Blob, - types::{ - MessageType, - SigningAlgorithmSpec, - }, -}; -#[cfg(feature = "aws-kms")] -use fuel_core_types::fuel_crypto::Message; -use fuel_core_types::{ - blockchain::{ - block::Block, - consensus::{ - poa::PoAConsensus, - Consensus, - }, - primitives::SecretKeyWrapper, +//! Block and generic data signing using a secret key or AWS KMS + +use crate::{ + blockchain::primitives::SecretKeyWrapper, + fuel_crypto::{ + Message, + PublicKey, }, - fuel_crypto::PublicKey, fuel_tx::{ Address, Input, @@ -29,7 +16,16 @@ use fuel_core_types::{ Secret, }, }; -use std::ops::Deref; +use anyhow::anyhow; +#[cfg(feature = "aws-kms")] +use aws_sdk_kms::{ + primitives::Blob, + types::{ + MessageType, + SigningAlgorithmSpec, + }, +}; +use core::ops::Deref; /// How the block is signed #[derive(Clone, Debug)] @@ -41,8 +37,11 @@ pub enum SignMode { /// Sign using AWS KMS #[cfg(feature = "aws-kms")] Kms { + /// The key ID in AWS KMS. key_id: String, + /// The AWS KMS client. client: aws_sdk_kms::Client, + /// The cached public key bytes. cached_public_key_bytes: Vec, }, } @@ -53,12 +52,9 @@ impl SignMode { !matches!(self, SignMode::Unavailable) } - /// Sign a block - pub async fn seal_block(&self, block: &Block) -> anyhow::Result { - let block_hash = block.id(); - let message = block_hash.into_message(); - - let poa_signature = match self { + /// Sign a prehashed message + pub async fn sign_message(&self, message: Message) -> anyhow::Result { + let signature = match self { SignMode::Unavailable => return Err(anyhow!("no PoA signing key configured")), SignMode::Key(key) => { let signing_key = key.expose_secret().deref(); @@ -71,7 +67,12 @@ impl SignMode { cached_public_key_bytes, } => sign_with_kms(client, key_id, cached_public_key_bytes, message).await?, }; - Ok(Consensus::PoA(PoAConsensus::new(poa_signature))) + Ok(signature) + } + + /// Sign a blob of data + pub async fn sign(&self, data: &[u8]) -> anyhow::Result { + self.sign_message(Message::new(data)).await } /// Returns the public key of the block producer, if any @@ -96,6 +97,31 @@ impl SignMode { } } + /// Returns the verifying key of the block producer, if any + pub fn verifying_key(&self) -> anyhow::Result> { + match self { + SignMode::Unavailable => Ok(None), + SignMode::Key(secret_key) => { + let secret: k256::SecretKey = secret_key.expose_secret().as_ref().into(); + let public_key = secret.public_key(); + + Ok(Some(public_key.into())) + } + + #[cfg(feature = "aws-kms")] + SignMode::Kms { + cached_public_key_bytes, + .. + } => { + use k256::pkcs8::DecodePublicKey; + + let k256_public_key = + k256::PublicKey::from_public_key_der(cached_public_key_bytes)?; + Ok(Some(k256_public_key.into())) + } + } + } + /// Returns the address of the block producer, if any pub fn address(&self) -> anyhow::Result> { let address = self.public_key()?.as_ref().map(Input::owner); @@ -126,7 +152,7 @@ async fn sign_with_kms( .message(Blob::new(*message)) .send() .await - .inspect_err(|err| tracing::error!("Failed to sign with AWS KMS: {err:?}"))?; + .map_err(|err| anyhow::anyhow!("Failed to sign with AWS KMS: {err:?}"))?; let signature_der = reply .signature .ok_or_else(|| anyhow!("no signature returned from AWS KMS"))? @@ -176,7 +202,7 @@ mod tests { use std::str::FromStr; use super::*; - use fuel_core_types::fuel_crypto::SecretKey; + use crate::fuel_crypto::SecretKey; use rand::{ rngs::StdRng, SeedableRng, diff --git a/tests/proptest-regressions/recovery.txt b/tests/proptest-regressions/recovery.txt index 964abdad7e9..28f5541dd30 100644 --- a/tests/proptest-regressions/recovery.txt +++ b/tests/proptest-regressions/recovery.txt @@ -6,3 +6,4 @@ # everyone who runs the test benefits from these saved cases. cc 7c4e472cb1f611f337e2b9e029d79cad58b6e80786135c0cea9e37808467f109 # shrinks to (height, lower_height) = (53, 0) cc 34f1effd503ddfe54ad3da854765812601969db5335e6bf5c385a99a7ef59e90 # shrinks to (height, lower_height) = (64, 27) +cc 46741826ecf4f441d17d822657a94fb33b941f19c222d181ccbb865b0b62b7fc # shrinks to (height, lower_height) = (4, 3) diff --git a/tests/test-helpers/src/builder.rs b/tests/test-helpers/src/builder.rs index 0d16bc48dc6..fa23f8608a1 100644 --- a/tests/test-helpers/src/builder.rs +++ b/tests/test-helpers/src/builder.rs @@ -13,6 +13,7 @@ use fuel_core::{ DbType, FuelService, }, + state::rocks_db::DatabaseConfig, }; use fuel_core_client::client::FuelClient; use fuel_core_poa::Trigger; @@ -99,6 +100,8 @@ pub struct TestSetupBuilder { pub privileged_address: Address, pub base_asset_id: AssetId, pub trigger: Trigger, + pub database_type: DbType, + pub database_config: DatabaseConfig, } impl TestSetupBuilder { @@ -231,14 +234,14 @@ impl TestSetupBuilder { ..StateConfig::default() }; - let config = Config { + let mut config = Config { utxo_validation: self.utxo_validation, txpool: fuel_core_txpool::config::Config::default(), block_production: self.trigger, starting_gas_price: self.starting_gas_price, ..Config::local_node_with_configs(chain_conf, state) }; - assert_eq!(config.combined_db_config.database_type, DbType::RocksDb); + config.combined_db_config.database_config = self.database_config; let srv = FuelService::new_node(config).await.unwrap(); let client = FuelClient::from(srv.bound_address); @@ -265,6 +268,8 @@ impl Default for TestSetupBuilder { privileged_address: Default::default(), base_asset_id: AssetId::BASE, trigger: Trigger::Instant, + database_type: DbType::RocksDb, + database_config: DatabaseConfig::config_for_tests(), } } } diff --git a/tests/tests/aws_kms.rs b/tests/tests/aws_kms.rs index 31dcb8e0448..e832a4afc2c 100644 --- a/tests/tests/aws_kms.rs +++ b/tests/tests/aws_kms.rs @@ -1,4 +1,7 @@ -use fuel_core::combined_database::CombinedDatabase; +use fuel_core::{ + combined_database::CombinedDatabase, + state::rocks_db::DatabaseConfig, +}; use fuel_core_storage::transactional::AtomicView; use fuel_core_types::blockchain::consensus::Consensus; use test_helpers::fuel_core_driver::FuelCoreDriver; @@ -47,8 +50,12 @@ async fn can_get_sealed_block_from_poa_produced_block_when_signing_with_kms() { // stop the node and just grab the database let db_path = driver.kill().await; - let db = CombinedDatabase::open(db_path.path(), 1024 * 1024, Default::default(), 512) - .unwrap(); + let db = CombinedDatabase::open( + db_path.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let view = db.on_chain().latest_view().unwrap(); diff --git a/tests/tests/balances.rs b/tests/tests/balances.rs index b2804141295..a658e36e9f2 100644 --- a/tests/tests/balances.rs +++ b/tests/tests/balances.rs @@ -33,6 +33,9 @@ use fuel_core_types::{ }, }; +const RETRYABLE: &[u8] = &[1]; +const NON_RETRYABLE: &[u8] = &[]; + #[tokio::test] async fn balance() { let owner = Address::default(); @@ -55,18 +58,22 @@ async fn balance() { ..coin_generator.generate() }) .collect(), - messages: vec![(owner, 60), (owner, 90)] - .into_iter() - .enumerate() - .map(|(nonce, (owner, amount))| MessageConfig { - sender: owner, - recipient: owner, - nonce: (nonce as u64).into(), - amount, - data: vec![], - da_height: DaBlockHeight::from(0usize), - }) - .collect(), + messages: vec![ + (owner, 60, NON_RETRYABLE), + (owner, 90, NON_RETRYABLE), + (owner, 200000, RETRYABLE), + ] + .into_iter() + .enumerate() + .map(|(nonce, (owner, amount, data))| MessageConfig { + sender: owner, + recipient: owner, + nonce: (nonce as u64).into(), + amount, + data: data.to_vec(), + da_height: DaBlockHeight::from(0usize), + }) + .collect(), ..Default::default() }; let config = Config::local_node_with_state_config(state_config); @@ -129,6 +136,8 @@ async fn balance() { client.submit_and_await_commit(&tx).await.unwrap(); let balance = client.balance(&owner, Some(&asset_id)).await.unwrap(); + + // Note that the big (200000) message, which is RETRYABLE is not included in the balance assert_eq!(balance, 449); } @@ -137,9 +146,6 @@ async fn balance_messages_only() { let owner = Address::default(); let asset_id = AssetId::BASE; - const RETRYABLE: &[u8] = &[1]; - const NON_RETRYABLE: &[u8] = &[]; - // setup config let state_config = StateConfig { contracts: vec![], diff --git a/tests/tests/blocks.rs b/tests/tests/blocks.rs index 923d457af93..ac367a62f08 100644 --- a/tests/tests/blocks.rs +++ b/tests/tests/blocks.rs @@ -17,10 +17,7 @@ use fuel_core_client::client::{ types::TransactionStatus, FuelClient, }; -use fuel_core_poa::{ - signer::SignMode, - Trigger, -}; +use fuel_core_poa::Trigger; use fuel_core_storage::{ tables::{ FuelBlocks, @@ -37,6 +34,7 @@ use fuel_core_types::{ }, fuel_tx::*, secrecy::ExposeSecret, + signer::SignMode, tai64::Tai64, }; use itertools::{ diff --git a/tests/tests/da_compression.rs b/tests/tests/da_compression.rs index b4f82b39a53..cd0d1430375 100644 --- a/tests/tests/da_compression.rs +++ b/tests/tests/da_compression.rs @@ -23,7 +23,6 @@ use fuel_core_compression::{ decompress::decompress, VersionedCompressedBlock, }; -use fuel_core_poa::signer::SignMode; use fuel_core_storage::transactional::{ HistoricalView, IntoTransaction, @@ -42,6 +41,7 @@ use fuel_core_types::{ TxPointer, }, secrecy::Secret, + signer::SignMode, }; use rand::{ rngs::StdRng, diff --git a/tests/tests/health.rs b/tests/tests/health.rs index d0b241fcb5a..231628493f1 100644 --- a/tests/tests/health.rs +++ b/tests/tests/health.rs @@ -5,6 +5,7 @@ use fuel_core::{ Config, FuelService, }, + state::rocks_db::DatabaseConfig, types::fuel_tx::Transaction, }; use fuel_core_client::client::FuelClient; @@ -28,9 +29,12 @@ async fn can_restart_node() { // start node once { - let database = - Database::open_rocksdb(tmp_dir.path(), None, Default::default(), 512) - .unwrap(); + let database = Database::open_rocksdb( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let first_startup = FuelService::from_database(database, Config::local_node()) .await .unwrap(); @@ -41,9 +45,12 @@ async fn can_restart_node() { } { - let database = - Database::open_rocksdb(tmp_dir.path(), None, Default::default(), 512) - .unwrap(); + let database = Database::open_rocksdb( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let _second_startup = FuelService::from_database(database, Config::local_node()) .await .unwrap(); @@ -52,14 +59,16 @@ async fn can_restart_node() { #[tokio::test] async fn can_restart_node_with_transactions() { - let capacity = 1024 * 1024; let tmp_dir = tempfile::TempDir::new().unwrap(); { // Given - let database = - CombinedDatabase::open(tmp_dir.path(), capacity, Default::default(), 512) - .unwrap(); + let database = CombinedDatabase::open( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let service = FuelService::from_combined_database(database, Config::local_node()) .await .unwrap(); @@ -76,9 +85,12 @@ async fn can_restart_node_with_transactions() { { // When - let database = - CombinedDatabase::open(tmp_dir.path(), capacity, Default::default(), 512) - .unwrap(); + let database = CombinedDatabase::open( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let service = FuelService::from_combined_database(database, Config::local_node()) .await .unwrap(); diff --git a/tests/tests/poa.rs b/tests/tests/poa.rs index d043d9b4ef9..0dc28a58387 100644 --- a/tests/tests/poa.rs +++ b/tests/tests/poa.rs @@ -12,13 +12,13 @@ use fuel_core_client::client::{ types::TransactionStatus, FuelClient, }; -use fuel_core_poa::signer::SignMode; use fuel_core_storage::transactional::AtomicView; use fuel_core_types::{ blockchain::consensus::Consensus, fuel_crypto::SecretKey, fuel_tx::Transaction, secrecy::Secret, + signer::SignMode, }; use rand::{ rngs::StdRng, @@ -183,7 +183,7 @@ mod p2p { // after the first_producer stops, second_producer should start producing blocks #[tokio::test(flavor = "multi_thread")] async fn test_poa_multiple_producers() { - const SYNC_TIMEOUT: u64 = 30; + const SYNC_TIMEOUT: u64 = 5; const TIME_UNTIL_SYNCED: u64 = SYNC_TIMEOUT + 10; let mut rng = StdRng::seed_from_u64(2222); @@ -201,7 +201,9 @@ mod p2p { let make_node_config = |name: &str| { let mut config = make_config(name.to_string(), config.clone()); config.debug = true; - config.block_production = Trigger::Never; + config.block_production = Trigger::Interval { + block_time: Duration::from_secs(1), + }; config.consensus_signer = SignMode::Key(Secret::new(secret.into())); config.p2p.as_mut().unwrap().bootstrap_nodes = bootstrap.listeners(); config.p2p.as_mut().unwrap().reserved_nodes = bootstrap.listeners(); @@ -216,7 +218,7 @@ mod p2p { let first_producer = make_node(first_producer_config, vec![]).await; - // The first producer should produce 3 blocks. + // The first producer should produce 1 block manually after `SYNC_TIMEOUT` seconds. first_producer .node .shared @@ -224,14 +226,15 @@ mod p2p { .manually_produce_blocks( None, Mode::Blocks { - number_of_blocks: 3, + number_of_blocks: 1, }, ) .await - .expect("The first should produce 3 blocks"); + .expect("The first should produce 1 block manually"); - // Start the second producer after 3 blocks. - // The second producer should synchronize 3 blocks produced by the first producer. + // After 1 manual block start the second producer. + // The first producer should produce 2 more blocks. + // The second producer should synchronize 3(1 manual and 2 produced) blocks. let second_producer = make_node(second_producer_config, vec![]).await; tokio::time::timeout( Duration::from_secs(SYNC_TIMEOUT), @@ -258,11 +261,11 @@ mod p2p { .manually_produce_blocks( None, Mode::Blocks { - number_of_blocks: 2, + number_of_blocks: 1, }, ) .await - .expect("The second should produce 2 blocks"); + .expect("The second should produce 1 blocks"); assert!(start_time.elapsed() >= Duration::from_secs(TIME_UNTIL_SYNCED)); // Restart fresh first producer. @@ -270,7 +273,7 @@ mod p2p { let first_producer = make_node(make_node_config("First Producer reborn"), vec![]).await; tokio::time::timeout( - Duration::from_secs(SYNC_TIMEOUT), + Duration::from_secs(TIME_UNTIL_SYNCED), first_producer.wait_for_blocks(5, false /* is_local */), ) .await diff --git a/tests/tests/recovery.rs b/tests/tests/recovery.rs index 60806b58f05..07a2c9797d1 100644 --- a/tests/tests/recovery.rs +++ b/tests/tests/recovery.rs @@ -171,7 +171,12 @@ async fn _gas_price_updater__can_recover_on_startup_when_gas_price_db_is_behind( for _ in 0..diff { let _ = database.gas_price().rollback_last_block(); } - assert!(database.on_chain().latest_height() > database.gas_price().latest_height()); + assert!( + database.on_chain().latest_height() > database.gas_price().latest_height(), + "on_chain: {:?}, gas_price: {:?}", + database.on_chain().latest_height(), + database.gas_price().latest_height() + ); let temp_dir = driver.kill().await; // When diff --git a/tests/tests/relayer.rs b/tests/tests/relayer.rs index 9ea80b1e242..1e9c2dd542f 100644 --- a/tests/tests/relayer.rs +++ b/tests/tests/relayer.rs @@ -15,6 +15,7 @@ use fuel_core::{ Config, FuelService, }, + state::rocks_db::DatabaseConfig, }; use fuel_core_client::client::{ pagination::{ @@ -22,6 +23,7 @@ use fuel_core_client::client::{ PaginationRequest, }, types::{ + CoinType, RelayedTransactionStatus as ClientRelayedTransactionStatus, TransactionStatus, }, @@ -72,9 +74,15 @@ use std::{ SocketAddr, }, sync::Arc, + time::Duration, }; use tokio::sync::oneshot::Sender; +enum MessageKind { + Retryable { nonce: u64, amount: u64 }, + NonRetryable { nonce: u64, amount: u64 }, +} + #[tokio::test(flavor = "multi_thread")] async fn relayer_can_download_logs() { let mut config = Config::local_node(); @@ -325,14 +333,16 @@ async fn can_restart_node_with_relayer_data() { .try_into() .unwrap()]); - let capacity = 1024 * 1024; let tmp_dir = tempfile::TempDir::new().unwrap(); { // Given - let database = - CombinedDatabase::open(tmp_dir.path(), capacity, Default::default(), 512) - .unwrap(); + let database = CombinedDatabase::open( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let service = FuelService::from_combined_database(database, config.clone()) .await @@ -350,9 +360,12 @@ async fn can_restart_node_with_relayer_data() { { // When - let database = - CombinedDatabase::open(tmp_dir.path(), capacity, Default::default(), 512) - .unwrap(); + let database = CombinedDatabase::open( + tmp_dir.path(), + Default::default(), + DatabaseConfig::config_for_tests(), + ) + .unwrap(); let service = FuelService::from_combined_database(database, config) .await .unwrap(); @@ -477,3 +490,233 @@ async fn handle( Ok(Response::new(Body::from(r))) } + +#[tokio::test(flavor = "multi_thread")] +async fn balances_and_coins_to_spend_never_return_retryable_messages() { + let mut rng = StdRng::seed_from_u64(1234); + let mut config = Config::local_node(); + config.relayer = Some(relayer::Config::default()); + let relayer_config = config.relayer.as_mut().expect("Expected relayer config"); + let eth_node = MockMiddleware::default(); + let contract_address = relayer_config.eth_v2_listening_contracts[0]; + const TIMEOUT: Duration = Duration::from_secs(1); + + // Large enough to get all messages, but not to trigger the "query is too complex" error. + const UNLIMITED_QUERY_RESULTS: i32 = 100; + + // Given + + // setup a retryable and non-retryable message + let secret_key: SecretKey = SecretKey::random(&mut rng); + let public_key = secret_key.public_key(); + let recipient = Input::owner(&public_key); + + const RETRYABLE_AMOUNT: u64 = 99; + const RETRYABLE_NONCE: u64 = 0; + const NON_RETRYABLE_AMOUNT: u64 = 100; + const NON_RETRYABLE_NONCE: u64 = 1; + let messages = vec![ + MessageKind::Retryable { + nonce: RETRYABLE_NONCE, + amount: RETRYABLE_AMOUNT, + }, + MessageKind::NonRetryable { + nonce: NON_RETRYABLE_NONCE, + amount: NON_RETRYABLE_AMOUNT, + }, + ]; + let logs: Vec<_> = setup_messages(&messages, &recipient, &contract_address); + + eth_node.update_data(|data| data.logs_batch = vec![logs.clone()]); + // Setup the eth node with a block high enough that there + // will be some finalized blocks. + eth_node.update_data(|data| data.best_block.number = Some(200.into())); + let eth_node = Arc::new(eth_node); + let eth_node_handle = spawn_eth_node(eth_node).await; + + relayer_config.relayer = Some(vec![format!("http://{}", eth_node_handle.address) + .as_str() + .try_into() + .unwrap()]); + + config.utxo_validation = true; + + // setup fuel node with mocked eth url + let db = Database::in_memory(); + + let srv = FuelService::from_database(db.clone(), config) + .await + .unwrap(); + + let client = FuelClient::from(srv.bound_address); + let base_asset_id = client + .consensus_parameters(0) + .await + .unwrap() + .unwrap() + .base_asset_id() + .clone(); + + // When + + // wait for relayer to catch up to eth node + srv.await_relayer_synced().await.unwrap(); + // Wait for the block producer to create a block that targets the latest da height. + srv.shared + .poa_adapter + .manually_produce_blocks( + None, + Mode::Blocks { + number_of_blocks: 1, + }, + ) + .await + .unwrap(); + + // Balances are processed in the off-chain worker, so we need to wait for it + // to process the messages before we can assert the balances. + let result = tokio::time::timeout(TIMEOUT, async { + loop { + let query = client + .balances( + &recipient, + PaginationRequest { + cursor: None, + results: UNLIMITED_QUERY_RESULTS, + direction: PageDirection::Forward, + }, + ) + .await + .unwrap(); + + if !query.results.is_empty() { + break; + } + } + }) + .await; + if let Err(_) = result { + panic!("Off-chain worker didn't process balances within timeout") + } + // Then + + // Expect two messages to be available + let query = client + .messages( + None, + PaginationRequest { + cursor: None, + results: UNLIMITED_QUERY_RESULTS, + direction: PageDirection::Forward, + }, + ) + .await + .unwrap(); + assert_eq!(query.results.len(), 2); + let total_amount = query.results.iter().map(|m| m.amount).sum::(); + assert_eq!(total_amount, NON_RETRYABLE_AMOUNT + RETRYABLE_AMOUNT); + + // Expect only the non-retryable message balance to be returned via "balance" + let query = client + .balance(&recipient, Some(&base_asset_id)) + .await + .unwrap(); + assert_eq!(query, NON_RETRYABLE_AMOUNT); + + // Expect only the non-retryable message balance to be returned via "balances" + let query = client + .balances( + &recipient, + PaginationRequest { + cursor: None, + results: UNLIMITED_QUERY_RESULTS, + direction: PageDirection::Forward, + }, + ) + .await + .unwrap(); + assert_eq!(query.results.len(), 1); + let total_amount = query + .results + .iter() + .map(|m| { + assert_eq!(m.asset_id, base_asset_id); + m.amount + }) + .sum::(); + assert_eq!(total_amount, NON_RETRYABLE_AMOUNT as u128); + + // Expect only the non-retryable message balance to be returned via "coins to spend" + let query = client + .coins_to_spend( + &recipient, + vec![(base_asset_id, NON_RETRYABLE_AMOUNT, None)], + None, + ) + .await + .unwrap(); + let message_coins: Vec<_> = query + .iter() + .flatten() + .map(|m| { + let CoinType::MessageCoin(m) = m else { + panic!("should have message coin") + }; + m + }) + .collect(); + assert_eq!(message_coins.len(), 1); + assert_eq!(message_coins[0].amount, NON_RETRYABLE_AMOUNT); + assert_eq!(message_coins[0].nonce, NON_RETRYABLE_NONCE.into()); + + // Expect no messages when querying more than the available non-retryable amount + let query = client + .coins_to_spend( + &recipient, + vec![(base_asset_id, NON_RETRYABLE_AMOUNT + 1, None)], + None, + ) + .await + .unwrap_err(); + assert_eq!( + query.to_string(), + "Response errors; not enough coins to fit the target" + ); + + srv.send_stop_signal_and_await_shutdown().await.unwrap(); + eth_node_handle.shutdown.send(()).unwrap(); +} + +fn setup_messages( + messages: &[MessageKind], + recipient: &Address, + contract_address: &Bytes20, +) -> Vec { + const SENDER: Address = Address::zeroed(); + + messages + .iter() + .map(|m| match m { + MessageKind::Retryable { nonce, amount } => make_message_event( + Nonce::from(*nonce), + 5, + *contract_address, + Some(SENDER.into()), + Some((*recipient).into()), + Some(*amount), + Some(vec![1]), + 0, + ), + MessageKind::NonRetryable { nonce, amount } => make_message_event( + Nonce::from(*nonce), + 5, + *contract_address, + Some(SENDER.into()), + Some((*recipient).into()), + Some(*amount), + None, + 0, + ), + }) + .collect() +} diff --git a/tests/tests/trigger_integration/instant.rs b/tests/tests/trigger_integration/instant.rs index df18da60190..848dd7e2aff 100644 --- a/tests/tests/trigger_integration/instant.rs +++ b/tests/tests/trigger_integration/instant.rs @@ -12,15 +12,13 @@ use fuel_core_client::client::{ }, FuelClient, }; -use fuel_core_poa::{ - signer::SignMode, - Trigger, -}; +use fuel_core_poa::Trigger; use fuel_core_types::{ fuel_asm::*, fuel_crypto::SecretKey, fuel_tx::TransactionBuilder, secrecy::Secret, + signer::SignMode, }; use rand::{ rngs::StdRng, diff --git a/tests/tests/trigger_integration/interval.rs b/tests/tests/trigger_integration/interval.rs index b928e54c35d..bf09c7083ca 100644 --- a/tests/tests/trigger_integration/interval.rs +++ b/tests/tests/trigger_integration/interval.rs @@ -12,15 +12,13 @@ use fuel_core_client::client::{ }, FuelClient, }; -use fuel_core_poa::{ - signer::SignMode, - Trigger, -}; +use fuel_core_poa::Trigger; use fuel_core_types::{ fuel_asm::*, fuel_crypto::SecretKey, fuel_tx::TransactionBuilder, secrecy::Secret, + signer::SignMode, }; use rand::{ rngs::StdRng, diff --git a/tests/tests/trigger_integration/never.rs b/tests/tests/trigger_integration/never.rs index ac412583ef4..63a3ceff85a 100644 --- a/tests/tests/trigger_integration/never.rs +++ b/tests/tests/trigger_integration/never.rs @@ -12,15 +12,13 @@ use fuel_core_client::client::{ }, FuelClient, }; -use fuel_core_poa::{ - signer::SignMode, - Trigger, -}; +use fuel_core_poa::Trigger; use fuel_core_types::{ fuel_asm::op, fuel_crypto::SecretKey, fuel_tx::TransactionBuilder, secrecy::Secret, + signer::SignMode, }; use rand::{ rngs::StdRng, diff --git a/tests/tests/vm_storage.rs b/tests/tests/vm_storage.rs index cff95ef5053..1b98fa63011 100644 --- a/tests/tests/vm_storage.rs +++ b/tests/tests/vm_storage.rs @@ -86,6 +86,11 @@ mod tests { => Err(()) ; "read fails on partially initialized range if keyspace exceeded" )] + #[test_case( + &[(*u256_to_bytes32(U256::MAX), vec![0; 32])], *u256_to_bytes32(U256::MAX), 1 + => Ok(vec![Some(vec![0; 32])]) + ; "read success when reading last key" + )] fn read_sequential_range( prefilled_slots: &[([u8; 32], Vec)], start_key: [u8; 32], @@ -161,6 +166,11 @@ mod tests { => Err(()) ; "insert fails if start_key + range > u256::MAX" )] + #[test_case( + &[(*u256_to_bytes32(U256::MAX), vec![0; 32])], *u256_to_bytes32(U256::MAX), &[vec![1; 32]] + => Ok(true) + ; "try to modify only the last value of storage" + )] fn insert_range( prefilled_slots: &[([u8; 32], Vec)], start_key: [u8; 32], @@ -255,6 +265,11 @@ mod tests { => (vec![], false) ; "remove multiple slots over partially uninitialized middle range" )] + #[test_case( + &[(*u256_to_bytes32(U256::MAX), vec![0; 32])], *u256_to_bytes32(U256::MAX), 1 + => (vec![], true) + ; "try to modify only the last value of storage" + )] fn remove_range( prefilled_slots: &[([u8; 32], Vec)], start_key: [u8; 32],