diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2d60ecce84..e7aa445f90 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -186,7 +186,7 @@ jobs: SECONDS=0 while ((SECONDS <= 300)) do - if curl -f -s -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"bank_supplyOf","params":["sov16m8fxq0x5wc5aw75fx9rus2p7g2l22zf4re72c3m058g77cdjemsavg2ft"],"id":1}' http://127.0.0.1:12345 | grep -q 1000; then + if curl -f -s -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"bank_supplyOf","params":["sov1zdwj8thgev2u3yyrrlekmvtsz4av4tp3m7dm5mx5peejnesga27svq9m72"],"id":1}' http://127.0.0.1:12345 | grep -q 1000; then echo "demo-rollup test succeeded" exit 0 fi diff --git a/Cargo.lock b/Cargo.lock index 3744fc5242..a8c84afb18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,39 +937,6 @@ dependencies = [ "libc", ] -[[package]] -name = "celestia" -version = "0.2.0" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.21.2", - "bech32 0.9.1", - "borsh", - "hex", - "hex-literal 0.4.1", - "jsonrpsee", - "nmt-rs", - "postcard", - "proptest", - "prost", - "prost-build", - "prost-types", - "risc0-zkvm", - "risc0-zkvm-platform", - "serde", - "serde_json", - "sha2 0.10.7", - "sov-rollup-interface", - "tendermint", - "tendermint-proto", - "thiserror", - "tokio", - "tracing", - "wiremock", - "zk-cycle-macros", -] - [[package]] name = "cexpr" version = "0.6.0" @@ -5637,24 +5604,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "risc0-adapter" -version = "0.2.0" -dependencies = [ - "anyhow", - "bincode", - "bytemuck", - "once_cell", - "parking_lot 0.12.1", - "risc0-circuit-rv32im", - "risc0-zkp", - "risc0-zkvm", - "risc0-zkvm-platform", - "serde", - "sov-rollup-interface", - "zk-cycle-utils", -] - [[package]] name = "risc0-build-kernel" version = "0.16.1" @@ -6609,7 +6558,7 @@ dependencies = [ [[package]] name = "sov-celestia-adapter" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "async-trait", @@ -6800,6 +6749,7 @@ dependencies = [ "ethers-signers", "hex", "jsonrpsee", + "lazy_static", "primitive-types", "reth-primitives", "reth-revm", @@ -6935,7 +6885,7 @@ dependencies = [ [[package]] name = "sov-risc0-adapter" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "bincode", @@ -7111,7 +7061,7 @@ dependencies = [ [[package]] name = "sov-zk-cycle-macros" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "borsh", @@ -7127,7 +7077,7 @@ dependencies = [ [[package]] name = "sov-zk-cycle-utils" -version = "0.1.0" +version = "0.2.0" dependencies = [ "risc0-zkvm", "risc0-zkvm-platform", @@ -8459,30 +8409,6 @@ dependencies = [ "zstd 0.11.2+zstd.1.5.2", ] -[[package]] -name = "zk-cycle-macros" -version = "0.2.0" -dependencies = [ - "anyhow", - "borsh", - "proc-macro2 1.0.66", - "quote 1.0.33", - "risc0-zkvm", - "risc0-zkvm-platform", - "syn 1.0.109", - "trybuild", - "zk-cycle-macros", - "zk-cycle-utils", -] - -[[package]] -name = "zk-cycle-utils" -version = "0.2.0" -dependencies = [ - "risc0-zkvm", - "risc0-zkvm-platform", -] - [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" diff --git a/adapters/celestia/Cargo.toml b/adapters/celestia/Cargo.toml index 347b0c1f2a..6e103ad33b 100644 --- a/adapters/celestia/Cargo.toml +++ b/adapters/celestia/Cargo.toml @@ -33,8 +33,11 @@ sov-zk-cycle-macros = { path = "../../utils/zk-cycle-macros", version = "0.2", o risc0-zkvm = { version = "0.16", default-features = false, features = ["std"], optional = true } risc0-zkvm-platform = { version = "0.16", optional = true } -sov-rollup-interface = { path = "../../rollup-interface" } -nmt-rs = { git = "https://github.com/Sovereign-Labs/nmt-rs.git", rev = "dd37588444fca72825d11fe4a46838f66525c49f", features = ["serde", "borsh"] } +sov-rollup-interface = { path = "../../rollup-interface", version = "0.2" } +nmt-rs = { git = "https://github.com/Sovereign-Labs/nmt-rs.git", rev = "dd37588444fca72825d11fe4a46838f66525c49f", features = [ + "serde", + "borsh", +] } [dev-dependencies] diff --git a/examples/demo-prover/Cargo.lock b/examples/demo-prover/Cargo.lock index 1fe80013b9..d690d6511f 100644 --- a/examples/demo-prover/Cargo.lock +++ b/examples/demo-prover/Cargo.lock @@ -3681,7 +3681,7 @@ dependencies = [ [[package]] name = "sov-celestia-adapter" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "async-trait", @@ -3809,6 +3809,7 @@ dependencies = [ "jsonrpsee 0.18.2", "serde", "serde_json", + "sov-celestia-adapter", "sov-cli", "sov-db", "sov-modules-api", @@ -3886,7 +3887,7 @@ dependencies = [ [[package]] name = "sov-risc0-adapter" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "bincode", @@ -4017,7 +4018,7 @@ dependencies = [ [[package]] name = "sov-zk-cycle-macros" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "borsh", @@ -4026,6 +4027,14 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sov-zk-cycle-utils" +version = "0.2.0" +dependencies = [ + "risc0-zkvm", + "risc0-zkvm-platform", +] + [[package]] name = "spin" version = "0.5.2" @@ -4932,14 +4941,6 @@ dependencies = [ "zstd", ] -[[package]] -name = "sov-zk-cycle-utils" -version = "0.1.0" -dependencies = [ - "risc0-zkvm", - "risc0-zkvm-platform", -] - [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" diff --git a/examples/demo-prover/methods/guest/Cargo.lock b/examples/demo-prover/methods/guest/Cargo.lock index a223ede2c4..c3064dbcf4 100644 --- a/examples/demo-prover/methods/guest/Cargo.lock +++ b/examples/demo-prover/methods/guest/Cargo.lock @@ -2060,6 +2060,33 @@ dependencies = [ "tracing", ] +[[package]] +name = "sov-celestia-adapter" +version = "0.2.0" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.21.2", + "bech32", + "borsh", + "hex", + "hex-literal", + "nmt-rs", + "prost", + "prost-build", + "prost-types", + "risc0-zkvm", + "risc0-zkvm-platform", + "serde", + "sha2 0.10.6", + "sov-rollup-interface", + "sov-zk-cycle-macros", + "tendermint", + "tendermint-proto", + "thiserror", + "tracing", +] + [[package]] name = "sov-chain-state" version = "0.2.0" @@ -2073,6 +2100,18 @@ dependencies = [ [[package]] name = "sov-demo-prover-guest" version = "0.2.0" +name = "sov-chain-state" +version = "0.1.0" +dependencies = [ + "anyhow", + "borsh", + "sov-modules-api", + "sov-state", +] + +[[package]] +name = "sov-demo-prover-guest" +version = "0.1.0" dependencies = [ "anyhow", "borsh", @@ -2134,7 +2173,7 @@ dependencies = [ "quote", "schemars", "syn 1.0.109", - "toml 0.7.6", + "toml 0.7.7", ] [[package]] @@ -2152,14 +2191,14 @@ dependencies = [ "sov-rollup-interface", "sov-state", "sov-zk-cycle-macros", + "sov-zk-cycle-utils", "thiserror", "tracing", - "sov-zk-cycle-utils", ] [[package]] name = "sov-risc0-adapter" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "bincode", @@ -2233,7 +2272,7 @@ dependencies = [ [[package]] name = "sov-zk-cycle-macros" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "borsh", @@ -2242,6 +2281,14 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sov-zk-cycle-utils" +version = "0.2.0" +dependencies = [ + "risc0-zkvm", + "risc0-zkvm-platform", +] + [[package]] name = "spki" version = "0.7.2" @@ -2468,9 +2515,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +checksum = "de0a3ab2091e52d7299a39d098e200114a972df0a7724add02a273aa9aada592" dependencies = [ "serde", "serde_spanned", @@ -2489,9 +2536,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.0.0", "serde", @@ -2866,14 +2913,6 @@ dependencies = [ "zstd", ] -[[package]] -name = "sov-zk-cycle-utils" -version = "0.1.0" -dependencies = [ - "risc0-zkvm", - "risc0-zkvm-platform", -] - [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" diff --git a/examples/demo-rollup/Makefile b/examples/demo-rollup/Makefile index b44d83d28d..a49036df02 100644 --- a/examples/demo-rollup/Makefile +++ b/examples/demo-rollup/Makefile @@ -97,16 +97,16 @@ test-generate-create-token-tx: check-container-running build-sov-cli $(SOV_CLI_REL_PATH) transactions import from-file bank --path ../test-data/requests/create_token.json set-rpc-url: build-sov-cli - $(SOV_CLI_REL_PATH) rpc set-url http://localhost:12345 + $(SOV_CLI_REL_PATH) rpc set-url http://127.0.0.1:12345 import-keys: build-sov-cli - $(SOV_CLI_REL_PATH) keys import --nickname DANGER__DO_NOT_USE_WITH_REAL_MONEY --path ../test-data/keys/minter_private_key.json + $(SOV_CLI_REL_PATH) keys import --nickname DANGER__DO_NOT_USE_WITH_REAL_MONEY --path ../test-data/keys/token_deployer_private_key.json test-create-token: set-rpc-url test-generate-create-token-tx import-keys - $(SOV_CLI_REL_PATH) rpc submit-batch + $(SOV_CLI_REL_PATH) rpc submit-batch by-nickname DANGER__DO_NOT_USE_WITH_REAL_MONEY remove-insecure-keys: build-sov-cli - $(SOV_CLI_REL_PATH) keys remove by-address sov15vspj48hpttzyvxu8kzq5klhvaczcpyxn6z6k0hwpwtzs4a6wkvqwr57gc + $(SOV_CLI_REL_PATH) keys remove by-address sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94 clean-rollup-db: $(eval path := ./$(shell awk -F'=' '/^path/ {print $$2}' rollup_config.toml | tr -d '[:space:]"\n')) diff --git a/examples/demo-rollup/README.md b/examples/demo-rollup/README.md index 895fa9b6e0..eac22fabf2 100644 --- a/examples/demo-rollup/README.md +++ b/examples/demo-rollup/README.md @@ -17,31 +17,33 @@ This is a demo full node running a simple Sovereign SDK rollup on [Celestia](htt -- [What is This?](#what-is-this) -- [Getting Started](#getting-started) - - [Run a local DA layer instance](#run-a-local-da-layer-instance) - - [Start the Rollup Full Node](#start-the-rollup-full-node) - - [Sanity Check: Creating a Token](#sanity-check-creating-a-token) - - [How to Submit Transactions](#how-to-submit-transactions) - - [1. Build `sov-cli`](#1-build-sov-cli) - - [2. Generate the Transaction](#2-generate-the-transaction) - - [3. Submit the Transaction(s)](#3-submit-the-transactions) - - [4. Verify the Token Supply](#4-verify-the-token-supply) - - [Makefile](#makefile) - - [Remote setup](#remote-setup) -- [How to Customize This Example](#how-to-customize-this-example) - - [1. Initialize the DA Service](#1-initialize-the-da-service) - - [2. Run the Main Loop](#2-run-the-main-loop) -- [Disclaimer](#disclaimer) -- [Interacting with your Node via RPC](#interacting-with-your-node-via-rpc) - - [Key Concepts](#key-concepts) - - [RPC Methods](#rpc-methods) - - [`ledger_getHead`](#ledger_gethead) - - [`ledger_getSlots`](#ledger_getslots) - - [`ledger_getBatches`](#ledger_getbatches) - - [`ledger_getTransactions`](#ledger_gettransactions) - - [`ledger_getEvents`](#ledger_getevents) -- [License](#license) +- [Demo Rollup ](#demo-rollup-) + - [Table of Contents](#table-of-contents) + - [What is This?](#what-is-this) + - [Getting Started](#getting-started) + - [Run a local DA layer instance](#run-a-local-da-layer-instance) + - [Start the Rollup Full Node](#start-the-rollup-full-node) + - [Sanity Check: Creating a Token](#sanity-check-creating-a-token) + - [How to Submit Transactions](#how-to-submit-transactions) + - [1. Build `sov-cli`](#1-build-sov-cli) + - [2. Generate the Transaction](#2-generate-the-transaction) + - [3. Submit the Transaction(s)](#3-submit-the-transactions) + - [4. Verify the Token Supply](#4-verify-the-token-supply) + - [Makefile](#makefile) + - [Remote setup](#remote-setup) + - [How to Customize This Example](#how-to-customize-this-example) + - [1. Initialize the DA Service](#1-initialize-the-da-service) + - [2. Run the Main Loop](#2-run-the-main-loop) + - [Disclaimer](#disclaimer) + - [Interacting with your Node via RPC](#interacting-with-your-node-via-rpc) + - [Key Concepts](#key-concepts) + - [RPC Methods](#rpc-methods) + - [`ledger_getHead`](#ledger_gethead) + - [`ledger_getSlots`](#ledger_getslots) + - [`ledger_getBatches`](#ledger_getbatches) + - [`ledger_getTransactions`](#ledger_gettransactions) + - [`ledger_getEvents`](#ledger_getevents) + - [License](#license) @@ -221,7 +223,7 @@ Here's an example of a JSON representing the above call: "to": "sov1zgfpyysjzgfpyysjzgfpyysjzgfpyysjzgfpyysjzgfpyysjzgfqve8h6h", "coins": { "amount": 200, - "token_address": "sov16m8fxq0x5wc5aw75fx9rus2p7g2l22zf4re72c3m058g77cdjemsavg2ft" + "token_address": "sov1zdwj8thgev2u3yyrrlekmvtsz4av4tp3m7dm5mx5peejnesga27svq9m72" } } } @@ -266,7 +268,7 @@ Adding the following transaction to batch: "to": "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94", "coins": { "amount": 200, - "token_address": "sov16m8fxq0x5wc5aw75fx9rus2p7g2l22zf4re72c3m058g77cdjemsavg2ft" + "token_address": "sov1zdwj8thgev2u3yyrrlekmvtsz4av4tp3m7dm5mx5peejnesga27svq9m72" } } } @@ -289,7 +291,7 @@ This command will use your default private key. #### 4. Verify the Token Supply ```bash -$ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"bank_supplyOf","params":["sov16m8fxq0x5wc5aw75fx9rus2p7g2l22zf4re72c3m058g77cdjemsavg2ft"],"id":1}' http://127.0.0.1:12345 +$ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"bank_supplyOf","params":["sov1zdwj8thgev2u3yyrrlekmvtsz4av4tp3m7dm5mx5peejnesga27svq9m72"],"id":1}' http://127.0.0.1:12345 {"jsonrpc":"2.0","result":{"amount":1000},"id":1} ``` @@ -323,19 +325,12 @@ $ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method" - `make stop`: - Stops the Celestia Docker image, if running. - Deletes all contents of the demo-rollup database. -- For submitting transactions, we use `make submit-txn SERIALIZED_BLOB_PATH=....` - - This makes use of `celestia-appd tx blob PayForBlobs` inside the docker container to submit the blob to the full node - - `--from ` is set to `sequencer-da-address` whose address has been updated at `examples/const-rollup-config/src/lib.rs` - - The namespace of celestia that the blob needs to be submitted to is obtained by using `sov-cli util print-namespace` which reads the namespace from `examples/const-rollup-config/src/lib.rs` - - The content of the blob is read directly from the file passed in via the command line using `SERIALIZED_BLOB_PATH` - - `BLOB_TXN_FEE` is set to `300utia` and would likely not need to be modified ### Remote setup > 🚧 This feature is under development! 🚧 -The above setup runs Celestia node locally to avoid any external network dependencies and to speed up development. The Sovereign SDK can also be configured to connect to the Celestia testnet using a Celestia light node running on your machine. -At present, the remote setup is not functional because the Celestia testnet version that our Celestia adapter supports has been sunsetted. We are collaborating with the Celestia team to update the adapter. +The above setup runs Celestia node locally to avoid any external network dependencies and to speed up development. Soon, the Sovereign SDK will also support connecting to the Celestia testnet using a Celestia light node running on your machine. ## How to Customize This Example diff --git a/examples/demo-rollup/remote_setup.md b/examples/demo-rollup/remote_setup.md deleted file mode 100644 index 633542aa5a..0000000000 --- a/examples/demo-rollup/remote_setup.md +++ /dev/null @@ -1,94 +0,0 @@ - - -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - -- [Remote setup](#remote-setup) - - [Set up Celestia](#set-up-celestia) - - [Submitting transactions](#submitting-transactions) - - [Install celestia-appd](#install-celestia-appd) - - [Create local keypair](#create-local-keypair) - - [Create bank transaction](#create-bank-transaction) - - [Submit blob to celestia](#submit-blob-to-celestia) - - [Verify the supply of the new token created](#verify-the-supply-of-the-new-token-created) - - - -## Remote setup - -This readme covers the steps necessary to setup the sovereign-sdk to work with a remote DA network which includes - -1. Running a celestia light client locally, on the same machine running the demo-rollup -2. A remote celestia network (Arabica testnet in this case) - -### Set up Celestia - -The current prototype runs against Celestia-node version `v0.7.1`. This is the version used on the `arabica` testnet -as of Mar 18, 2023. To get started, you'll need to sync a Celestia light node running on the Arabica testnet - -1. Clone the repository: `git clone https://github.com/celestiaorg/celestia-node.git`. -1. `cd celestia-node` -1. Checkout the code at v0.7.1: `git checkout tags/v0.7.1` -1. Build and install the celestia binary: `make build && make go-install` -1. Build celestia's key management tool `make cel-key` -1. Initialize the node: `celestia light init --p2p.network arabica` -1. Start the node with rpc enabled. Our default config uses port 11111: `celestia light start --core.ip https://limani.celestia-devops.dev --p2p.network arabica --gateway --rpc.port 11111`. If you want to use a different port, you can adjust the rollup's configuration in rollup_config.toml. -1. Obtain a JWT for RPC access: `celestia light auth admin --p2p.network arabica` -1. Copy the JWT and store it in the `celestia_rpc_auth_token` field of the rollup's config file (`rollup_config.toml`). Be careful to paste the entire JWT - it may wrap across several lines in your terminal. -1. Wait a few minutes for your Celestia node to sync. It needs to have synced to the rollup's configured `start_height `671431` before the demo can run properly. - -Once your Celestia node is up and running, simply `cargo +nightly run` to test out the prototype. - -### Submitting transactions - -You can use either the rest API or celestia-appd. The following instructions assume celestia-appd. -For testing, we can submit a transaction to the bank module to create a new token - -- Ensure demo-rollup is running in one window following the steps from the previous section, and that it's caught up - -### Install celestia-appd - -1. Install Go 1.20 - https://go.dev/doc/install -2. Clone the repository: `git clone https://github.com/celestiaorg/celestia-app.git`. -3. `cd celestia-app` -4. Check out tag v0.13.0 - `git checkout tags/v0.13.0` -5. `make install` - -### Create local keypair - -1. `celestia-appd keys add sequencer_keypair` (this will be the sequencer da keypair) -2. For the arabica testnet, you can get tokens from the arabica-faucet channel in the celestia discord https://discord.gg/celestiacommunity - -### Create bank transaction - -1. `cd ../../` (sovereign root) -2. `cargo build --release --bin sov-cli` -3. `./target/release/sov-cli util create-private-key .` This is the rollup private key that's used to sign rollup transactions. It's important to make the distinction between this key and the sequencer private key. -4. `ls -lahtr | grep sov1` - you should see a new json file created containing the keypair. We will refer to this in later commands as `` -5. `./target/release/sov-cli generate-transaction-from-json Bank examples/demo-stf/src/sov-cli/test_data/create_token.json 0` -6. Get the token address from the above the command (on Step 4) eg: `sov1jzvd95rjx7xpcdun2h8kyqee2z5r988h3wy4gsdn6ukc5ae04dvsrad3jj` -7. The binary serialized transaction is created at : `examples/demo-stf/src/sov-cli/test_data/create_token.dat` - -### Submit blob to celestia - -``` -$ xxd -p examples/demo-stf/src/sov-cli/test_data/create_token.dat | tr -d '\n' -01000000b0000000dd02eda4c1d40cdbb13686c58a127b82cb18d36191afd7eddd7e6eaeeee5bc82f139a4ef84f578e86f9f6c920fb32f505a1fa78d11ff4059263dd3037d44d8035b35bae2751216067eef40b8bad501bab50111e8f74dbb1d64c1a629dcf093c74400000001000b000000000000000e000000736f762d746573742d746f6b656ee803000000000000a3201954f70ad62230dc3d840a5bf767702c04869e85ab3eee0b962857ba75980000000000000000 - -$ celestia-appd tx blob PayForBlobs 736f762d74657374 01000000b000000004ee8ca2c343fe0acd2b72249c48b56351ebfb4b7eef73ddae363880b61380cc23b3ebf15375aa110d7aa84206b1f22c1885b26e980d5e03244cc588e314b004a60b594d5751dc2a326c18923eaa74b48424c0f246733c6c028d7ee16899ad944400000001000b000000000000000e000000736f762d746573742d746f6b656e8813000000000000a3201954f70ad62230dc3d840a5bf767702c04869e85ab3eee0b962857ba75980000000000000000 --from sequencer_keypair --node tcp://limani.celestia-devops.dev:26657 --chain-id=arabica-6 --fees=300utia - -``` - -- `xxd` is used to convert the serialized file into hex to post as an argument to `celestia-appd` -- `736f762d74657374` is the namespace `ROLLUP_NAMESPACE` in `examples/demo-rollup/src/main.rs` -- `01000000b000000004ee8ca2....` is the serialized binary blob in hex -- `sequencer_keypair` is the keypair created earlier and should also match the value of `SEQUENCER_DA_ADDRESS` in `examples/demo-rollup/src/main.rs` -- `celestia-appd` asks for confirmation - accept with y/Y - -### Verify the supply of the new token created - -``` -$ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"bank_supplyOf","params":["sov1jzvd95rjx7xpcdun2h8kyqee2z5r988h3wy4gsdn6ukc5ae04dvsrad3jj"],"id":1}' http://127.0.0.1:12345 -{"jsonrpc":"2.0","result":{"amount":5000},"id":1} -``` - -- params: should be the token address created in step 6 diff --git a/examples/demo-rollup/src/lib.rs b/examples/demo-rollup/src/lib.rs index 8f0a8e88f1..4bc109d1d0 100644 --- a/examples/demo-rollup/src/lib.rs +++ b/examples/demo-rollup/src/lib.rs @@ -41,13 +41,10 @@ pub fn initialize_ledger(path: impl AsRef) -> LedgerDB { pub fn get_genesis_config( sequencer_da_address: <::BlobTransaction as BlobReaderTrait>::Address, ) -> GenesisConfig { - let hex_key: HexPrivateAndAddress = serde_json::from_slice(include_bytes!( - "../../test-data/keys/token_deployer_private_key.json" - )) - .expect("Broken key data file"); - let key_and_address: PrivateKeyAndAddress = hex_key - .try_into() - .expect("Failed to parse sequencer private key and address"); + let data = std::fs::read_to_string("../test-data/keys/token_deployer_private_key.json") + .expect("Unable to read file to string"); + let key_and_address: PrivateKeyAndAddress = serde_json::from_str(&data) + .unwrap_or_else(|_| panic!("Unable to convert data {} to PrivateKeyAndAddress", &data)); assert!( key_and_address.is_matching_to_default(), "Inconsistent key data" diff --git a/examples/demo-rollup/src/rollup.rs b/examples/demo-rollup/src/rollup.rs index f9f901004d..6e731f9444 100644 --- a/examples/demo-rollup/src/rollup.rs +++ b/examples/demo-rollup/src/rollup.rs @@ -133,14 +133,10 @@ pub fn new_rollup_with_mock_da_from_config( pub fn read_tx_signer_priv_key() -> Result { let data = std::fs::read_to_string(TX_SIGNER_PRIV_KEY_PATH).context("Unable to read file")?; - let hex_key: crate::HexPrivateAndAddress = - serde_json::from_str(&data).context("JSON does not have correct format.")?; + let key_and_address: PrivateKeyAndAddress = serde_json::from_str(&data) + .unwrap_or_else(|_| panic!("Unable to convert data {} to PrivateKeyAndAddress", &data)); - let priv_key = sov_modules_api::default_signature::private_key::DefaultPrivateKey::from_hex( - &hex_key.hex_priv_key, - )?; - - Ok(priv_key) + Ok(key_and_address.private_key) } impl + Clone> Rollup { diff --git a/examples/demo-stf/src/genesis_config.rs b/examples/demo-stf/src/genesis_config.rs index 3b87f43250..e6bb36fd31 100644 --- a/examples/demo-stf/src/genesis_config.rs +++ b/examples/demo-stf/src/genesis_config.rs @@ -83,6 +83,7 @@ pub fn create_demo_genesis_config( chain_id: 1, limit_contract_code_size: None, spec: vec![(0, SpecId::LATEST)].into_iter().collect(), + ..Default::default() }, ) } diff --git a/examples/test-data/keys/token_deployer_private_key.json b/examples/test-data/keys/token_deployer_private_key.json index 0305b18629..a51524bbbf 100644 --- a/examples/test-data/keys/token_deployer_private_key.json +++ b/examples/test-data/keys/token_deployer_private_key.json @@ -1,4 +1,6 @@ { - "hex_priv_key": "75fbf8d98746c2692e502942b938c82379fd09ea9f5b60d4d39e87e1b42468fdf8ad2437a279e1c8932c07358c91dc4fe34864a98c6c25f298e2a0199c1509ff", + "private_key": { + "key_pair": [117, 251, 248, 217, 135, 70, 194, 105, 46, 80, 41, 66, 185, 56, 200, 35, 121, 253, 9, 234, 159, 91, 96, 212, 211, 158, 135, 225, 180, 36, 104, 253] + }, "address": "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqrr8r94" } \ No newline at end of file diff --git a/examples/test-data/keys/tx_signer_private_key.json b/examples/test-data/keys/tx_signer_private_key.json index b237947bc6..fd53a900a6 100644 --- a/examples/test-data/keys/tx_signer_private_key.json +++ b/examples/test-data/keys/tx_signer_private_key.json @@ -1,4 +1,6 @@ { - "hex_priv_key": "27c3774d52e71ea266a9c5256cd98b9ae67e62f2ae5ed34a668db8eaa83e1bac61fcf0f466bc20ca3882d46ae07d65227e31cfaefb852bc8f579415247565dd4", + "private_key": { + "key_pair": [39, 195, 119, 77, 82, 231, 30, 162, 102, 169, 197, 37, 108, 217, 139, 154, 230, 126, 98, 242, 174, 94, 211, 74, 102, 141, 184, 234, 168, 62, 27, 172] + }, "address": "sov1dnhqk4mdsj2kwv4xymt8a624xuahfx8906j9usdkx7ensfghndkq8p33f7" } \ No newline at end of file diff --git a/examples/test-data/requests/mint.json b/examples/test-data/requests/mint.json index 10436e602c..aeb6e92369 100644 --- a/examples/test-data/requests/mint.json +++ b/examples/test-data/requests/mint.json @@ -2,7 +2,7 @@ "Mint": { "coins": { "amount": 3000, - "token_address": "sov16m8fxq0x5wc5aw75fx9rus2p7g2l22zf4re72c3m058g77cdjemsavg2ft" + "token_address": "sov1zdwj8thgev2u3yyrrlekmvtsz4av4tp3m7dm5mx5peejnesga27svq9m72" }, "minter_address": "sov15vspj48hpttzyvxu8kzq5klhvaczcpyxn6z6k0hwpwtzs4a6wkvqwr57gc" } diff --git a/examples/test-data/requests/transfer.json b/examples/test-data/requests/transfer.json index 92411d14c3..dac1d86389 100644 --- a/examples/test-data/requests/transfer.json +++ b/examples/test-data/requests/transfer.json @@ -3,7 +3,7 @@ "to": "sov1l6n2cku82yfqld30lanm2nfw43n2auc8clw7r5u5m6s7p8jrm4zqklh0qh", "coins": { "amount": 200, - "token_address": "sov16m8fxq0x5wc5aw75fx9rus2p7g2l22zf4re72c3m058g77cdjemsavg2ft" + "token_address": "sov1zdwj8thgev2u3yyrrlekmvtsz4av4tp3m7dm5mx5peejnesga27svq9m72" } } } diff --git a/full-node/sov-stf-runner/Cargo.toml b/full-node/sov-stf-runner/Cargo.toml index c7741b38ab..a7400f4276 100644 --- a/full-node/sov-stf-runner/Cargo.toml +++ b/full-node/sov-stf-runner/Cargo.toml @@ -27,8 +27,7 @@ sov-db = { path = "../db/sov-db" } sov-rollup-interface = { path = "../../rollup-interface", version = "0.2" } sov-state = { path = "../../module-system/sov-state", version = "0.2", features = ["native"] } sov-modules-api = { path = "../../module-system/sov-modules-api", version = "0.2", features = ["native"] } -celestia = { path = "../../adapters/celestia", features = ["native"] } -sov-celestia-adapter = { path = "../../adapters/celestia", features = ["native"] } +sov-celestia-adapter = { path = "../../adapters/celestia", version = "0.2", features = ["native"] } [dev-dependencies] tempfile = { workspace = true } diff --git a/module-system/module-implementations/sov-bank/src/call.rs b/module-system/module-implementations/sov-bank/src/call.rs index 4686f2b9fb..50d27de3b4 100644 --- a/module-system/module-implementations/sov-bank/src/call.rs +++ b/module-system/module-implementations/sov-bank/src/call.rs @@ -256,6 +256,16 @@ impl Bank { .get(&token_address, working_set) .and_then(|token| token.balances.get(&user_address, working_set)) } + + /// Get the name of a token by address + pub fn get_token_name( + &self, + token_address: &C::Address, + working_set: &mut WorkingSet, + ) -> Option { + let token = self.tokens.get(token_address, working_set); + token.map(|token| token.name) + } } /// Creates a new prefix from an already existing prefix `parent_prefix` and a `token_address` diff --git a/module-system/module-implementations/sov-bank/src/lib.rs b/module-system/module-implementations/sov-bank/src/lib.rs index 6d81a201d5..63baea6d7d 100644 --- a/module-system/module-implementations/sov-bank/src/lib.rs +++ b/module-system/module-implementations/sov-bank/src/lib.rs @@ -5,7 +5,8 @@ mod genesis; #[cfg(feature = "native")] pub mod query; mod token; -mod utils; +/// Util functions for bank +pub mod utils; /// Specifies the call methods using in that module. pub use call::CallMessage; diff --git a/module-system/module-implementations/sov-bank/tests/create_token_test.rs b/module-system/module-implementations/sov-bank/tests/create_token_test.rs index b75abcf544..37ce02a81b 100644 --- a/module-system/module-implementations/sov-bank/tests/create_token_test.rs +++ b/module-system/module-implementations/sov-bank/tests/create_token_test.rs @@ -24,7 +24,7 @@ fn initial_and_deployed_token() { let token_address = get_token_address::(&token_name, sender_address.as_ref(), salt); let create_token_message = CallMessage::CreateToken:: { salt, - token_name, + token_name: token_name.clone(), initial_balance, minter_address, authorized_minters: vec![minter_address], @@ -38,6 +38,11 @@ fn initial_and_deployed_token() { let sender_balance = bank.get_balance_of(sender_address, token_address, &mut working_set); assert!(sender_balance.is_none()); + let observed_token_name = bank + .get_token_name(&token_address, &mut working_set) + .expect("Token is missing its name"); + assert_eq!(&token_name, &observed_token_name); + let minter_balance = bank.get_balance_of(minter_address, token_address, &mut working_set); assert_eq!(Some(initial_balance), minter_balance); diff --git a/module-system/module-implementations/sov-evm/Cargo.toml b/module-system/module-implementations/sov-evm/Cargo.toml index 5f945f8482..6b6e407899 100644 --- a/module-system/module-implementations/sov-evm/Cargo.toml +++ b/module-system/module-implementations/sov-evm/Cargo.toml @@ -29,6 +29,7 @@ hex = { workspace = true } jsonrpsee = { workspace = true, features = ["macros", "client-core", "server"], optional = true } tracing = { workspace = true } derive_more = { workspace = true } +lazy_static = "1.4.0" ethereum-types = "0.14.1" diff --git a/module-system/module-implementations/sov-evm/src/call.rs b/module-system/module-implementations/sov-evm/src/call.rs index e21068601b..3d135644d2 100644 --- a/module-system/module-implementations/sov-evm/src/call.rs +++ b/module-system/module-implementations/sov-evm/src/call.rs @@ -6,7 +6,7 @@ use sov_state::WorkingSet; use crate::evm::db::EvmDb; use crate::evm::executor::{self}; use crate::evm::transaction::{BlockEnv, EvmTransactionSignedEcRecovered}; -use crate::evm::{contract_address, EvmChainCfg, RawEvmTransaction}; +use crate::evm::{contract_address, EvmChainConfig, RawEvmTransaction}; use crate::experimental::SpecIdWrapper; use crate::Evm; @@ -30,7 +30,7 @@ impl Evm { ) -> Result { let evm_tx_recovered: EvmTransactionSignedEcRecovered = tx.try_into()?; - let block_env = self.block_env.get(working_set).unwrap_or_default(); + let block_env = self.pending_block.get(working_set).unwrap_or_default(); let cfg = self.cfg.get(working_set).unwrap_or_default(); let cfg_env = get_cfg_env(&block_env, cfg, None); @@ -86,7 +86,7 @@ impl Evm { /// Copies context dependent values from template_cfg or default if not provided pub(crate) fn get_cfg_env( block_env: &BlockEnv, - cfg: EvmChainCfg, + cfg: EvmChainConfig, template_cfg: Option, ) -> CfgEnv { CfgEnv { diff --git a/module-system/module-implementations/sov-evm/src/evm/conversions.rs b/module-system/module-implementations/sov-evm/src/evm/conversions.rs index acfce19886..3014c64cb1 100644 --- a/module-system/module-implementations/sov-evm/src/evm/conversions.rs +++ b/module-system/module-implementations/sov-evm/src/evm/conversions.rs @@ -49,7 +49,7 @@ impl From for ReVmBlockEnv { difficulty: U256::ZERO, prevrandao: block_env.prevrandao.map(|r| B256::from_slice(&r)), basefee: U256::from_le_bytes(block_env.basefee), - gas_limit: U256::from_le_bytes(block_env.gas_limit), + gas_limit: U256::from(block_env.gas_limit), } } } diff --git a/module-system/module-implementations/sov-evm/src/evm/mod.rs b/module-system/module-implementations/sov-evm/src/evm/mod.rs index 9e88618dd9..d11f2f6339 100644 --- a/module-system/module-implementations/sov-evm/src/evm/mod.rs +++ b/module-system/module-implementations/sov-evm/src/evm/mod.rs @@ -75,7 +75,7 @@ pub(crate) fn contract_address(result: ExecutionResult) -> Option { /// EVM Chain configuration #[derive(Debug, Clone, PartialEq, BorshSerialize, BorshDeserialize)] -pub struct EvmChainCfg { +pub struct EvmChainConfig { /// Unique chain id /// Chains can be registered at . pub chain_id: u64, @@ -86,14 +86,26 @@ pub struct EvmChainCfg { /// List of EVM hardforks by block number pub spec: Vec<(u64, SpecIdWrapper)>, + + /// Coinbase where all the fees go + pub coinbase: EthAddress, + + /// Gas limit for single block + pub block_gas_limit: u64, + + /// Delta to add to parent block timestamp + pub block_timestamp_delta: u64, } -impl Default for EvmChainCfg { - fn default() -> EvmChainCfg { - EvmChainCfg { +impl Default for EvmChainConfig { + fn default() -> EvmChainConfig { + EvmChainConfig { chain_id: 1, limit_contract_code_size: None, spec: vec![(0, SpecIdWrapper::from(SpecId::LATEST))], + coinbase: [0u8; 20], + block_gas_limit: reth_primitives::constants::ETHEREUM_BLOCK_GAS_LIMIT, + block_timestamp_delta: 1, } } } diff --git a/module-system/module-implementations/sov-evm/src/evm/tests.rs b/module-system/module-implementations/sov-evm/src/evm/tests.rs index 24b93dd311..f2cd20d336 100644 --- a/module-system/module-implementations/sov-evm/src/evm/tests.rs +++ b/module-system/module-implementations/sov-evm/src/evm/tests.rs @@ -67,8 +67,11 @@ fn simple_contract_execution + DatabaseCommit + .unwrap(); let tx = &tx.try_into().unwrap(); - let result = - executor::execute_tx(&mut evm_db, BlockEnv::default(), tx, CfgEnv::default()).unwrap(); + let block_env = BlockEnv { + gas_limit: reth_primitives::constants::ETHEREUM_BLOCK_GAS_LIMIT, + ..Default::default() + }; + let result = executor::execute_tx(&mut evm_db, block_env, tx, CfgEnv::default()).unwrap(); contract_address(result).expect("Expected successful contract creation") }; diff --git a/module-system/module-implementations/sov-evm/src/evm/transaction.rs b/module-system/module-implementations/sov-evm/src/evm/transaction.rs index 099e902ae5..977b1e9add 100644 --- a/module-system/module-implementations/sov-evm/src/evm/transaction.rs +++ b/module-system/module-implementations/sov-evm/src/evm/transaction.rs @@ -13,7 +13,7 @@ pub(crate) struct BlockEnv { pub(crate) prevrandao: Option, /// basefee is added in EIP1559 London upgrade pub(crate) basefee: Bytes32, - pub(crate) gas_limit: Bytes32, + pub(crate) gas_limit: u64, } impl Default for BlockEnv { @@ -24,7 +24,7 @@ impl Default for BlockEnv { timestamp: Default::default(), prevrandao: Some(Default::default()), basefee: Default::default(), - gas_limit: [u8::MAX; 32], + gas_limit: reth_primitives::constants::ETHEREUM_BLOCK_GAS_LIMIT, } } } diff --git a/module-system/module-implementations/sov-evm/src/genesis.rs b/module-system/module-implementations/sov-evm/src/genesis.rs index dd6582f1b3..cccaf06bd7 100644 --- a/module-system/module-implementations/sov-evm/src/genesis.rs +++ b/module-system/module-implementations/sov-evm/src/genesis.rs @@ -1,9 +1,12 @@ use anyhow::Result; +use reth_primitives::constants::{EMPTY_RECEIPTS, EMPTY_TRANSACTIONS}; +use reth_primitives::{Bloom, Bytes, EMPTY_OMMER_ROOT, H256, KECCAK_EMPTY, U256}; +use reth_rpc_types::{Block, BlockTransactions, Header}; use revm::primitives::SpecId; use sov_state::WorkingSet; use crate::evm::db_init::InitEvmDb; -use crate::evm::{AccountInfo, EvmChainCfg}; +use crate::evm::{AccountInfo, EvmChainConfig}; use crate::experimental::SpecIdWrapper; use crate::Evm; @@ -41,14 +44,55 @@ impl Evm { panic!("EVM spec must start from block 0"); } - let chain_cfg = EvmChainCfg { + let chain_cfg = EvmChainConfig { chain_id: config.chain_id, limit_contract_code_size: config.limit_contract_code_size, spec, + coinbase: config.coinbase, + block_gas_limit: config.block_gas_limit, + block_timestamp_delta: config.block_timestamp_delta, }; self.cfg.set(&chain_cfg, working_set); + let genesis_block_number = 0u64; + self.head_number.set(&genesis_block_number, working_set); + + let header = reth_primitives::Header { + parent_hash: H256::default(), + ommers_hash: EMPTY_OMMER_ROOT, + beneficiary: config.coinbase.into(), + state_root: KECCAK_EMPTY, // TODO: Can we get state from working set now? + transactions_root: EMPTY_TRANSACTIONS, + receipts_root: EMPTY_RECEIPTS, + withdrawals_root: None, + logs_bloom: Bloom::default(), + difficulty: U256::ZERO, + number: 0, + gas_limit: config.block_gas_limit, + gas_used: 0, + timestamp: config.genesis_timestamp, + mix_hash: H256::default(), + nonce: 0, + base_fee_per_gas: Some(config.starting_base_fee), + extra_data: Bytes::default(), + }; + + let header = header.seal_slow(); + + self.blocks.set( + &genesis_block_number, + &Block { + header: Header::from_primitive_with_hash(header), + total_difficulty: None, + uncles: vec![], + transactions: BlockTransactions::Hashes(vec![]), + size: None, + withdrawals: None, + }, + &mut working_set.accessory_state(), + ); + Ok(()) } } diff --git a/module-system/module-implementations/sov-evm/src/hooks.rs b/module-system/module-implementations/sov-evm/src/hooks.rs index d181bcbad8..a4753fb06a 100644 --- a/module-system/module-implementations/sov-evm/src/hooks.rs +++ b/module-system/module-implementations/sov-evm/src/hooks.rs @@ -1,14 +1,49 @@ +use reth_primitives::U256; use reth_rpc_types::Transaction; use sov_state::WorkingSet; +use crate::evm::transaction::BlockEnv; use crate::Evm; impl Evm { pub fn begin_slot_hook( &self, - _da_root_hash: [u8; 32], - _working_set: &mut WorkingSet, + da_root_hash: [u8; 32], + working_set: &mut WorkingSet, ) { + let block_number: u64 = self.head_number.get(working_set).unwrap(); + let parent_block: reth_rpc_types::Block = self + .blocks + .get(&block_number, &mut working_set.accessory_state()) + .expect("Head block should always be set"); + let cfg = self.cfg.get(working_set).unwrap_or_default(); + let new_pending_block = BlockEnv { + number: block_number + 1, + coinbase: cfg.coinbase, + + // TODO: simplify this conversion by doing something with Bytes32 + // TODO simplify conversion fro U256 to u64 + // Reth rpc types keep stuff as U256, even when actually only u64 makes sense: + // block_number, timespamp, gas_used, base_fee_per_gas + timestamp: (parent_block.header.timestamp + U256::from(cfg.block_timestamp_delta)) + .to_le_bytes(), + prevrandao: Some(da_root_hash), + basefee: { + let base_fee = reth_primitives::basefee::calculate_next_block_base_fee( + parent_block.header.gas_used.try_into().unwrap(), + cfg.block_gas_limit, + parent_block + .header + .base_fee_per_gas + .unwrap_or(reth_primitives::constants::MIN_PROTOCOL_BASE_FEE_U256) + .as_limbs()[0], + ); + + U256::from_limbs([base_fee, 0u64, 0u64, 0u64]).to_le_bytes() + }, + gas_limit: cfg.block_gas_limit, + }; + self.pending_block.set(&new_pending_block, working_set); } pub fn end_slot_hook(&self, _root_hash: [u8; 32], working_set: &mut WorkingSet) { diff --git a/module-system/module-implementations/sov-evm/src/lib.rs b/module-system/module-implementations/sov-evm/src/lib.rs index 25dc8fc345..660857b0e4 100644 --- a/module-system/module-implementations/sov-evm/src/lib.rs +++ b/module-system/module-implementations/sov-evm/src/lib.rs @@ -32,7 +32,7 @@ mod experimental { use super::evm::db::EvmDb; use super::evm::transaction::BlockEnv; use super::evm::{DbAccount, EthAddress}; - use crate::evm::{Bytes32, EvmChainCfg}; + use crate::evm::{Bytes32, EvmChainConfig}; #[derive(Clone, Debug)] pub struct AccountData { pub address: EthAddress, @@ -58,6 +58,11 @@ mod experimental { pub chain_id: u64, pub limit_contract_code_size: Option, pub spec: HashMap, + pub coinbase: EthAddress, + pub starting_base_fee: u64, + pub block_gas_limit: u64, + pub genesis_timestamp: u64, + pub block_timestamp_delta: u64, } impl Default for EvmConfig { @@ -67,6 +72,11 @@ mod experimental { chain_id: 1, limit_contract_code_size: None, spec: vec![(0, SpecId::LATEST)].into_iter().collect(), + coinbase: [0u8; 20], + starting_base_fee: reth_primitives::constants::MIN_PROTOCOL_BASE_FEE, + block_gas_limit: reth_primitives::constants::ETHEREUM_BLOCK_GAS_LIMIT, + block_timestamp_delta: reth_primitives::constants::SLOT_DURATION.as_secs(), + genesis_timestamp: 0, } } } @@ -82,10 +92,10 @@ mod experimental { pub(crate) accounts: sov_state::StateMap, #[state] - pub(crate) cfg: sov_state::StateValue, + pub(crate) cfg: sov_state::StateValue, #[state] - pub(crate) block_env: sov_state::StateValue, + pub(crate) pending_block: sov_state::StateValue, #[state] pub(crate) head_number: sov_state::StateValue, @@ -94,9 +104,6 @@ mod experimental { // binary serialization formats. // 1. Implement custom types for Block, Transaction etc.. with borsh derived. // 2. Remove JsonCodec. - #[state] - pub(crate) current_block: sov_state::StateValue, - #[state] pub(crate) blocks: sov_state::AccessoryStateMap, diff --git a/module-system/module-implementations/sov-evm/src/query.rs b/module-system/module-implementations/sov-evm/src/query.rs index b09625ea79..05cef734a0 100644 --- a/module-system/module-implementations/sov-evm/src/query.rs +++ b/module-system/module-implementations/sov-evm/src/query.rs @@ -138,7 +138,7 @@ impl Evm { info!("evm module: eth_call"); let tx_env = prepare_call_env(request); - let block_env = self.block_env.get(working_set).unwrap_or_default(); + let block_env = self.pending_block.get(working_set).unwrap_or_default(); let cfg = self.cfg.get(working_set).unwrap_or_default(); let cfg_env = get_cfg_env(&block_env, cfg, Some(Self::CALL_CFG_ENV_TEMPLATE)); diff --git a/module-system/module-implementations/sov-evm/src/tests/cfg_tests.rs b/module-system/module-implementations/sov-evm/src/tests/cfg_tests.rs index ae9e52826a..d4046e7f92 100644 --- a/module-system/module-implementations/sov-evm/src/tests/cfg_tests.rs +++ b/module-system/module-implementations/sov-evm/src/tests/cfg_tests.rs @@ -2,7 +2,7 @@ use revm::primitives::{CfgEnv, SpecId, U256}; use crate::call::{get_cfg_env, get_spec_id}; use crate::evm::transaction::BlockEnv; -use crate::evm::EvmChainCfg; +use crate::evm::EvmChainConfig; use crate::SpecIdWrapper; #[test] @@ -12,12 +12,12 @@ fn cfg_test() { ..Default::default() }; - let cfg = EvmChainCfg { - chain_id: 1, + let cfg = EvmChainConfig { limit_contract_code_size: Some(100), spec: vec![(0, SpecIdWrapper::new(SpecId::SHANGHAI))] .into_iter() .collect(), + ..Default::default() }; let template_cfg = CfgEnv { diff --git a/module-system/module-implementations/sov-evm/src/tests/dev_signer.rs b/module-system/module-implementations/sov-evm/src/tests/dev_signer.rs index c7e83c3597..246556e93c 100644 --- a/module-system/module-implementations/sov-evm/src/tests/dev_signer.rs +++ b/module-system/module-implementations/sov-evm/src/tests/dev_signer.rs @@ -66,7 +66,7 @@ impl DevSigner { input: RethBytes::from(data), nonce, chain_id: 1, - gas_limit: u64::MAX, + gas_limit: reth_primitives::constants::ETHEREUM_BLOCK_GAS_LIMIT / 2, ..Default::default() }; diff --git a/module-system/module-implementations/sov-evm/src/tests/genesis_tests.rs b/module-system/module-implementations/sov-evm/src/tests/genesis_tests.rs new file mode 100644 index 0000000000..726060b25b --- /dev/null +++ b/module-system/module-implementations/sov-evm/src/tests/genesis_tests.rs @@ -0,0 +1,128 @@ +use ethereum_types::H64; +use lazy_static::lazy_static; +use reth_primitives::constants::{EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, ETHEREUM_BLOCK_GAS_LIMIT}; +use reth_primitives::hex_literal::hex; +use reth_primitives::{Address, Bloom, Bytes, EMPTY_OMMER_ROOT, H256}; +use reth_rpc_types::{Block, BlockTransactions, Header}; +use revm::primitives::{SpecId, KECCAK_EMPTY, U256}; +use sov_modules_api::default_context::DefaultContext; +use sov_modules_api::Module; +use sov_state::{DefaultStorageSpec, ProverStorage, WorkingSet}; + +// use crate::evm::db; +use crate::{evm::EvmChainConfig, AccountData, Evm, EvmConfig, SpecIdWrapper}; +type C = DefaultContext; + +lazy_static! { + pub(crate) static ref TEST_CONFIG: EvmConfig = EvmConfig { + data: vec![AccountData { + address: [1u8; 20], + balance: U256::from(1000000000).to_le_bytes(), + code_hash: KECCAK_EMPTY.to_fixed_bytes(), + code: vec![], + nonce: 0, + }], + spec: vec![(0, SpecId::BERLIN), (1, SpecId::LATEST)] + .into_iter() + .collect(), + chain_id: 1000, + block_gas_limit: reth_primitives::constants::ETHEREUM_BLOCK_GAS_LIMIT, + block_timestamp_delta: 2, + genesis_timestamp: 50, + coinbase: [3u8; 20], + limit_contract_code_size: Some(5000), + starting_base_fee: 70, + }; +} + +#[test] +fn genesis_data() { + get_evm(&TEST_CONFIG); + + // TODO: assert on account being the same - easier after unifying stored types! + // let db_account = evm.accounts.get(&address, working_set).unwrap(); +} + +#[test] +fn genesis_cfg() { + let (evm, mut working_set) = get_evm(&TEST_CONFIG); + + let cfg = evm.cfg.get(&mut working_set).unwrap(); + assert_eq!( + cfg, + EvmChainConfig { + spec: vec![ + (0, SpecIdWrapper(SpecId::BERLIN)), + (1, SpecIdWrapper(SpecId::LATEST)) + ], + chain_id: 1000, + block_gas_limit: reth_primitives::constants::ETHEREUM_BLOCK_GAS_LIMIT, + block_timestamp_delta: 2, + coinbase: [3u8; 20], + limit_contract_code_size: Some(5000) + } + ); +} + +#[test] +#[should_panic(expected = "EVM spec must start from block 0")] +fn genesis_cfg_missing_specs() { + get_evm(&EvmConfig { + spec: vec![(5, SpecId::BERLIN)].into_iter().collect(), + ..Default::default() + }); +} + +#[test] +fn genesis_block() { + let (evm, mut working_set) = get_evm(&TEST_CONFIG); + + let block = evm + .blocks + .get(&0u64, &mut working_set.accessory_state()) + .unwrap(); + + assert_eq!( + block, + Block { + header: Header { + parent_hash: H256::default(), + state_root: KECCAK_EMPTY, + transactions_root: EMPTY_TRANSACTIONS, + receipts_root: EMPTY_RECEIPTS, + logs_bloom: Bloom::default(), + difficulty: U256::ZERO, + number: Some(U256::ZERO), + gas_limit: U256::from(ETHEREUM_BLOCK_GAS_LIMIT), + gas_used: U256::ZERO, + timestamp: U256::from(50), + extra_data: Bytes::default(), + mix_hash: H256::default(), + nonce: Some(H64::default()), + base_fee_per_gas: Some(U256::from(70)), + hash: Some(H256(hex!( + "d57423e4375c45bc114cd137146aab671dbd3f6304f05b31bdd416301b4a99f0" + ))), + uncles_hash: EMPTY_OMMER_ROOT, + miner: Address::from([3u8; 20]), + withdrawals_root: None + }, + transactions: BlockTransactions::Hashes(vec![]), + total_difficulty: None, + uncles: vec![], + size: None, + withdrawals: None + } + ); +} + +pub(crate) fn get_evm( + config: &EvmConfig, +) -> (Evm, WorkingSet>) { + let tmpdir = tempfile::tempdir().unwrap(); + let mut working_set = WorkingSet::new(ProverStorage::with_path(tmpdir.path()).unwrap()); + let evm = Evm::::default(); + evm.genesis(config, &mut working_set).unwrap(); + + (evm, working_set) +} diff --git a/module-system/module-implementations/sov-evm/src/tests/hooks_tests.rs b/module-system/module-implementations/sov-evm/src/tests/hooks_tests.rs new file mode 100644 index 0000000000..793fe42c33 --- /dev/null +++ b/module-system/module-implementations/sov-evm/src/tests/hooks_tests.rs @@ -0,0 +1,28 @@ +use super::genesis_tests::{get_evm, TEST_CONFIG}; +use crate::evm::transaction::BlockEnv; + +#[test] +fn begin_slot_hook_creates_pending_block() { + let (evm, mut working_set) = get_evm(&TEST_CONFIG); + evm.begin_slot_hook([5u8; 32], &mut working_set); + let pending_block = evm.pending_block.get(&mut working_set).unwrap(); + assert_eq!( + pending_block, + BlockEnv { + number: 1, + coinbase: [3u8; 20], + timestamp: { + let mut a = [0u8; 32]; + a[0] = 52; + a + }, + prevrandao: Some([5u8; 32]), + basefee: { + let mut a = [0u8; 32]; + a[0] = 62; + a + }, + gas_limit: 30000000, + } + ); +} diff --git a/module-system/module-implementations/sov-evm/src/tests/mod.rs b/module-system/module-implementations/sov-evm/src/tests/mod.rs index d5b55080e8..778478f9f9 100644 --- a/module-system/module-implementations/sov-evm/src/tests/mod.rs +++ b/module-system/module-implementations/sov-evm/src/tests/mod.rs @@ -1,4 +1,6 @@ mod call_tests; mod cfg_tests; pub(crate) mod dev_signer; +mod genesis_tests; +mod hooks_tests; mod tx_tests; diff --git a/module-system/sov-cli/src/workflows/keys.rs b/module-system/sov-cli/src/workflows/keys.rs index cc5b676296..801f555037 100644 --- a/module-system/sov-cli/src/workflows/keys.rs +++ b/module-system/sov-cli/src/workflows/keys.rs @@ -123,15 +123,16 @@ pub fn generate_and_save_key( wallet_state: &mut WalletState, ) -> Result<(), anyhow::Error> { let keys = ::PrivateKey::generate(); - let public_key = keys.pub_key(); - let address = keys.pub_key().to_address::<::Address>(); + let key_and_address = PrivateKeyAndAddress::::from_key(keys); + let public_key = key_and_address.private_key.pub_key(); + let address = key_and_address.address.clone(); let key_path = app_dir.as_ref().join(format!("{}.json", address)); println!( "Generated key pair with address: {}. Saving to {}", address, key_path.display() ); - std::fs::write(&key_path, serde_json::to_string(&keys)?)?; + std::fs::write(&key_path, serde_json::to_string(&key_and_address)?)?; wallet_state .addresses .add(address, nickname, public_key, key_path); diff --git a/module-system/sov-modules-macros/tests/custom_codec_must_be_used.rs b/module-system/sov-modules-macros/tests/custom_codec_must_be_used.rs index 1dbfb88963..ef5b6c406d 100644 --- a/module-system/sov-modules-macros/tests/custom_codec_must_be_used.rs +++ b/module-system/sov-modules-macros/tests/custom_codec_must_be_used.rs @@ -2,7 +2,7 @@ use std::panic::catch_unwind; use sov_modules_api::default_context::DefaultContext; use sov_modules_api::{Context, ModuleInfo}; -use sov_state::codec::StateValueCodec; +use sov_state::codec::{StateCodec, StateKeyCodec, StateValueCodec}; use sov_state::{DefaultStorageSpec, ProverStorage, StateValue, WorkingSet}; #[derive(ModuleInfo)] @@ -26,6 +26,23 @@ impl CustomCodec { } } +impl StateCodec for CustomCodec { + type KeyCodec = Self; + type ValueCodec = Self; + fn key_codec(&self) -> &Self::KeyCodec { + self + } + fn value_codec(&self) -> &Self::ValueCodec { + self + } +} + +impl StateKeyCodec for CustomCodec { + fn encode_key(&self, _key: &K) -> Vec { + unimplemented!() + } +} + impl StateValueCodec for CustomCodec { type Error = String; diff --git a/module-system/sov-state/src/codec/bcs_codec.rs b/module-system/sov-state/src/codec/bcs_codec.rs index 9d641fbed0..0635477a37 100644 --- a/module-system/sov-state/src/codec/bcs_codec.rs +++ b/module-system/sov-state/src/codec/bcs_codec.rs @@ -1,9 +1,19 @@ +use super::{StateCodec, StateKeyCodec}; use crate::codec::StateValueCodec; /// A [`StateValueCodec`] that uses [`bcs`] for all keys and values. -#[derive(Debug, Default, PartialEq, Eq, Clone)] +#[derive(Debug, Default, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] pub struct BcsCodec; +impl StateKeyCodec for BcsCodec +where + K: serde::Serialize, +{ + fn encode_key(&self, key: &K) -> Vec { + bcs::to_bytes(key).expect("Failed to serialize key") + } +} + impl StateValueCodec for BcsCodec where V: serde::Serialize + for<'a> serde::Deserialize<'a>, @@ -11,10 +21,22 @@ where type Error = bcs::Error; fn encode_value(&self, value: &V) -> Vec { - bcs::to_bytes(value).expect("Failed to serialize key") + bcs::to_bytes(value).expect("Failed to serialize value") } fn try_decode_value(&self, bytes: &[u8]) -> Result { bcs::from_bytes(bytes) } } + +impl StateCodec for BcsCodec { + type KeyCodec = Self; + type ValueCodec = Self; + fn key_codec(&self) -> &Self::KeyCodec { + self + } + + fn value_codec(&self) -> &Self::ValueCodec { + self + } +} diff --git a/module-system/sov-state/src/codec/borsh_codec.rs b/module-system/sov-state/src/codec/borsh_codec.rs index 4647755dea..92f59ca57a 100644 --- a/module-system/sov-state/src/codec/borsh_codec.rs +++ b/module-system/sov-state/src/codec/borsh_codec.rs @@ -1,9 +1,19 @@ +use super::{StateCodec, StateKeyCodec}; use crate::codec::StateValueCodec; /// A [`StateValueCodec`] that uses [`borsh`] for all values. #[derive(Debug, Default, PartialEq, Eq, Clone, borsh::BorshDeserialize, borsh::BorshSerialize)] pub struct BorshCodec; +impl StateKeyCodec for BorshCodec +where + K: borsh::BorshSerialize + borsh::BorshDeserialize, +{ + fn encode_key(&self, value: &K) -> Vec { + value.try_to_vec().expect("Failed to serialize value") + } +} + impl StateValueCodec for BorshCodec where V: borsh::BorshSerialize + borsh::BorshDeserialize, @@ -18,3 +28,15 @@ where V::try_from_slice(bytes) } } + +impl StateCodec for BorshCodec { + type KeyCodec = Self; + type ValueCodec = Self; + fn key_codec(&self) -> &Self::KeyCodec { + self + } + + fn value_codec(&self) -> &Self::ValueCodec { + self + } +} diff --git a/module-system/sov-state/src/codec/json_codec.rs b/module-system/sov-state/src/codec/json_codec.rs index 99b301bf8f..c7d5ab35d8 100644 --- a/module-system/sov-state/src/codec/json_codec.rs +++ b/module-system/sov-state/src/codec/json_codec.rs @@ -1,11 +1,21 @@ use serde_json; +use super::{StateCodec, StateKeyCodec}; use crate::codec::StateValueCodec; /// A [`StateValueCodec`] that uses [`serde_json`] for all values. -#[derive(Debug, Default, PartialEq, Eq, Clone)] +#[derive(Debug, Default, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] pub struct JsonCodec; +impl StateKeyCodec for JsonCodec +where + K: serde::Serialize, +{ + fn encode_key(&self, key: &K) -> Vec { + serde_json::to_vec(key).expect("Failed to serialize value") + } +} + impl StateValueCodec for JsonCodec where V: serde::Serialize + for<'a> serde::Deserialize<'a>, @@ -20,3 +30,16 @@ where serde_json::from_slice(bytes) } } + +impl StateCodec for JsonCodec { + type KeyCodec = Self; + type ValueCodec = Self; + + fn key_codec(&self) -> &Self::KeyCodec { + self + } + + fn value_codec(&self) -> &Self::ValueCodec { + self + } +} diff --git a/module-system/sov-state/src/codec/mod.rs b/module-system/sov-state/src/codec/mod.rs index ad86a43a30..0a350b32e0 100644 --- a/module-system/sov-state/src/codec/mod.rs +++ b/module-system/sov-state/src/codec/mod.rs @@ -3,8 +3,10 @@ mod bcs_codec; mod borsh_codec; mod json_codec; +mod split_codec; pub use bcs_codec::BcsCodec; +use borsh::BorshSerialize; pub use borsh_codec::BorshCodec; pub use json_codec::JsonCodec; @@ -42,3 +44,66 @@ pub trait StateValueCodec { .unwrap() } } + +/// A trait for types that can serialize keys for storage +/// access. +pub trait StateKeyCodec { + fn encode_key(&self, key: &K) -> Vec; +} + +/// A trait for types that can serialize keys and values, as well +/// as deserializing values for storage access. +pub trait StateCodec { + /// The codec used to serialize keys + type KeyCodec; + /// The codec used to serialize and deserialize values + type ValueCodec; + + /// Returns a reference to the type's key codec + fn key_codec(&self) -> &Self::KeyCodec; + /// Returns a reference to the type's value codec + fn value_codec(&self) -> &Self::ValueCodec; +} + +/// A trait for codecs which know how to serialize a type `Ref` as if it were +/// some other type `Target`. +/// +/// A good example of this is [`BorshCodec`], which knows how to serialize a +/// `[T;N]` as if it were a `Vec` even though the two types have different +/// encodings by default. +pub trait EncodeKeyLike { + /// Encodes a reference to `Ref` as if it were a reference to `Target`. + fn encode_key_like(&self, borrowed: &Ref) -> Vec; +} + +// All items can be encoded like themselves by all codecs +impl EncodeKeyLike for C +where + C: StateKeyCodec, +{ + fn encode_key_like(&self, borrowed: &T) -> Vec { + self.encode_key(borrowed) + } +} + +// In borsh, a slice is encoded the same way as a vector except in edge case where +// T is zero-sized, in which case Vec is not borsh encodable. +impl EncodeKeyLike<[T], Vec> for BorshCodec +where + T: BorshSerialize, +{ + fn encode_key_like(&self, borrowed: &[T]) -> Vec { + borrowed.try_to_vec().unwrap() + } +} + +#[test] +fn test_borsh_slice_encode_alike() { + let codec = BorshCodec; + let slice = [1, 2, 3]; + let vec = vec![1, 2, 3]; + assert_eq!( + >>::encode_key_like(&codec, &slice), + codec.encode_value(&vec) + ); +} diff --git a/module-system/sov-state/src/codec/split_codec.rs b/module-system/sov-state/src/codec/split_codec.rs new file mode 100644 index 0000000000..47d409c96a --- /dev/null +++ b/module-system/sov-state/src/codec/split_codec.rs @@ -0,0 +1,46 @@ +//! This module defines a codec which delegates to one codec for keys and one codec for values. + +use super::{StateCodec, StateKeyCodec, StateValueCodec}; + +/// A [`StateValueCodec`] that uses one pre-existing codec for keys and a different one values. +#[derive(Debug, Default, PartialEq, Eq, Clone)] +pub struct SplitCodec { + pub key_codec: KC, + pub value_codec: VC, +} + +impl StateKeyCodec for SplitCodec +where + KC: StateKeyCodec, +{ + fn encode_key(&self, key: &K) -> Vec { + self.key_codec.encode_key(key) + } +} + +impl StateValueCodec for SplitCodec +where + VC: StateValueCodec, +{ + type Error = VC::Error; + + fn encode_value(&self, value: &V) -> Vec { + self.value_codec.encode_value(value) + } + + fn try_decode_value(&self, bytes: &[u8]) -> Result { + self.value_codec.try_decode_value(bytes) + } +} + +impl StateCodec for SplitCodec { + type KeyCodec = KC; + type ValueCodec = VC; + fn key_codec(&self) -> &Self::KeyCodec { + &self.key_codec + } + + fn value_codec(&self) -> &Self::ValueCodec { + &self.value_codec + } +} diff --git a/module-system/sov-state/src/containers/accessory_map.rs b/module-system/sov-state/src/containers/accessory_map.rs index 0f13fb023f..e148ce651e 100644 --- a/module-system/sov-state/src/containers/accessory_map.rs +++ b/module-system/sov-state/src/containers/accessory_map.rs @@ -1,9 +1,7 @@ -use std::borrow::Borrow; -use std::hash::Hash; use std::marker::PhantomData; use super::StateMapError; -use crate::codec::{BorshCodec, StateValueCodec}; +use crate::codec::{BorshCodec, EncodeKeyLike, StateCodec, StateKeyCodec, StateValueCodec}; use crate::storage::StorageKey; use crate::{AccessoryWorkingSet, Prefix, StateReaderAndWriter, Storage}; @@ -14,7 +12,7 @@ use crate::{AccessoryWorkingSet, Prefix, StateReaderAndWriter, Storage}; /// [`AccessoryStateMap`] is generic over: /// - a key type `K`; /// - a value type `V`; -/// - a [`StateValueCodec`] `VC`. +/// - a [`StateValueCodec`] `Codec`. #[derive( Debug, Clone, @@ -24,9 +22,9 @@ use crate::{AccessoryWorkingSet, Prefix, StateReaderAndWriter, Storage}; serde::Serialize, serde::Deserialize, )] -pub struct AccessoryStateMap { +pub struct AccessoryStateMap { _phantom: (PhantomData, PhantomData), - value_codec: VC, + codec: Codec, prefix: Prefix, } @@ -38,12 +36,12 @@ impl AccessoryStateMap { } } -impl AccessoryStateMap { +impl AccessoryStateMap { /// Creates a new [`AccessoryStateMap`] with the given prefix and [`StateValueCodec`]. - pub fn with_codec(prefix: Prefix, codec: VC) -> Self { + pub fn with_codec(prefix: Prefix, codec: Codec) -> Self { Self { _phantom: (PhantomData, PhantomData), - value_codec: codec, + codec, prefix, } } @@ -54,10 +52,11 @@ impl AccessoryStateMap { } } -impl AccessoryStateMap +impl AccessoryStateMap where - K: Hash + Eq, - VC: StateValueCodec, + Codec: StateCodec, + Codec::KeyCodec: StateKeyCodec, + Codec::ValueCodec: StateValueCodec, { /// Inserts a key-value pair into the map. /// @@ -65,10 +64,10 @@ where /// map’s key type. pub fn set(&self, key: &Q, value: &V, working_set: &mut AccessoryWorkingSet) where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec::KeyCodec: EncodeKeyLike, + Q: ?Sized, { - working_set.set_value(self.prefix(), key, value, &self.value_codec) + working_set.set_value(self.prefix(), key, value, &self.codec) } /// Returns the value corresponding to the key, or [`None`] if the map @@ -76,9 +75,8 @@ where /// /// # Examples /// - /// The key may be any borrowed form of the map’s key type. Note that - /// [`Hash`] and [`Eq`] on the borrowed form must match those for the key - /// type. + /// The key may be any item that implements [`EncodeKeyLike`] the map's key type + /// using your chosen codec. /// /// ``` /// use sov_state::{AccessoryStateMap, Storage, AccessoryWorkingSet}; @@ -93,7 +91,7 @@ where /// } /// ``` /// - /// If the map's key type does not implement [`Borrow`] for your desired + /// If the map's key type does not implement [`EncodeKeyLike`] for your desired /// target type, you'll have to convert the key to something else. An /// example of this would be "slicing" an array to use in [`Vec`]-keyed /// maps: @@ -110,10 +108,10 @@ where /// ``` pub fn get(&self, key: &Q, working_set: &mut AccessoryWorkingSet) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec::KeyCodec: EncodeKeyLike, + Q: ?Sized, { - working_set.get_value(self.prefix(), key, &self.value_codec) + working_set.get_value(self.prefix(), key, &self.codec) } /// Returns the value corresponding to the key or [`StateMapError`] if key is absent in @@ -124,11 +122,14 @@ where working_set: &mut AccessoryWorkingSet, ) -> Result where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec::KeyCodec: EncodeKeyLike, + Q: ?Sized, { self.get(key, working_set).ok_or_else(|| { - StateMapError::MissingValue(self.prefix().clone(), StorageKey::new(self.prefix(), key)) + StateMapError::MissingValue( + self.prefix().clone(), + StorageKey::new(self.prefix(), key, self.codec.key_codec()), + ) }) } @@ -140,10 +141,10 @@ where working_set: &mut AccessoryWorkingSet, ) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec::KeyCodec: EncodeKeyLike, + Q: ?Sized, { - working_set.remove_value(self.prefix(), key, &self.value_codec) + working_set.remove_value(self.prefix(), key, &self.codec) } /// Removes a key from the map, returning the corresponding value (or @@ -156,11 +157,14 @@ where working_set: &mut AccessoryWorkingSet, ) -> Result where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec::KeyCodec: EncodeKeyLike, + Q: ?Sized, { self.remove(key, working_set).ok_or_else(|| { - StateMapError::MissingValue(self.prefix().clone(), StorageKey::new(self.prefix(), key)) + StateMapError::MissingValue( + self.prefix().clone(), + StorageKey::new(self.prefix(), key, self.codec.key_codec()), + ) }) } @@ -170,19 +174,21 @@ where /// return the value beforing deletion. pub fn delete(&self, key: &Q, working_set: &mut AccessoryWorkingSet) where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec::KeyCodec: EncodeKeyLike, + Q: ?Sized, { - working_set.delete_value(self.prefix(), key); + working_set.delete_value(self.prefix(), key, &self.codec); } } #[cfg(feature = "arbitrary")] -impl<'a, K, V, VC> AccessoryStateMap +impl<'a, K, V, Codec> AccessoryStateMap where - K: arbitrary::Arbitrary<'a> + Hash + Eq, - V: arbitrary::Arbitrary<'a> + Hash + Eq, - VC: StateValueCodec + Default, + K: arbitrary::Arbitrary<'a>, + V: arbitrary::Arbitrary<'a>, + Codec: StateCodec + Default, + Codec::KeyCodec: StateKeyCodec, + Codec::ValueCodec: StateValueCodec, { pub fn arbitrary_workset( u: &mut arbitrary::Unstructured<'a>, @@ -195,8 +201,8 @@ where let prefix = Prefix::arbitrary(u)?; let len = u.arbitrary_len::<(K, V)>()?; - let codec = VC::default(); - let map = AccessoryStateMap::with_codec(prefix, codec); + let codec = Codec::default(); + let map = Self::with_codec(prefix, codec); (0..len).try_fold(map, |map, _| { let key = K::arbitrary(u)?; diff --git a/module-system/sov-state/src/containers/accessory_value.rs b/module-system/sov-state/src/containers/accessory_value.rs index 2c95a82007..dda3641e8c 100644 --- a/module-system/sov-state/src/containers/accessory_value.rs +++ b/module-system/sov-state/src/containers/accessory_value.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use borsh::{BorshDeserialize, BorshSerialize}; use thiserror::Error; -use crate::codec::{BorshCodec, StateValueCodec}; +use crate::codec::{BorshCodec, StateCodec, StateValueCodec}; use crate::{AccessoryWorkingSet, Prefix, StateReaderAndWriter, Storage}; /// Container for a single value stored as "accessory" state, outside of the @@ -18,9 +18,9 @@ use crate::{AccessoryWorkingSet, Prefix, StateReaderAndWriter, Storage}; serde::Serialize, serde::Deserialize, )] -pub struct AccessoryStateValue { +pub struct AccessoryStateValue { _phantom: PhantomData, - codec: VC, + codec: Codec, prefix: Prefix, } @@ -39,9 +39,9 @@ impl AccessoryStateValue { } } -impl AccessoryStateValue { +impl AccessoryStateValue { /// Creates a new [`AccessoryStateValue`] with the given prefix and codec. - pub fn with_codec(prefix: Prefix, codec: VC) -> Self { + pub fn with_codec(prefix: Prefix, codec: Codec) -> Self { Self { _phantom: PhantomData, codec, @@ -55,18 +55,19 @@ impl AccessoryStateValue { } } -impl AccessoryStateValue +impl AccessoryStateValue where - VC: StateValueCodec, + Codec: StateCodec, + Codec::ValueCodec: StateValueCodec, { /// Sets a value in the AccessoryStateValue. pub fn set(&self, value: &V, working_set: &mut AccessoryWorkingSet) { - working_set.set_value(self.prefix(), &SingletonKey, value, &self.codec) + working_set.set_singleton(self.prefix(), value, &self.codec) } /// Gets a value from the AccessoryStateValue or None if the value is absent. pub fn get(&self, working_set: &mut AccessoryWorkingSet) -> Option { - working_set.get_value(self.prefix(), &SingletonKey, &self.codec) + working_set.get_singleton(self.prefix(), &self.codec) } /// Gets a value from the AccessoryStateValue or Error if the value is absent. @@ -80,7 +81,7 @@ where /// Removes a value from the AccessoryStateValue, returning the value (or None if the key is absent). pub fn remove(&self, working_set: &mut AccessoryWorkingSet) -> Option { - working_set.remove_value(self.prefix(), &SingletonKey, &self.codec) + working_set.remove_singleton(self.prefix(), &self.codec) } /// Removes a value and from the AccessoryStateValue, returning the value (or Error if the key is absent). @@ -94,10 +95,6 @@ where /// Deletes a value from the AccessoryStateValue. pub fn delete(&self, working_set: &mut AccessoryWorkingSet) { - working_set.delete_value(self.prefix(), &SingletonKey); + working_set.delete_singleton(self.prefix()); } } - -// SingletonKey is very similar to the unit type `()` i.e. it has only one value. -#[derive(Debug, PartialEq, Eq, Hash)] -struct SingletonKey; diff --git a/module-system/sov-state/src/containers/accessory_vec.rs b/module-system/sov-state/src/containers/accessory_vec.rs index b7ff94fd3f..3028d83b73 100644 --- a/module-system/sov-state/src/containers/accessory_vec.rs +++ b/module-system/sov-state/src/containers/accessory_vec.rs @@ -1,7 +1,7 @@ use std::iter::FusedIterator; use std::marker::PhantomData; -use crate::codec::{BorshCodec, StateValueCodec}; +use crate::codec::{BorshCodec, StateCodec, StateKeyCodec, StateValueCodec}; use crate::{ AccessoryStateMap, AccessoryStateValue, AccessoryWorkingSet, Prefix, StateVecError, Storage, }; @@ -15,19 +15,18 @@ use crate::{ serde::Serialize, serde::Deserialize, )] -pub struct AccessoryStateVec -where - VC: StateValueCodec, -{ +pub struct AccessoryStateVec { _phantom: PhantomData, prefix: Prefix, - len_value: AccessoryStateValue, - elems: AccessoryStateMap, + len_value: AccessoryStateValue, + elems: AccessoryStateMap, } impl AccessoryStateVec where - BorshCodec: StateValueCodec, + BorshCodec: StateCodec + Clone, + ::ValueCodec: StateValueCodec + StateValueCodec, + ::KeyCodec: StateKeyCodec, { /// Crates a new [`AccessoryStateVec`] with the given prefix and the default /// [`StateValueCodec`] (i.e. [`BorshCodec`]). @@ -36,18 +35,20 @@ where } } -impl AccessoryStateVec +impl AccessoryStateVec where - VC: StateValueCodec + StateValueCodec + Clone, + Codec: StateCodec + Clone, + Codec::ValueCodec: StateValueCodec + StateValueCodec, + Codec::KeyCodec: StateKeyCodec, { /// Creates a new [`AccessoryStateVec`] with the given prefix and codec. - pub fn with_codec(prefix: Prefix, codec: VC) -> Self { + pub fn with_codec(prefix: Prefix, codec: Codec) -> Self { // Differentiating the prefixes for the length and the elements // shouldn't be necessary, but it's best not to rely on implementation // details of `StateValue` and `StateMap` as they both have the right to // reserve the whole key space for themselves. let len_value = - AccessoryStateValue::::with_codec(prefix.extended(b"l"), codec.clone()); + AccessoryStateValue::::with_codec(prefix.extended(b"l"), codec.clone()); let elems = AccessoryStateMap::with_codec(prefix.extended(b"e"), codec); Self { _phantom: PhantomData, @@ -169,7 +170,7 @@ where pub fn iter<'a, 'ws, S: Storage>( &'a self, working_set: &'ws mut AccessoryWorkingSet<'ws, S>, - ) -> AccessoryStateVecIter<'a, 'ws, V, VC, S> { + ) -> AccessoryStateVecIter<'a, 'ws, V, Codec, S> { let len = self.len(working_set); AccessoryStateVecIter { state_vec: self, @@ -183,20 +184,24 @@ where /// An [`Iterator`] over a [`AccessoryStateVec`] /// /// See [`AccessoryStateVec::iter`] for more details. -pub struct AccessoryStateVecIter<'a, 'ws, V, VC, S> +pub struct AccessoryStateVecIter<'a, 'ws, V, Codec, S> where - VC: StateValueCodec, + Codec: StateCodec + Clone, + Codec::ValueCodec: StateValueCodec + StateValueCodec, + Codec::KeyCodec: StateKeyCodec, S: Storage, { - state_vec: &'a AccessoryStateVec, + state_vec: &'a AccessoryStateVec, ws: &'ws mut AccessoryWorkingSet<'ws, S>, len: usize, next_i: usize, } -impl<'a, 'ws, V, VC, S> Iterator for AccessoryStateVecIter<'a, 'ws, V, VC, S> +impl<'a, 'ws, V, Codec, S> Iterator for AccessoryStateVecIter<'a, 'ws, V, Codec, S> where - VC: StateValueCodec + StateValueCodec + Clone, + Codec: StateCodec + Clone, + Codec::ValueCodec: StateValueCodec + StateValueCodec, + Codec::KeyCodec: StateKeyCodec, S: Storage, { type Item = V; @@ -211,9 +216,11 @@ where } } -impl<'a, 'ws, V, VC, S> ExactSizeIterator for AccessoryStateVecIter<'a, 'ws, V, VC, S> +impl<'a, 'ws, V, Codec, S> ExactSizeIterator for AccessoryStateVecIter<'a, 'ws, V, Codec, S> where - VC: StateValueCodec + StateValueCodec + Clone, + Codec: StateCodec + Clone, + Codec::ValueCodec: StateValueCodec + StateValueCodec, + Codec::KeyCodec: StateKeyCodec, S: Storage, { fn len(&self) -> usize { @@ -221,9 +228,11 @@ where } } -impl<'a, 'ws, V, VC, S> FusedIterator for AccessoryStateVecIter<'a, 'ws, V, VC, S> +impl<'a, 'ws, V, Codec, S> FusedIterator for AccessoryStateVecIter<'a, 'ws, V, Codec, S> where - VC: StateValueCodec + StateValueCodec + Clone, + Codec: StateCodec + Clone, + Codec::ValueCodec: StateValueCodec + StateValueCodec, + Codec::KeyCodec: StateKeyCodec, S: Storage, { } diff --git a/module-system/sov-state/src/containers/map.rs b/module-system/sov-state/src/containers/map.rs index f596b293d1..9eccfdbb40 100644 --- a/module-system/sov-state/src/containers/map.rs +++ b/module-system/sov-state/src/containers/map.rs @@ -1,10 +1,8 @@ -use std::borrow::Borrow; -use std::hash::Hash; use std::marker::PhantomData; use thiserror::Error; -use crate::codec::{BorshCodec, StateValueCodec}; +use crate::codec::{BorshCodec, EncodeKeyLike, StateCodec, StateKeyCodec, StateValueCodec}; use crate::storage::StorageKey; use crate::{Prefix, StateReaderAndWriter, Storage, WorkingSet}; @@ -14,7 +12,7 @@ use crate::{Prefix, StateReaderAndWriter, Storage, WorkingSet}; /// [`StateMap`] is generic over: /// - a key type `K`; /// - a value type `V`; -/// - a [`StateValueCodec`] `VC`. +/// - a [`StateValueCodec`] `Codec`. #[derive( Debug, Clone, @@ -24,9 +22,9 @@ use crate::{Prefix, StateReaderAndWriter, Storage, WorkingSet}; serde::Serialize, serde::Deserialize, )] -pub struct StateMap { +pub struct StateMap { _phantom: (PhantomData, PhantomData), - value_codec: VC, + codec: Codec, prefix: Prefix, } @@ -45,26 +43,31 @@ impl StateMap { } } -impl StateMap { +impl StateMap { /// Creates a new [`StateMap`] with the given prefix and [`StateValueCodec`]. - pub fn with_codec(prefix: Prefix, codec: VC) -> Self { + pub fn with_codec(prefix: Prefix, codec: Codec) -> Self { Self { _phantom: (PhantomData, PhantomData), - value_codec: codec, + codec, prefix, } } + pub fn codec(&self) -> &Codec { + &self.codec + } + /// Returns the prefix used when this [`StateMap`] was created. pub fn prefix(&self) -> &Prefix { &self.prefix } } -impl StateMap +impl StateMap where - K: Hash + Eq, - VC: StateValueCodec, + Codec: StateCodec, + Codec::KeyCodec: StateKeyCodec, + Codec::ValueCodec: StateValueCodec, { /// Inserts a key-value pair into the map. /// @@ -72,10 +75,10 @@ where /// map’s key type. pub fn set(&self, key: &Q, value: &V, working_set: &mut WorkingSet) where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec::KeyCodec: EncodeKeyLike, + Q: ?Sized, { - working_set.set_value(self.prefix(), key, value, &self.value_codec) + working_set.set_value(self.prefix(), key, value, &self.codec) } /// Returns the value corresponding to the key, or [`None`] if the map @@ -83,9 +86,8 @@ where /// /// # Examples /// - /// The key may be any borrowed form of the map’s key type. Note that - /// [`Hash`] and [`Eq`] on the borrowed form must match those for the key - /// type. + /// The key may be any item that implements [`EncodeKeyLike`] the map's key type + /// using your chosen codec. /// /// ``` /// use sov_state::{StateMap, Storage, WorkingSet}; @@ -100,7 +102,7 @@ where /// } /// ``` /// - /// If the map's key type does not implement [`Borrow`] for your desired + /// If the map's key type does not implement [`EncodeKeyLike`] for your desired /// target type, you'll have to convert the key to something else. An /// example of this would be "slicing" an array to use in [`Vec`]-keyed /// maps: @@ -117,10 +119,12 @@ where /// ``` pub fn get(&self, key: &Q, working_set: &mut WorkingSet) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec: StateCodec, + Codec::KeyCodec: EncodeKeyLike, + Codec::ValueCodec: StateValueCodec, + Q: ?Sized, { - working_set.get_value(self.prefix(), key, &self.value_codec) + working_set.get_value(self.prefix(), key, &self.codec) } /// Returns the value corresponding to the key or [`StateMapError`] if key is absent in @@ -131,11 +135,16 @@ where working_set: &mut WorkingSet, ) -> Result where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec: StateCodec, + Codec::KeyCodec: EncodeKeyLike, + Codec::ValueCodec: StateValueCodec, + Q: ?Sized, { self.get(key, working_set).ok_or_else(|| { - StateMapError::MissingValue(self.prefix().clone(), StorageKey::new(self.prefix(), key)) + StateMapError::MissingValue( + self.prefix().clone(), + StorageKey::new(self.prefix(), key, self.codec.key_codec()), + ) }) } @@ -143,10 +152,12 @@ where /// [`None`] if the key is absent). pub fn remove(&self, key: &Q, working_set: &mut WorkingSet) -> Option where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec: StateCodec, + Codec::KeyCodec: EncodeKeyLike, + Codec::ValueCodec: StateValueCodec, + Q: ?Sized, { - working_set.remove_value(self.prefix(), key, &self.value_codec) + working_set.remove_value(self.prefix(), key, &self.codec) } /// Removes a key from the map, returning the corresponding value (or @@ -159,11 +170,16 @@ where working_set: &mut WorkingSet, ) -> Result where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec: StateCodec, + Codec::KeyCodec: EncodeKeyLike, + Codec::ValueCodec: StateValueCodec, + Q: ?Sized, { self.remove(key, working_set).ok_or_else(|| { - StateMapError::MissingValue(self.prefix().clone(), StorageKey::new(self.prefix(), key)) + StateMapError::MissingValue( + self.prefix().clone(), + StorageKey::new(self.prefix(), key, self.codec.key_codec()), + ) }) } @@ -173,19 +189,22 @@ where /// return the value beforing deletion. pub fn delete(&self, key: &Q, working_set: &mut WorkingSet) where - K: Borrow, - Q: Hash + Eq + ?Sized, + Codec: StateCodec, + Codec::KeyCodec: EncodeKeyLike, + Q: ?Sized, { - working_set.delete_value(self.prefix(), key); + working_set.delete_value(self.prefix(), key, &self.codec); } } #[cfg(feature = "arbitrary")] -impl<'a, K, V, VC> StateMap +impl<'a, K, V, Codec> StateMap where - K: arbitrary::Arbitrary<'a> + Hash + Eq, - V: arbitrary::Arbitrary<'a> + Hash + Eq, - VC: StateValueCodec + Default, + K: arbitrary::Arbitrary<'a>, + V: arbitrary::Arbitrary<'a>, + Codec: StateCodec + Default, + Codec::KeyCodec: StateKeyCodec, + Codec::ValueCodec: StateValueCodec, { pub fn arbitrary_workset( u: &mut arbitrary::Unstructured<'a>, @@ -198,7 +217,7 @@ where let prefix = Prefix::arbitrary(u)?; let len = u.arbitrary_len::<(K, V)>()?; - let codec = VC::default(); + let codec = Codec::default(); let map = StateMap::with_codec(prefix, codec); (0..len).try_fold(map, |map, _| { diff --git a/module-system/sov-state/src/containers/value.rs b/module-system/sov-state/src/containers/value.rs index c4d0c26abd..388c4e7dc7 100644 --- a/module-system/sov-state/src/containers/value.rs +++ b/module-system/sov-state/src/containers/value.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use thiserror::Error; -use crate::codec::{BorshCodec, StateValueCodec}; +use crate::codec::{BorshCodec, StateCodec, StateValueCodec}; use crate::{Prefix, StateReaderAndWriter, Storage, WorkingSet}; /// Container for a single value. @@ -15,9 +15,9 @@ use crate::{Prefix, StateReaderAndWriter, Storage, WorkingSet}; serde::Serialize, serde::Deserialize, )] -pub struct StateValue { +pub struct StateValue { _phantom: PhantomData, - codec: VC, + codec: Codec, prefix: Prefix, } @@ -36,9 +36,9 @@ impl StateValue { } } -impl StateValue { +impl StateValue { /// Creates a new [`StateValue`] with the given prefix and codec. - pub fn with_codec(prefix: Prefix, codec: VC) -> Self { + pub fn with_codec(prefix: Prefix, codec: Codec) -> Self { Self { _phantom: PhantomData, codec, @@ -52,18 +52,19 @@ impl StateValue { } } -impl StateValue +impl StateValue where - VC: StateValueCodec, + Codec: StateCodec, + Codec::ValueCodec: StateValueCodec, { /// Sets a value in the StateValue. pub fn set(&self, value: &V, working_set: &mut WorkingSet) { - working_set.set_value(self.prefix(), &SingletonKey, value, &self.codec) + working_set.set_singleton(self.prefix(), value, &self.codec) } /// Gets a value from the StateValue or None if the value is absent. pub fn get(&self, working_set: &mut WorkingSet) -> Option { - working_set.get_value(self.prefix(), &SingletonKey, &self.codec) + working_set.get_singleton(self.prefix(), &self.codec) } /// Gets a value from the StateValue or Error if the value is absent. @@ -74,7 +75,7 @@ where /// Removes a value from the StateValue, returning the value (or None if the key is absent). pub fn remove(&self, working_set: &mut WorkingSet) -> Option { - working_set.remove_value(self.prefix(), &SingletonKey, &self.codec) + working_set.remove_singleton(self.prefix(), &self.codec) } /// Removes a value and from the StateValue, returning the value (or Error if the key is absent). @@ -85,10 +86,6 @@ where /// Deletes a value from the StateValue. pub fn delete(&self, working_set: &mut WorkingSet) { - working_set.delete_value(self.prefix(), &SingletonKey); + working_set.delete_singleton(self.prefix()); } } - -// SingletonKey is very similar to the unit type `()` i.e. it has only one value. -#[derive(Debug, PartialEq, Eq, Hash)] -struct SingletonKey; diff --git a/module-system/sov-state/src/containers/vec.rs b/module-system/sov-state/src/containers/vec.rs index 1d0b3b8c92..1873064e3e 100644 --- a/module-system/sov-state/src/containers/vec.rs +++ b/module-system/sov-state/src/containers/vec.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use thiserror::Error; -use crate::codec::{BorshCodec, StateValueCodec}; +use crate::codec::{BorshCodec, StateCodec, StateKeyCodec, StateValueCodec}; use crate::{Prefix, StateMap, StateValue, Storage, WorkingSet}; #[derive( @@ -15,14 +15,11 @@ use crate::{Prefix, StateMap, StateValue, Storage, WorkingSet}; serde::Serialize, serde::Deserialize, )] -pub struct StateVec -where - VC: StateValueCodec, -{ +pub struct StateVec { _phantom: PhantomData, prefix: Prefix, - len_value: StateValue, - elems: StateMap, + len_value: StateValue, + elems: StateMap, } /// Error type for `StateVec` get method. @@ -45,12 +42,14 @@ where } } -impl StateVec +impl StateVec where - VC: StateValueCodec + StateValueCodec + Clone, + Codec: StateCodec + Clone, + Codec::ValueCodec: StateValueCodec + StateValueCodec, + Codec::KeyCodec: StateKeyCodec, { /// Creates a new [`StateVec`] with the given prefix and codec. - pub fn with_codec(prefix: Prefix, codec: VC) -> Self { + pub fn with_codec(prefix: Prefix, codec: Codec) -> Self { // Differentiating the prefixes for the length and the elements // shouldn't be necessary, but it's best not to rely on implementation // details of `StateValue` and `StateMap` as they both have the right to @@ -173,7 +172,7 @@ where pub fn iter<'a, 'ws, S: Storage>( &'a self, working_set: &'ws mut WorkingSet, - ) -> StateVecIter<'a, 'ws, V, VC, S> { + ) -> StateVecIter<'a, 'ws, V, Codec, S> { let len = self.len(working_set); StateVecIter { state_vec: self, @@ -187,20 +186,24 @@ where /// An [`Iterator`] over a [`StateVec`] /// /// See [`StateVec::iter`] for more details. -pub struct StateVecIter<'a, 'ws, V, VC, S> +pub struct StateVecIter<'a, 'ws, V, Codec, S> where - VC: StateValueCodec, + Codec: StateCodec + Clone, + Codec::ValueCodec: StateValueCodec + StateValueCodec, + Codec::KeyCodec: StateKeyCodec, S: Storage, { - state_vec: &'a StateVec, + state_vec: &'a StateVec, ws: &'ws mut WorkingSet, len: usize, next_i: usize, } -impl<'a, 'ws, V, VC, S> Iterator for StateVecIter<'a, 'ws, V, VC, S> +impl<'a, 'ws, V, Codec, S> Iterator for StateVecIter<'a, 'ws, V, Codec, S> where - VC: StateValueCodec + StateValueCodec + Clone, + Codec: StateCodec + Clone, + Codec::ValueCodec: StateValueCodec + StateValueCodec, + Codec::KeyCodec: StateKeyCodec, S: Storage, { type Item = V; @@ -215,9 +218,11 @@ where } } -impl<'a, 'ws, V, VC, S> ExactSizeIterator for StateVecIter<'a, 'ws, V, VC, S> +impl<'a, 'ws, V, Codec, S> ExactSizeIterator for StateVecIter<'a, 'ws, V, Codec, S> where - VC: StateValueCodec + StateValueCodec + Clone, + Codec: StateCodec + Clone, + Codec::ValueCodec: StateValueCodec + StateValueCodec, + Codec::KeyCodec: StateKeyCodec, S: Storage, { fn len(&self) -> usize { @@ -225,9 +230,11 @@ where } } -impl<'a, 'ws, V, VC, S> FusedIterator for StateVecIter<'a, 'ws, V, VC, S> +impl<'a, 'ws, V, Codec, S> FusedIterator for StateVecIter<'a, 'ws, V, Codec, S> where - VC: StateValueCodec + StateValueCodec + Clone, + Codec: StateCodec + Clone, + Codec::ValueCodec: StateValueCodec + StateValueCodec, + Codec::KeyCodec: StateKeyCodec, S: Storage, { } @@ -302,7 +309,7 @@ mod test { ws: &mut WorkingSet, ) where S: Storage, - BorshCodec: StateValueCodec + StateValueCodec, + BorshCodec: StateValueCodec, T: Eq + Debug, { match action { diff --git a/module-system/sov-state/src/scratchpad.rs b/module-system/sov-state/src/scratchpad.rs index f0174f59c6..558b025a92 100644 --- a/module-system/sov-state/src/scratchpad.rs +++ b/module-system/sov-state/src/scratchpad.rs @@ -1,11 +1,10 @@ use std::collections::HashMap; use std::fmt::Debug; -use std::hash::Hash; use sov_first_read_last_write_cache::{CacheKey, CacheValue}; use sov_rollup_interface::stf::Event; -use crate::codec::StateValueCodec; +use crate::codec::{EncodeKeyLike, StateCodec, StateValueCodec}; use crate::internal_cache::{OrderedReadsAndWrites, StorageInternalCache}; use crate::storage::{StorageKey, StorageValue}; use crate::{Prefix, Storage}; @@ -310,50 +309,113 @@ pub(crate) trait StateReaderAndWriter { fn delete(&mut self, key: &StorageKey); - fn set_value(&mut self, prefix: &Prefix, storage_key: &K, value: &V, codec: &VC) + fn set_value( + &mut self, + prefix: &Prefix, + storage_key: &Q, + value: &V, + codec: &Codec, + ) where + Q: ?Sized, + Codec: StateCodec, + Codec::KeyCodec: EncodeKeyLike, + Codec::ValueCodec: StateValueCodec, + { + let storage_key = StorageKey::new(prefix, storage_key, codec.key_codec()); + let storage_value = StorageValue::new(value, codec.value_codec()); + self.set(&storage_key, storage_value); + } + + fn set_singleton(&mut self, prefix: &Prefix, value: &V, codec: &Codec) where - K: Hash + Eq + ?Sized, - VC: StateValueCodec, + Codec: StateCodec, + Codec::ValueCodec: StateValueCodec, { - let storage_key = StorageKey::new(prefix, storage_key); - let storage_value = StorageValue::new(value, codec); + let storage_key = StorageKey::singleton(prefix); + let storage_value = StorageValue::new(value, codec.value_codec()); self.set(&storage_key, storage_value); } - fn get_decoded(&mut self, storage_key: &StorageKey, codec: &VC) -> Option + fn get_decoded(&mut self, storage_key: &StorageKey, codec: &Codec) -> Option where - VC: StateValueCodec, + Codec: StateCodec, + Codec::ValueCodec: StateValueCodec, { let storage_value = self.get(storage_key)?; - Some(codec.decode_value_unwrap(storage_value.value())) + Some( + codec + .value_codec() + .decode_value_unwrap(storage_value.value()), + ) + } + + fn get_value( + &mut self, + prefix: &Prefix, + storage_key: &Q, + codec: &Codec, + ) -> Option + where + Q: ?Sized, + Codec: StateCodec, + Codec::KeyCodec: EncodeKeyLike, + Codec::ValueCodec: StateValueCodec, + { + let storage_key = StorageKey::new(prefix, storage_key, codec.key_codec()); + self.get_decoded(&storage_key, codec) } - fn get_value(&mut self, prefix: &Prefix, storage_key: &K, codec: &VC) -> Option + fn get_singleton(&mut self, prefix: &Prefix, codec: &Codec) -> Option where - K: Hash + Eq + ?Sized, - VC: StateValueCodec, + Codec: StateCodec, + Codec::ValueCodec: StateValueCodec, { - let storage_key = StorageKey::new(prefix, storage_key); + let storage_key = StorageKey::singleton(prefix); self.get_decoded(&storage_key, codec) } - fn remove_value(&mut self, prefix: &Prefix, storage_key: &K, codec: &VC) -> Option + fn remove_value( + &mut self, + prefix: &Prefix, + storage_key: &Q, + codec: &Codec, + ) -> Option where - K: Hash + Eq + ?Sized, - VC: StateValueCodec, + Q: ?Sized, + Codec: StateCodec, + Codec::KeyCodec: EncodeKeyLike, + Codec::ValueCodec: StateValueCodec, { - let storage_key = StorageKey::new(prefix, storage_key); + let storage_key = StorageKey::new(prefix, storage_key, codec.key_codec()); let storage_value = self.get_decoded(&storage_key, codec)?; self.delete(&storage_key); Some(storage_value) } - fn delete_value(&mut self, prefix: &Prefix, storage_key: &K) + fn remove_singleton(&mut self, prefix: &Prefix, codec: &Codec) -> Option where - K: Hash + Eq + ?Sized, + Codec: StateCodec, + Codec::ValueCodec: StateValueCodec, { - let storage_key = StorageKey::new(prefix, storage_key); + let storage_key = StorageKey::singleton(prefix); + let storage_value = self.get_decoded(&storage_key, codec)?; + self.delete(&storage_key); + Some(storage_value) + } + + fn delete_value(&mut self, prefix: &Prefix, storage_key: &Q, codec: &Codec) + where + Q: ?Sized, + Codec: StateCodec, + Codec::KeyCodec: EncodeKeyLike, + { + let storage_key = StorageKey::new(prefix, storage_key, codec.key_codec()); + self.delete(&storage_key); + } + + fn delete_singleton(&mut self, prefix: &Prefix) { + let storage_key = StorageKey::singleton(prefix); self.delete(&storage_key); } } diff --git a/module-system/sov-state/src/storage.rs b/module-system/sov-state/src/storage.rs index 92dcfbd809..d3c1445905 100644 --- a/module-system/sov-state/src/storage.rs +++ b/module-system/sov-state/src/storage.rs @@ -1,5 +1,4 @@ use std::fmt::Display; -use std::hash::Hash; use std::sync::Arc; use anyhow::ensure; @@ -9,7 +8,7 @@ use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use sov_first_read_last_write_cache::{CacheKey, CacheValue}; -use crate::codec::StateValueCodec; +use crate::codec::{EncodeKeyLike, StateKeyCodec, StateValueCodec}; use crate::internal_cache::OrderedReadsAndWrites; use crate::utils::AlignedVec; use crate::witness::Witness; @@ -57,11 +56,12 @@ impl Display for StorageKey { impl StorageKey { /// Creates a new StorageKey that combines a prefix and a key. - pub fn new(prefix: &Prefix, key: &K) -> Self + pub fn new(prefix: &Prefix, key: &Q, codec: &KC) -> Self where - K: Hash + ?Sized, + KC: EncodeKeyLike, + Q: ?Sized, { - let encoded_key = nohash_serialize(key); + let encoded_key = codec.encode_key_like(key); let encoded_key = AlignedVec::new(encoded_key); let full_key = Vec::::with_capacity(prefix.len() + encoded_key.len()); @@ -73,26 +73,13 @@ impl StorageKey { key: Arc::new(full_key.into_inner()), } } -} - -// Serializes a value into a `Vec` using `std::hash::Hasher` -// writer methods, but without actually ever hashing anything. -fn nohash_serialize(item: T) -> Vec { - struct NoHasher(Vec); - impl std::hash::Hasher for NoHasher { - fn finish(&self) -> u64 { - 0 - } - - fn write(&mut self, bytes: &[u8]) { - self.0.extend_from_slice(bytes); + /// Creates a new StorageKey that combines a prefix and a key. + pub fn singleton(prefix: &Prefix) -> Self { + Self { + key: Arc::new(prefix.as_aligned_vec().clone().into_inner()), } } - - let mut hasher = NoHasher(vec![]); - item.hash(&mut hasher); - hasher.0 } /// A serialized value suitable for storing. Internally uses an [`Arc>`] for cheap cloning. @@ -235,21 +222,21 @@ pub trait Storage: Clone { proof: StorageProof, ) -> Result<(StorageKey, Option), anyhow::Error>; - fn verify_proof( + fn verify_proof( &self, state_root: [u8; 32], proof: StorageProof, expected_key: &K, - storage_map: &StateMap, + storage_map: &StateMap, ) -> Result, anyhow::Error> where - K: Hash + Eq, + Codec: StateKeyCodec, { let (storage_key, storage_value) = self.open_proof(state_root, proof)?; // We have to check that the storage key is the same as the external key ensure!( - storage_key == StorageKey::new(storage_map.prefix(), expected_key), + storage_key == StorageKey::new(storage_map.prefix(), expected_key, storage_map.codec()), "The storage key from the proof doesn't match the expected storage key." ); @@ -288,15 +275,18 @@ pub trait NativeStorage: Storage { fn get_with_proof(&self, key: StorageKey, witness: &Self::Witness) -> StorageProof; - fn get_with_proof_from_state_map( + fn get_with_proof_from_state_map( &self, - key: &K, - state_map: &StateMap, + key: &Q, + state_map: &StateMap, witness: &Self::Witness, ) -> StorageProof where - K: Hash + Eq, + Codec: EncodeKeyLike, { - self.get_with_proof(StorageKey::new(state_map.prefix(), key), witness) + self.get_with_proof( + StorageKey::new(state_map.prefix(), key, state_map.codec()), + witness, + ) } } diff --git a/packages_to_publish.txt b/packages_to_publish.txt index 1c23ce40cf..7a87f940c9 100644 --- a/packages_to_publish.txt +++ b/packages_to_publish.txt @@ -3,8 +3,8 @@ sov-first-read-last-write-cache sov-schema-db sov-db sov-sequencer -zk-cycle-macros -zk-cycle-utils +sov-zk-cycle-macros +sov-zk-cycle-utils sov-state sov-modules-macros sov-modules-api @@ -16,4 +16,8 @@ sov-bank sov-sequencer-registry sov-prover-incentives sov-chain-state -sov-blob-storage \ No newline at end of file +sov-blob-storage + +# Adapters +sov-risc0-adapter +sov-celestia-adapter diff --git a/rollup-interface/src/state_machine/optimistic.rs b/rollup-interface/src/state_machine/optimistic.rs index 84668f7dfe..e33af6599e 100644 --- a/rollup-interface/src/state_machine/optimistic.rs +++ b/rollup-interface/src/state_machine/optimistic.rs @@ -34,11 +34,11 @@ pub struct Attestation { /// The contents of a challenge to an attestation, which are contained as a public output of the proof /// Generic over an address type and a validity condition #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize)] -pub struct ChallengeContents { +pub struct ChallengeContents { /// The rollup address of the originator of this challenge pub challenger_address: Address, /// The state transition that was proven - pub state_transition: StateTransition, + pub state_transition: StateTransition, } #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, Serialize, Deserialize)]