From 5596ab5818796b91d57ac99e2adc9488408f1888 Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Wed, 27 Sep 2023 18:10:14 +0530 Subject: [PATCH 01/34] offchain processing --- Cargo.lock | 122 +++++++++- Cargo.toml | 3 +- examples/demo-rollup/Cargo.toml | 1 + examples/demo-stf/Cargo.toml | 1 + full-node/sov-sequencer/src/utils.rs | 33 +++ full-node/sov-stf-runner/src/batch_builder.rs | 8 +- .../sov-nft-module/Cargo.toml | 4 + .../sov-nft-module/offchain_readme.md | 56 +++++ .../sov-nft-module/src/call.rs | 23 +- .../sov-nft-module/src/init_db.sql | 47 ++++ .../sov-nft-module/src/lib.rs | 1 + .../sov-nft-module/src/offchain.rs | 168 ++++++++++++++ .../sov-modules-api/src/transaction.rs | 4 +- module-system/sov-modules-macros/src/lib.rs | 10 +- .../sov-modules-macros/src/offchain.rs | 31 +++ utils/nft-utils/Cargo.toml | 31 +++ utils/nft-utils/src/main.rs | 217 ++++++++++++++++++ 17 files changed, 748 insertions(+), 12 deletions(-) create mode 100644 module-system/module-implementations/sov-nft-module/offchain_readme.md create mode 100644 module-system/module-implementations/sov-nft-module/src/init_db.sql create mode 100644 module-system/module-implementations/sov-nft-module/src/offchain.rs create mode 100644 module-system/sov-modules-macros/src/offchain.rs create mode 100644 utils/nft-utils/Cargo.toml create mode 100644 utils/nft-utils/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index a27d25264..521be6e73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2636,6 +2636,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + [[package]] name = "fixed-hash" version = "0.8.0" @@ -3947,8 +3953,6 @@ checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "librocksdb-sys" version = "0.11.0+8.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" dependencies = [ "bindgen 0.65.1", "bzip2-sys", @@ -4282,6 +4286,26 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nft-utils" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "demo-stf", + "jsonrpsee", + "schemars", + "serde", + "serde_json", + "sov-cli", + "sov-modules-api", + "sov-modules-macros", + "sov-nft-module", + "sov-rollup-interface", + "sov-sequencer", + "tokio", +] + [[package]] name = "nibble_vec" version = "0.1.0" @@ -4991,6 +5015,49 @@ dependencies = [ "serde", ] +[[package]] +name = "postgres" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7915b33ed60abc46040cbcaa25ffa1c7ec240668e0477c4f3070786f5916d451" +dependencies = [ + "bytes", + "fallible-iterator", + "futures-util", + "log", + "tokio", + "tokio-postgres", +] + +[[package]] +name = "postgres-protocol" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" +dependencies = [ + "base64 0.21.4", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand 0.8.5", + "sha2 0.10.7", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -6293,8 +6360,6 @@ dependencies = [ [[package]] name = "rocksdb" version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" dependencies = [ "libc", "librocksdb-sys", @@ -7480,6 +7545,7 @@ dependencies = [ "anyhow", "borsh", "jsonrpsee", + "postgres", "schemars", "serde", "serde_json", @@ -7490,6 +7556,7 @@ dependencies = [ "sov-rollup-interface", "sov-state", "tempfile", + "tokio", ] [[package]] @@ -7767,6 +7834,17 @@ dependencies = [ "precomputed-hash", ] +[[package]] +name = "stringprep" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +dependencies = [ + "finl_unicode", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "strsim" version = "0.9.3" @@ -8177,6 +8255,32 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-postgres" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d340244b32d920260ae7448cb72b6e238bddc3d4f7603394e7dd46ed8e48f5b8" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot 0.12.1", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand 0.8.5", + "socket2 0.5.4", + "tokio", + "tokio-util", + "whoami", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -8869,6 +8973,16 @@ dependencies = [ "rustix", ] +[[package]] +name = "whoami" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wildmatch" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 88604a73c..60c8ce9a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ "utils/zk-cycle-macros", "utils/zk-cycle-utils", "utils/bashtestmd", + "utils/nft-utils", "module-system/sov-cli", "module-system/sov-modules-stf-template", @@ -77,7 +78,7 @@ proptest = "1.0.0" proptest-derive = "0.3.0" rand = "0.8" rayon = "1.5.2" -rocksdb = { version = "0.21.0", features = ["lz4"] } +rocksdb = { path="/Users/dubbelosix/rust-rocksdb", features = ["lz4"] } serde = { version = "1.0.188", features = ["derive", "rc"] } serde_json = { version = "1.0" } sha2 = "0.10.6" diff --git a/examples/demo-rollup/Cargo.toml b/examples/demo-rollup/Cargo.toml index e7fbbd669..2c3a0a2c2 100644 --- a/examples/demo-rollup/Cargo.toml +++ b/examples/demo-rollup/Cargo.toml @@ -80,6 +80,7 @@ native = ["anyhow", "jsonrpsee", "serde", "serde_json", "tracing", "tokio", "tra "sov-modules-api/native"] bench = ["native", "async-trait", "borsh", "hex"] local = ["sov-ethereum/local"] +offchain = ["demo-stf/offchain"] [[bench]] name = "rollup_bench" diff --git a/examples/demo-stf/Cargo.toml b/examples/demo-stf/Cargo.toml index 7ff6ba2be..2d888f222 100644 --- a/examples/demo-stf/Cargo.toml +++ b/examples/demo-stf/Cargo.toml @@ -50,6 +50,7 @@ rand = "0.8" [features] default = [] +offchain = ["sov-nft-module/offchain"] experimental = ["sov-evm/experimental", "reth-primitives"] native = [ "sov-bank/native", diff --git a/full-node/sov-sequencer/src/utils.rs b/full-node/sov-sequencer/src/utils.rs index a4a828ee9..8c21860c4 100644 --- a/full-node/sov-sequencer/src/utils.rs +++ b/full-node/sov-sequencer/src/utils.rs @@ -38,6 +38,39 @@ impl SimpleClient { Ok(()) } + /// Sends multiple transactions to the sequencer for immediate publication. + pub async fn send_transactions( + &self, + txs: Vec, + micro_batch: Option, + ) -> Result<(), anyhow::Error> { + let serialized_txs: Vec> = txs + .into_iter() + .map(|tx| tx.try_to_vec()) + .collect::>()?; + + match micro_batch { + Some(batch_size) => { + for chunk in serialized_txs.chunks(batch_size) { + let response: String = self + .http_client + .request("sequencer_publishBatch", chunk.to_vec()) + .await?; + info!("publish batch response for chunk: {:?}", response); + } + } + None => { + let response: String = self + .http_client + .request("sequencer_publishBatch", serialized_txs) + .await?; + info!("publish batch response: {:?}", response); + } + } + + Ok(()) + } + /// Get a reference to the underlying [`HttpClient`] pub fn http(&self) -> &HttpClient { &self.http_client diff --git a/full-node/sov-stf-runner/src/batch_builder.rs b/full-node/sov-stf-runner/src/batch_builder.rs index 47c935c52..e6b3cd009 100644 --- a/full-node/sov-stf-runner/src/batch_builder.rs +++ b/full-node/sov-stf-runner/src/batch_builder.rs @@ -125,14 +125,14 @@ where current_batch_size += tx_len; } - if txs.is_empty() { - bail!("No valid transactions are available"); - } - for (tx, err) in dismissed { warn!("Transaction 0x{} was dismissed: {:?}", hex::encode(tx), err); } + if txs.is_empty() { + bail!("No valid transactions are available"); + } + Ok(txs) } } diff --git a/module-system/module-implementations/sov-nft-module/Cargo.toml b/module-system/module-implementations/sov-nft-module/Cargo.toml index c11afd529..165745de2 100644 --- a/module-system/module-implementations/sov-nft-module/Cargo.toml +++ b/module-system/module-implementations/sov-nft-module/Cargo.toml @@ -21,6 +21,9 @@ sov-modules-api = { path = "../../sov-modules-api" } sov-modules-macros = {path = "../../sov-modules-macros"} sov-state = { path = "../../sov-state" } +postgres = {version = "0.19.7", optional = true} +tokio = {version = "1.32.0", features=["full"],optional = true} + [dev-dependencies] sov-rollup-interface = { path = "../../../rollup-interface" } sov-data-generators = { path = "../../utils/sov-data-generators" } @@ -29,6 +32,7 @@ sov-nft-module = { version = "*", features = ["native"], path = "." } [features] default = [] +offchain = ["postgres","tokio"] serde = ["dep:serde"] native = ["serde", "serde_json", "jsonrpsee", "schemars", "sov-state/native", "sov-modules-api/native", ] test = ["native"] diff --git a/module-system/module-implementations/sov-nft-module/offchain_readme.md b/module-system/module-implementations/sov-nft-module/offchain_readme.md new file mode 100644 index 000000000..340238117 --- /dev/null +++ b/module-system/module-implementations/sov-nft-module/offchain_readme.md @@ -0,0 +1,56 @@ +## Offchain testing + +* Install postgres on your system +* Start the postgres terminal +* Create the tables necessary for offchain processing + +```bash +psql postgres -f sovereign/module-system/module-implementations/sov-nft-module/src/init_db.sql +``` +* The above command runs the `init_db.sql` script which creates 3 tables + * `collections` - tracking the NFT collections that have been created, their supply and other info + * `nfts` - tracking the individual NFTs, the `token_uri` pointing to offchain state and other info + * `top_owners` - tracks the number of NFTs of each collection that a user owns + * running the following query can show the top owners for a specific collection + ```sql + SELECT owner, count + FROM top_owners + WHERE collection_address = ( + SELECT collection_address + FROM collections + WHERE collection_name = 'your_collection_name' + ) + ORDER BY count DESC + LIMIT 5; + ``` + * running the following query can show the largest owner for each collection + ```sql + SELECT + collection_name, + owner, + count + FROM ( + SELECT c.collection_name, t.owner, t.count, + RANK() OVER (PARTITION BY t.collection_address ORDER BY t.count DESC) as rank + FROM top_owners t + INNER JOIN collections c ON c.collection_address = t.collection_address + ) sub + WHERE rank = 1; + ``` + +* Run the demo rollup in offchain mode +```bash +rm -rf demo_data; POSTGRES_CONNECTION_STRING="postgresql://username:password@localhost/postgres" cargo run --features offchain -- --da-layer mock +``` +* Explanation of the above command + * `rm -rf demo_data` is to wipe the rollup state. For testing its better to start with clean state + * `POSTGRES_CONNECTION_STRING` is to allow the offchain component of the `sov-nft-module` to connect to postgres instance + * `--features offchain` is necessary to enable offchain processing. Without the feature, the functions are no-ops + * `--da-layer mock` is used to run an in-memory local DA layer +* Run the NFT minting script +```bash +$ cd sovereign/utils/nft-utils +$ cargo run +``` + * The above script creates 3 NFT collections, mints some NFTs to each collection + * The tables can be explored by connecting to postgres and running sample queries from above \ No newline at end of file diff --git a/module-system/module-implementations/sov-nft-module/src/call.rs b/module-system/module-implementations/sov-nft-module/src/call.rs index 095942e78..ce0fc232b 100644 --- a/module-system/module-implementations/sov-nft-module/src/call.rs +++ b/module-system/module-implementations/sov-nft-module/src/call.rs @@ -2,6 +2,7 @@ use anyhow::Result; use sov_modules_api::{CallResponse, Context, WorkingSet}; use crate::address::UserAddress; +use crate::offchain::{track_collection, track_nft, update_top_owners}; use crate::{Collection, CollectionAddress, Nft, NftIdentifier, NonFungibleToken, TokenId}; #[cfg_attr( @@ -87,6 +88,8 @@ impl NonFungibleToken { )?; self.collections .set(&collection_address, &collection, working_set); + std::thread::sleep(std::time::Duration::from_millis(300)); + track_collection(&collection); Ok(CallResponse::default()) } @@ -107,6 +110,7 @@ impl NonFungibleToken { collection.set_collection_uri(collection_uri); self.collections .set(&collection_address, collection.inner(), working_set); + track_collection(collection.inner()); Ok(CallResponse::default()) } @@ -126,6 +130,7 @@ impl NonFungibleToken { collection.freeze(); self.collections .set(&collection_address, collection.inner(), working_set); + track_collection(collection.inner()); Ok(CallResponse::default()) } @@ -165,6 +170,14 @@ impl NonFungibleToken { self.collections .set(&collection_address, collection.inner(), working_set); + track_collection(collection.inner()); + track_nft(&new_nft); + update_top_owners( + &collection_address, + Some(&[(mint_to_address.to_string(), 1)]), + None, + ); + Ok(CallResponse::default()) } @@ -178,12 +191,19 @@ impl NonFungibleToken { ) -> Result { let mut owned_nft = Nft::get_owned_nft(nft_id, collection_address, &self.nfts, context, working_set)?; + let original_owner = owned_nft.inner().get_owner().clone(); owned_nft.set_owner(to); self.nfts.set( &NftIdentifier(nft_id, collection_address.clone()), owned_nft.inner(), working_set, ); + track_nft(owned_nft.inner()); + update_top_owners( + collection_address, + Some(&[(to.to_string(), 1)]), + Some(&[(original_owner.to_string(), 1)]), + ); Ok(CallResponse::default()) } @@ -211,10 +231,11 @@ impl NonFungibleToken { mutable_nft.update_token_uri(&uri); } self.nfts.set( - &NftIdentifier(token_id, collection_address), + &NftIdentifier(token_id, collection_address.clone()), mutable_nft.inner(), working_set, ); + track_nft(mutable_nft.inner()); Ok(CallResponse::default()) } } diff --git a/module-system/module-implementations/sov-nft-module/src/init_db.sql b/module-system/module-implementations/sov-nft-module/src/init_db.sql new file mode 100644 index 000000000..8caa89a72 --- /dev/null +++ b/module-system/module-implementations/sov-nft-module/src/init_db.sql @@ -0,0 +1,47 @@ +-- Drop existing tables if they exist +DROP TABLE IF EXISTS top_owners CASCADE; +DROP TABLE IF EXISTS nfts CASCADE; +DROP TABLE IF EXISTS collections CASCADE; + +-- Create collection table +CREATE TABLE collections ( + collection_address TEXT PRIMARY KEY, + collection_name TEXT NOT NULL, + creator_address TEXT NOT NULL, + frozen BOOLEAN NOT NULL, + metadata_url TEXT, + supply BIGINT NOT NULL +); + +-- Create index on creator_address to quickly find collections by a creator +CREATE INDEX idx_creator ON collections(creator_address); + +-- Create nft table +CREATE TABLE nfts ( + collection_address TEXT NOT NULL, + nft_id BIGINT NOT NULL, + metadata_url TEXT, + owner TEXT NOT NULL, + frozen BOOLEAN NOT NULL, + PRIMARY KEY (collection_address, nft_id) +); + +-- Create index on owner to quickly find NFTs owned by a particular address +CREATE INDEX idx_nft_owner ON nfts(owner); + +-- Create index on collection_address to quickly find NFTs belonging to a particular collection +CREATE INDEX idx_nft_collection ON nfts(collection_address); + +-- Create top_owners table +CREATE TABLE top_owners ( + owner TEXT NOT NULL, + collection_address TEXT NOT NULL, + count BIGINT NOT NULL, + PRIMARY KEY (owner, collection_address) +); + +-- Create index on collection_address to quickly find top owners in a particular collection +CREATE INDEX idx_top_owners_collection ON top_owners(collection_address); + +-- Create index on count to quickly find top owners by count (optional) +CREATE INDEX idx_top_owners_count ON top_owners(count); diff --git a/module-system/module-implementations/sov-nft-module/src/lib.rs b/module-system/module-implementations/sov-nft-module/src/lib.rs index 3470fec3f..7096c2ffe 100644 --- a/module-system/module-implementations/sov-nft-module/src/lib.rs +++ b/module-system/module-implementations/sov-nft-module/src/lib.rs @@ -15,6 +15,7 @@ mod query; #[cfg(feature = "native")] pub use query::*; use sov_modules_api::{CallResponse, Context, Error, Module, ModuleInfo, StateMap, WorkingSet}; +mod offchain; /// Utility functions. pub mod utils; diff --git a/module-system/module-implementations/sov-nft-module/src/offchain.rs b/module-system/module-implementations/sov-nft-module/src/offchain.rs new file mode 100644 index 000000000..01cc2c654 --- /dev/null +++ b/module-system/module-implementations/sov-nft-module/src/offchain.rs @@ -0,0 +1,168 @@ +#[cfg(feature = "offchain")] +use postgres::NoTls; +use sov_modules_macros::offchain; + +#[cfg(feature = "offchain")] +use crate::utils::get_collection_address; +use crate::{Collection, CollectionAddress, Nft}; + +// CREATE TABLE collection ( +// collection_address TEXT PRIMARY KEY, +// collection_name TEXT NOT NULL, +// creator_address TEXT NOT NULL, +// frozen BOOLEAN NOT NULL, +// metadata_url TEXT, +// supply BIGINT NOT NULL +// ); + +#[offchain] +pub fn track_collection(collection: &Collection) { + // data extraction + let collection_name = collection.get_name(); + let creator_address = collection.get_creator(); + let frozen = collection.is_frozen(); + let metadata_url = collection.get_collection_uri(); + let supply = collection.get_supply(); + let collection_address: CollectionAddress = + get_collection_address(collection_name, creator_address.as_ref()); + let collection_address_str = collection_address.to_string(); + let creator_address_str = creator_address.to_string(); + // postgres insert + tokio::task::block_in_place(|| { + if let Ok(conn_string) = std::env::var("POSTGRES_CONNECTION_STRING") { + match postgres::Client::connect(&conn_string, NoTls) { + Ok(mut client) => { + let result = client.execute( + "INSERT INTO collections (\ + collection_address, collection_name, creator_address,\ + frozen, metadata_url, supply)\ + VALUES ($1, $2, $3, $4, $5, $6)\ + ON CONFLICT (collection_address)\ + DO UPDATE SET collection_name = EXCLUDED.collection_name,\ + creator_address = EXCLUDED.creator_address,\ + frozen = EXCLUDED.frozen,\ + metadata_url = EXCLUDED.metadata_url,\ + supply = EXCLUDED.supply", + &[ + &collection_address_str, + &collection_name, + &creator_address_str, + &frozen, + &metadata_url, + &(supply as i64), + ], + ); + if let Err(e) = result { + println!("Failed to execute query: {}", e); + } + } + Err(e) => { + println!("Failed to connect to the database: {}", e); + } + } + } else { + println!("Environment variable POSTGRES_CONNECTION_STRING is not set"); + } + }) +} + +// CREATE TABLE nft ( +// collection_address TEXT NOT NULL, +// nft_id BIGINT NOT NULL, +// metadata_url TEXT, +// owner TEXT NOT NULL, +// frozen BOOLEAN NOT NULL, +// PRIMARY KEY (collection_address, nft_id) +// ); + +#[offchain] +pub fn track_nft(nft: &Nft) { + let collection_address = nft.get_collection_address().to_string(); + let nft_id = nft.get_token_id(); + let owner = nft.get_owner().to_string(); + let frozen = nft.is_frozen(); + let metadata_url = nft.get_token_uri(); + tokio::task::block_in_place(|| { + if let Ok(conn_string) = std::env::var("POSTGRES_CONNECTION_STRING") { + match postgres::Client::connect(&conn_string, NoTls) { + Ok(mut client) => { + let result = client.execute( + "INSERT INTO nfts (\ + collection_address, nft_id, metadata_url,\ + owner, frozen)\ + VALUES ($1, $2, $3, $4, $5)\ + ON CONFLICT (collection_address, nft_id)\ + DO UPDATE SET metadata_url = EXCLUDED.metadata_url,\ + owner = EXCLUDED.owner,\ + frozen = EXCLUDED.frozen", + &[ + &collection_address, + &(nft_id as i64), + &metadata_url, + &owner, + &frozen, + ], + ); + if let Err(e) = result { + println!("Failed to execute query: {}", e); + } + } + Err(e) => { + println!("Failed to connect to the database: {}", e); + } + } + } else { + println!("Environment variable POSTGRES_CONNECTION_STRING is not set"); + } + }) +} + +// CREATE TABLE top_owners ( +// owner TEXT NOT NULL, +// collection_address TEXT NOT NULL, +// count BIGINT NOT NULL, +// PRIMARY KEY (owner, collection_address) +// ); + +#[offchain] +pub fn update_top_owners( + collection_address: &CollectionAddress, + owner_count_incr: Option<&[(String, u64)]>, + owner_count_decr: Option<&[(String, u64)]>, +) { + let collection_address_str = collection_address.to_string(); + tokio::task::block_in_place(|| { + if let Ok(conn_string) = std::env::var("POSTGRES_CONNECTION_STRING") { + let mut client = postgres::Client::connect(&conn_string, NoTls).unwrap(); + + // Handle increments if provided + if let Some(increments) = owner_count_incr { + for (owner, increment_value) in increments { + let result = client.execute( + "INSERT INTO top_owners (owner, collection_address, count) VALUES ($1, $2, $3) \ + ON CONFLICT (owner, collection_address) \ + DO UPDATE SET count = top_owners.count + EXCLUDED.count", + &[&&owner, &collection_address_str, &(*increment_value as i64)], + ); + if let Err(e) = result { + eprintln!("Failed to execute query: {}", e); + } + } + } + + // Handle decrements if provided + if let Some(decrements) = owner_count_decr { + for (owner, decrement_value) in decrements { + let result = client.execute( + "UPDATE top_owners SET count = count - $3 \ + WHERE owner = $1 AND collection_address = $2 AND count >= $3", + &[&owner, &collection_address_str, &(*decrement_value as i64)], + ); + if let Err(e) = result { + eprintln!("Failed to execute query: {}", e); + } + } + } + } + }) +} diff --git a/module-system/sov-modules-api/src/transaction.rs b/module-system/sov-modules-api/src/transaction.rs index 1810fbd86..bad6da527 100644 --- a/module-system/sov-modules-api/src/transaction.rs +++ b/module-system/sov-modules-api/src/transaction.rs @@ -6,7 +6,9 @@ use crate::PrivateKey; use crate::{Context, Signature}; /// A Transaction object that is compatible with the module-system/sov-default-stf. -#[derive(Debug, PartialEq, Eq, Clone, borsh::BorshDeserialize, borsh::BorshSerialize)] +#[derive( + Debug, PartialEq, Eq, Clone, borsh::BorshDeserialize, borsh::BorshSerialize, serde::Serialize, +)] pub struct Transaction { signature: C::Signature, pub_key: C::PublicKey, diff --git a/module-system/sov-modules-macros/src/lib.rs b/module-system/sov-modules-macros/src/lib.rs index 4db6d96af..f44b6cda4 100644 --- a/module-system/sov-modules-macros/src/lib.rs +++ b/module-system/sov-modules-macros/src/lib.rs @@ -18,6 +18,7 @@ mod manifest; mod module_call_json_schema; mod module_info; mod new_types; +mod offchain; #[cfg(feature = "native")] mod rpc; @@ -29,10 +30,11 @@ use dispatch::genesis::GenesisMacro; use dispatch::message_codec::MessageCodec; use module_call_json_schema::derive_module_call_json_schema; use new_types::address_type_helper; +use offchain::offchain_generator; use proc_macro::TokenStream; #[cfg(feature = "native")] use rpc::ExposeRpcMacro; -use syn::{parse_macro_input, DeriveInput}; +use syn::{parse_macro_input, DeriveInput, ItemFn}; #[proc_macro_derive(ModuleInfo, attributes(state, module, address))] pub fn module_info(input: TokenStream) -> TokenStream { @@ -263,3 +265,9 @@ pub fn address_type(_attr: TokenStream, item: TokenStream) -> TokenStream { let input = parse_macro_input!(item as DeriveInput); handle_macro_error(address_type_helper(input)) } + +#[proc_macro_attribute] +pub fn offchain(_attr: TokenStream, item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as ItemFn); + handle_macro_error(offchain_generator(input)) +} diff --git a/module-system/sov-modules-macros/src/offchain.rs b/module-system/sov-modules-macros/src/offchain.rs new file mode 100644 index 000000000..274021ad9 --- /dev/null +++ b/module-system/sov-modules-macros/src/offchain.rs @@ -0,0 +1,31 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::ItemFn; + +pub fn offchain_generator(function: ItemFn) -> Result { + let visibility = &function.vis; + let name = &function.sig.ident; + let inputs = &function.sig.inputs; + let output = &function.sig.output; + let block = &function.block; + let generics = &function.sig.generics; + let where_clause = &function.sig.generics.where_clause; + let asyncness = &function.sig.asyncness; + + let output = quote! { + // The "real" function + #[cfg(feature = "offchain")] + #visibility #asyncness fn #name #generics(#inputs) #output #where_clause { + #block + } + + // The no-op function + #[cfg(not(feature = "offchain"))] + #[allow(unused_variables)] + #visibility #asyncness fn #name #generics(#inputs) #output #where_clause { + // Do nothing. Should be optimized away + } + }; + + Ok(output.into()) +} diff --git a/utils/nft-utils/Cargo.toml b/utils/nft-utils/Cargo.toml new file mode 100644 index 000000000..3ef68cfb3 --- /dev/null +++ b/utils/nft-utils/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "nft-utils" +authors = { workspace = true } +description = "Utils for NFTs" +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +rust-version = { workspace = true } +version = { workspace = true } +readme = "README.md" +resolver = "2" +autotests = false + +[dependencies] +anyhow = { workspace = true } +borsh = { workspace = true, features = ["rc"] } +serde = { workspace = true} +schemars = { workspace = true} +serde_json = { workspace = true} +jsonrpsee = { workspace = true, features = ["macros", "client-core", "server"]} + +sov-nft-module = { path = "../../module-system/module-implementations/sov-nft-module", features = ["native"]} +sov-modules-macros = {path = "../../module-system/sov-modules-macros"} +sov-sequencer = { path = "../../full-node/sov-sequencer"} +demo-stf = {path = "../../examples/demo-stf"} +sov-rollup-interface = {path = "../../rollup-interface"} + +sov-modules-api = {path = "../../module-system/sov-modules-api", features = ["native"]} +sov-cli = {path = "../../module-system/sov-cli"} +tokio = {version = "1.32.0"} \ No newline at end of file diff --git a/utils/nft-utils/src/main.rs b/utils/nft-utils/src/main.rs new file mode 100644 index 000000000..5799c620a --- /dev/null +++ b/utils/nft-utils/src/main.rs @@ -0,0 +1,217 @@ +use std::thread; +use std::time::Duration; + +use borsh::ser::BorshSerialize; +use demo_stf::runtime::RuntimeCall; +use sov_modules_api::default_context::DefaultContext; +use sov_modules_api::default_signature::private_key::DefaultPrivateKey; +use sov_modules_api::transaction::Transaction; +use sov_modules_api::{Address, PrivateKey}; +use sov_nft_module::utils::get_collection_address; +use sov_nft_module::{CallMessage, CollectionAddress, UserAddress}; +use sov_rollup_interface::mocks::MockDaSpec; +use sov_sequencer::utils::SimpleClient; + +const COLLECTION_1: &str = "Sovereign Squirrel Syndicate"; +const COLLECTION_2: &str = "Celestial Dolphins"; +const COLLECTION_3: &str = "Risky Rhinos"; + +const DUMMY_URL: &str = "http://foobar.storage"; + +const PK1: [u8; 32] = [ + 199, 23, 116, 41, 227, 173, 69, 178, 7, 24, 164, 151, 88, 149, 52, 187, 102, 167, 163, 248, 38, + 86, 207, 66, 87, 81, 56, 66, 211, 150, 208, 155, +]; +const PK2: [u8; 32] = [ + 92, 136, 187, 3, 235, 27, 9, 215, 232, 93, 24, 78, 85, 255, 234, 60, 152, 21, 139, 246, 151, + 129, 152, 227, 231, 204, 38, 84, 159, 129, 71, 143, +]; +const PK3: [u8; 32] = [ + 233, 139, 68, 72, 169, 252, 229, 117, 72, 144, 47, 191, 13, 42, 32, 107, 190, 52, 102, 210, + 161, 208, 245, 116, 93, 84, 37, 87, 171, 44, 30, 239, +]; + +fn get_collection_metadata_url(collection_address: &str) -> String { + format!("{}/collection/{}", DUMMY_URL, collection_address) +} + +fn get_nft_metadata_url(collection_address: &str, nft_id: u64) -> String { + format!("{}/nft/{}/{}", DUMMY_URL, collection_address, nft_id) +} + +async fn create_collection( + signer: &DefaultPrivateKey, + nonce: u64, + collection_name: &str, +) -> Transaction { + let collection_address = get_collection_address::( + collection_name, + signer.default_address().as_ref(), + ); + + let collection_uri = get_collection_metadata_url(&collection_address.to_string()); + let create_collection_message = RuntimeCall::::nft( + CallMessage::::CreateCollection { + name: collection_name.to_string(), + collection_uri, + }, + ); + Transaction::::new_signed_tx( + signer, + create_collection_message.try_to_vec().unwrap(), + nonce, + ) +} + +async fn mint_nft( + signer: &DefaultPrivateKey, + nonce: u64, + collection_name: &str, + token_id: u64, + owner: &Address, +) -> Transaction { + let collection_address = get_collection_address::( + collection_name, + signer.default_address().as_ref(), + ); + let token_uri = get_nft_metadata_url(&collection_address.to_string(), token_id); + let mint_nft_message = + RuntimeCall::::nft(CallMessage::::MintNft { + collection_name: collection_name.to_string(), + token_uri, + token_id, + owner: UserAddress::new(owner), + frozen: false, + }); + Transaction::::new_signed_tx( + signer, + mint_nft_message.try_to_vec().unwrap(), + nonce, + ) +} + +async fn transfer_nft( + signer: &DefaultPrivateKey, + nonce: u64, + collection_address: &CollectionAddress, + token_id: u64, + to: &Address, +) -> Transaction { + let transfer_message = RuntimeCall::::nft(CallMessage::< + DefaultContext, + >::TransferNft { + collection_address: collection_address.clone(), + token_id, + to: UserAddress::new(to), + }); + Transaction::::new_signed_tx( + signer, + transfer_message.try_to_vec().unwrap(), + nonce, + ) +} + +#[tokio::main] +async fn main() { + let creator_pk = DefaultPrivateKey::try_from(&PK1[..]).unwrap(); + let owner_1_pk = DefaultPrivateKey::try_from(&PK2[..]).unwrap(); + let owner_2_pk = DefaultPrivateKey::try_from(&PK3[..]).unwrap(); + + let client = SimpleClient::new("localhost", 12345).await.unwrap(); + + let mut nonce = 0; + let mut nft_id = 1; + let mut transactions = vec![]; + transactions.push(create_collection(&creator_pk, nonce, COLLECTION_1).await); + nonce += 1; + transactions.push(create_collection(&creator_pk, nonce, COLLECTION_2).await); + nonce += 1; + transactions.push(create_collection(&creator_pk, nonce, COLLECTION_3).await); + + client.send_transactions(transactions, None).await.unwrap(); + // sleep is necessary because of how the sequencer currently works + // without the sleep, there is a concurrency issue and some transactions would be ignored + thread::sleep(Duration::from_millis(1000)); + + let mut transactions = vec![]; + for _ in 0..15 { + nonce += 1; + transactions.push( + mint_nft( + &creator_pk, + nonce, + COLLECTION_1, + nft_id, + &owner_1_pk.default_address(), + ) + .await, + ); + nft_id += 1; + } + + client.send_transactions(transactions, None).await.unwrap(); + thread::sleep(Duration::from_millis(1000)); + + let mut transactions = vec![]; + for _ in 0..5 { + nonce += 1; + transactions.push( + mint_nft( + &creator_pk, + nonce, + COLLECTION_1, + nft_id, + &owner_2_pk.default_address(), + ) + .await, + ); + nft_id += 1; + } + + let mut nft_id = 1; + for _ in 0..20 { + nonce += 1; + transactions.push( + mint_nft( + &creator_pk, + nonce, + COLLECTION_2, + nft_id, + &owner_1_pk.default_address(), + ) + .await, + ); + nft_id += 1; + } + + client.send_transactions(transactions, None).await.unwrap(); + thread::sleep(Duration::from_millis(1000)); + + let mut transactions = vec![]; + + let mut owner_1_nonce = 0; + let mut nft_id = 1; + let collection_1_address = get_collection_address::( + COLLECTION_1, + creator_pk.default_address().as_ref(), + ); + + #[allow(clippy::explicit_counter_loop)] + for _ in 1..7 { + let new_owner = DefaultPrivateKey::generate().default_address(); + transactions.push( + transfer_nft( + &owner_1_pk, + owner_1_nonce, + &collection_1_address, + nft_id, + &new_owner, + ) + .await, + ); + owner_1_nonce += 1; + nft_id += 1; + } + + client.send_transactions(transactions, None).await.unwrap(); +} From 3b9b5478b6b45923104dd62942ceb275c836c8c3 Mon Sep 17 00:00:00 2001 From: Filippo Neysofu Costa Date: Mon, 25 Sep 2023 12:48:39 +0200 Subject: [PATCH 02/34] BlockScout integration fixes (#917) * Fixes * retesteth config Signed-off-by: Filippo Costa --------- Signed-off-by: Filippo Costa --- examples/demo-stf/src/genesis_config.rs | 1 + .../sov-evm/src/query.rs | 4 +- scripts/blockscout/.env | 8 + scripts/retesteth/README.md | 1 + scripts/retesteth/config.json | 267 ++++++++++++++++++ 5 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 scripts/blockscout/.env create mode 100644 scripts/retesteth/README.md create mode 100644 scripts/retesteth/config.json diff --git a/examples/demo-stf/src/genesis_config.rs b/examples/demo-stf/src/genesis_config.rs index 935a9afa5..f0fe2b110 100644 --- a/examples/demo-stf/src/genesis_config.rs +++ b/examples/demo-stf/src/genesis_config.rs @@ -130,6 +130,7 @@ fn get_evm_config(genesis_addresses: Vec) -> EvmConfig chain_id: 1, limit_contract_code_size: None, spec: vec![(0, SpecId::SHANGHAI)].into_iter().collect(), + block_timestamp_delta: 1u64, ..Default::default() } } diff --git a/module-system/module-implementations/sov-evm/src/query.rs b/module-system/module-implementations/sov-evm/src/query.rs index 6446ce483..5df95006f 100644 --- a/module-system/module-implementations/sov-evm/src/query.rs +++ b/module-system/module-implementations/sov-evm/src/query.rs @@ -46,6 +46,7 @@ impl Evm { .last(&mut working_set.accessory_state()) .expect("Head block must be set"), Some(ref block_number) => { + let block_number = block_number.strip_prefix("0x").unwrap_or(block_number); let block_number = usize::from_str_radix(block_number, 16).expect("Block number must be hex"); self.blocks @@ -122,9 +123,10 @@ impl Evm { }; // Build rpc block response + let total_difficulty = Some(block.header.difficulty); let block = reth_rpc_types::Block { header, - total_difficulty: Default::default(), + total_difficulty, uncles: Default::default(), transactions, size: Default::default(), diff --git a/scripts/blockscout/.env b/scripts/blockscout/.env new file mode 100644 index 000000000..12e1817e8 --- /dev/null +++ b/scripts/blockscout/.env @@ -0,0 +1,8 @@ +DATABASE_URL="postgresql://postgres:foobar@localhost:5432/blockscout" +ETHEREUM_JSONRPC_HTTP_URL="http://127.0.0.1:12345" +ETHEREUM_JSONRPC_VARIANT="ganache" +COIN="sovereign" +MIX_ENV="dev" +MICROSERVICE_SC_VERIFIER_URL="http://0.0.0.0:8050/" +DISABLE_INDEXER="false" +LOG_LEVEL="trace" diff --git a/scripts/retesteth/README.md b/scripts/retesteth/README.md new file mode 100644 index 000000000..954938aee --- /dev/null +++ b/scripts/retesteth/README.md @@ -0,0 +1 @@ +`config.json` is the WIP configuration file for using `retesteth` with `sov-evm`. It's based on the configuration options of `evmone` and must be extensively updated to work with `sov-evm`, it's broken as of now. Nonetheless, it's committed into version control to share progress across `sov-evm` maintainers. diff --git a/scripts/retesteth/config.json b/scripts/retesteth/config.json new file mode 100644 index 000000000..9de2644cd --- /dev/null +++ b/scripts/retesteth/config.json @@ -0,0 +1,267 @@ +{ + "name": "EVMONE on StateTool", + "socketType": "tranition-tool", + "socketAddress": "start.sh", + "initializeTime": "0", + "checkDifficulty": true, + "calculateDifficulty": false, + "checkBasefee": true, + "calculateBasefee": false, + "checkLogsHash": true, + "support1559": true, + "supportBigint": false, + "transactionsAsJson": true, + "tmpDir": "/dev/shm", + "defaultChainID": 1, + "customCompilers": { + ":yul": "yul.sh" + }, + "forks": [ + "Frontier", + "Homestead", + "EIP150", + "EIP158", + "Byzantium", + "Constantinople", + "ConstantinopleFix", + "Istanbul", + "Berlin", + "London", + "Merge", + "Shanghai" + ], + "additionalForks": [ + "FrontierToHomesteadAt5", + "HomesteadToEIP150At5", + "EIP158ToByzantiumAt5", + "HomesteadToDaoAt5", + "ByzantiumToConstantinopleFixAt5", + "BerlinToLondonAt5", + "ArrowGlacier", + "ArrowGlacierToMergeAtDiffC0000", + "GrayGlacier", + "MergeToShanghaiAtTime15k" + ], + "fillerSkipForks": [], + "exceptions": { + "PYSPECS_EXCEPTIONS": "", + "Transaction without funds": "insufficient funds for gas * price + value", + "AddressTooShort": "input string too short for common.Address", + "AddressTooLong": "rlp: input string too long for common.Address, decoding into (types.Transaction)(types.LegacyTx).To", + "NonceMax": "nonce exceeds 2^64-1", + "NonceTooLong": "rlp: input string too long for uint64, decoding into (types.Transaction)(types.LegacyTx).Nonce", + "InvalidVRS": "invalid transaction v, r, s values", + "InvalidV": "rlp: expected input string or byte for *big.Int, decoding into (types.Transaction)(types.LegacyTx).V", + "InvalidR": "rlp: expected input string or byte for *big.Int, decoding into (types.Transaction)(types.LegacyTx).R", + "InvalidS": "rlp: expected input string or byte for *big.Int, decoding into (types.Transaction)(types.LegacyTx).S", + "InvalidChainID": "invalid chain id for signer", + "ECRecoveryFail": "recovery failed", + "ExtraDataTooBig": "Error importing raw rlp block: Header extraData > 32 bytes", + "InvalidData": "rlp: expected input string or byte for []uint8, decoding into (types.Transaction)(types.LegacyTx).Data", + "InvalidDifficulty": "Invalid difficulty:", + "InvalidDifficulty2": "Error in field: difficulty", + "InvalidWithdrawals": "Error in field: withdrawalsRoot", + "InvalidDifficulty_TooLarge": "Blockheader parse error: VALUE >u256", + "InvalidGasLimit": "Header gasLimit > 0x7fffffffffffffff", + "InvalidGasLimit2": "Invalid gaslimit:", + "InvalidGasLimit3": "GasLimit must be < 0x7fffffffffffffff", + "InvalidGasLimit4": "rlp: input string too long for uint64, decoding into (types.Transaction)(types.LegacyTx).Gas", + "InvalidGasLimit5": "rlp: expected input string or byte for uint64, decoding into (types.Transaction)(types.LegacyTx).Gas", + "InvalidValue": "value exceeds 256 bits", + "InvalidGasPrice": "gasPrice exceeds 256 bits", + "InvalidMaxPriorityFeePerGas": "maxPriorityFeePerGas exceeds 256 bits", + "InvalidMaxFeePerGas": "maxFeePerGas exceeds 256 bits", + "InvalidNonce": "rlp: expected input string or byte for uint64, decoding into (types.Transaction)(types.LegacyTx).Nonce", + "InvalidTo": "rlp: expected input string or byte for common.Address, decoding into (types.Transaction)(types.LegacyTx).To", + "GasLimitPriceProductOverflow": "gas * gasPrice exceeds 256 bits", + "TooMuchGasUsed": "Invalid gasUsed:", + "TooMuchGasUsed2": "Error importing raw rlp block: t8ntool didn't return a transaction with hash", + "LeadingZerosGasLimit": "rlp: non-canonical integer (leading zero bytes) for uint64, decoding into (types.Transaction)(types.LegacyTx).Gas", + "LeadingZerosGasPrice": "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.LegacyTx).GasPrice", + "LeadingZerosValue": "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.LegacyTx).Value", + "LeadingZerosNonce": "rlp: non-canonical integer (leading zero bytes) for uint64, decoding into (types.Transaction)(types.LegacyTx).Nonce", + "LeadingZerosR": "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.LegacyTx).R", + "LeadingZerosS": "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.LegacyTx).S", + "LeadingZerosV": "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.LegacyTx).V", + "LeadingZerosDataSize": "rlp: non-canonical size information for []uint8, decoding into (types.Transaction)(types.LegacyTx).Data", + "LeadingZerosNonceSize": "rlp: non-canonical size information for uint64, decoding into (types.Transaction)(types.LegacyTx).Nonce", + "InvalidNumber": "BlockHeader number != parent.number + 1", + "InvalidTimestampEqualParent": "timestamp equals parent's", + "InvalidTimestampOlderParent": "BlockHeader timestamp is less or equal then it's parent block!", + "InvalidLogBloom": "Error in field: bloom", + "InvalidStateRoot": "Error in field: stateRoot", + "InvalidGasUsed": "Error in field: gasUsed", + "InvalidGasUsed2": "t8ntool didn't return a transaction with hash", + "InvalidBlockMixHash": "invalid mix digest", + "InvalidBlockNonce": "", + "UnknownParent": "unknown parent hash", + "UnknownParent2": "unknown parent hash", + "InvalidReceiptsStateRoot": "Error in field: receiptTrie", + "InvalidTransactionsRoot": "Error in field: transactionsTrie", + "InvalidUnclesHash": "Error in field: uncleHash", + "InvalidUncleParentHash": "Parent block hash not found:", + "UncleInChain": "Block is already in chain!", + "UncleIsAncestor": "Block is already in chain!", + "UncleParentIsNotAncestor": "Uncle number is wrong!", + "TooManyUncles": "Too many uncles!", + "UncleIsBrother": "Uncle is brother!", + "OutOfGas": "out of gas", + "SenderNotEOA": "sender not an eoa:", + "IntrinsicGas": "intrinsic gas too low:", + "ExtraDataIncorrectDAO": "BlockHeader require Dao ExtraData!", + "InvalidTransactionVRS": "t8ntool didn't return a transaction with hash", + "BLOCKHEADER_VALUE_TOOLARGE": "Blockheader parse error: VALUE >u256", + "TRANSACTION_VALUE_TOOLARGE": "TransactionLegacy convertion error: VALUE >u256", + "TRANSACTION_VALUE_TOOSHORT": "t8ntool didn't return a transaction with hash", + "TR_NonceHasMaxValue": "nonce has max value:", + "OVERSIZE_RLP": "Error importing raw rlp block: OversizeRLP", + "RLP_BadCast": "BadCast", + "RLP_ExpectedAsList": "expected to be list", + "RLP_TooFewElements": "rlp: too few elements ", + "RLP_TooManyElements": "rlp: input list has too many elements ", + "RLP_InputContainsMoreThanOneValue": "Error importing raw rlp block: OversizeRLP", + "RLP_VALUESIZE_MORE_AVAILABLEINPUTLENGTH": "Error importing raw rlp block: UndersizeRLP", + "RLP_ELEMENT_LARGER_CONTAININGLIST_UNDERSIZE": "Error importing raw rlp block: UndersizeRLP", + "RLP_ELEMENT_LARGER_CONTAININGLIST_OVERSIZE": "Error importing raw rlp block: OversizeRLP", + "RLP_ExpectedInputList_EXTBLOCK": "Error importing raw rlp block: RLP is expected to be list", + "RLP_InvalidArg0_UNMARSHAL_BYTES": "Error importing raw rlp block: BadCast", + "RLP_ExpectedInputList_HEADER_DECODEINTO_BLOCK_EXTBLOCK": "Error importing raw rlp block: BlockHeader RLP is expected to be list", + "RLP_InputList_TooManyElements_HEADER_DECODEINTO_BLOCK_EXTBLOCK_HEADER": "Error importing raw rlp block: Uncleheader RLP is expected to be list", + "RLP_InputList_TooManyElements_TXDATA_DECODEINTO_BLOCK_EXTBLOCK_TXS0": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE2": "Blockheader parse error: Key `coinbase` is not hash20", + "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TXS0_RECIPIENT": "TransactionLegacy convertion error: Key `to` is not hash20", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT2": "Blockheader parse error: Key `stateRoot` is not hash32", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST2": "Blockheader parse error: Key `mixHash` is not hash32", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH2": "Blockheader parse error: Key `parentHash` is not hash32", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH2": "Blockheader parse error: Key `receiptTrie` is not hash32", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH": "Blockheader parse error: Key `transactionsTrie` is not hash32", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH2": "Blockheader parse error: Key `uncleHash` is not hash32", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT": "Blockheader parse error: VALUE >u256", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED": "Blockheader parse error: VALUE >u256", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME": "Blockheader parse error: VALUE >u256", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT": "TransactionLegacy convertion error: VALUE >u256", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH2": "Blockheader parse error: Key `receiptTrie` is not hash32", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT": "Blockheader parse error: Key `stateRoot` is not hash32", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST2": "Blockheader parse error: Key `mixHash` is not hash32", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH2": "Blockheader parse error: Key `parentHash` is not hash32", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH2": "Blockheader parse error: Key `uncleHash` is not hash32", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH2": "Blockheader parse error: Key `transactionsTrie` is not hash32", + "RLP_InputString_TooShort_BLOOM_DECODEINTO_BLOCK_EXTBLOCK_HEADER_BLOOM": "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY": "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY2": "Blockheader parse error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT": "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT2": "Blockheader parse error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED": "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED2": "Blockheader parse error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME": "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME2": "Blockheader parse error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT": "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT2": "TransactionLegacy convertion error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NUMBER": "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NUMBER2": "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_PRICE": "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_R": "TransactionLegacy convertion error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_S": "TransactionLegacy convertion error: VALUE has leading 0", + "RLP_InputString_TooLong_BLOOM_DECODEINTO_BLOCK_EXTBLOCK_HEADER_BLOOM": "Blockheader parse error: Key `bloom` is not hash256", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TX0_RECIPIENT": "Error importing raw rlp block: Transaction RLP field is not data!", + "RLP_InputString_TooLong_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE": "Blockheader parse error: Key `coinbase` is not hash20", + "RLP_InputString_TooLong_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TXS0_RECIPIENT": "TransactionLegacy convertion error: Key `to` is not hash20", + "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXR": "Error importing raw rlp block: Transaction RLP field is not data!", + "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXS": "Error importing raw rlp block: Transaction RLP field is not data!", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT": "Error importing raw rlp block: Transaction RLP field is not data!", + "RLP_ExpectedInputString_NONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE": "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_UINT8_DECODEINTO_BLOCK_EXTBLOCK_TXS0_PAYLOAD": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_BLOCKNONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE": "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_BLOCKNONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE2": "Blockheader parse error: Key `nonce` is not hash8", + "RLP_NonCanonical_SizeInfo_EXTBLOCK": "Error importing raw rlp block: BadRLP", + "RLP_ExpectedInputList_TRANSACTION_DECODEINTO_BLOCK_EXTBLOCK_TXS": "Error importing raw rlp block: BadCast", + "RLP_ExpectedInputList_HEADER_DECODEINTO_BLOCK_EXTBLOCK_UNCLES": "Error importing raw rlp block: OversizeRLP", + "RLP_ExpectedInputList_TXDATA_DECODEINTO_BLOCK_EXTBLOCK_TXS0": "Error importing raw rlp block: Transaction RLP is expected to be list", + "RLP_Error_EOF": "ERROR(11): unexpected EOF", + "RLP_Error_RLP_Size": "ERROR(11): rlp: value size exceeds available input length", + "RLP_Error_Size_Information": "ERROR(11): rlp: non-canonical size information", + "LegacyBlockImportImpossible": "Legacy block import is impossible", + "LegacyBlockImportImpossible2": "Legacy block can only be on top of LegacyBlock", + "LegacyBlockBaseFeeTransaction": "BaseFee transaction in a Legacy blcok", + "1559BlockImportImpossible_HeaderIsLegacy": "1559 block must be on top of 1559", + "1559BlockImportImpossible_BaseFeeWrong": "Error in field: baseFeePerGas", + "1559BlockImportImpossible_InitialBaseFeeWrong": "Initial baseFee must be 1000000000", + "1559BlockImportImpossible_TargetGasLow": "gasTarget decreased too much", + "1559BlockImportImpossible_TargetGasHigh": "gasTarget increased too much", + "1559BlockImportImpossible_InitialGasLimitInvalid": "Invalid block1559: Initial gasLimit must be", + "MergeBlockImportImpossible": "Trying to import Merge block on top of Shanghai block after transition", + "ShanghaiBlockImportImpossible": "Trying to import Shanghai block on top of block that is not Shanghai!!", + "TR_IntrinsicGas": "intrinsic gas too low:", + "TR_NoFunds": "insufficient funds for gas * price + value", + "TR_NoFundsX": "insufficient funds for gas * price + value", + "TR_NoFundsValue": "insufficient funds for transfer", + "TR_NoFundsOrGas": "insufficient funds for gas * price + value", + "TR_FeeCapLessThanBlocks": "max fee per gas less than block base fee", + "TR_GasLimitReached": "gas limit reached", + "TR_FeeCapLessThanBlocksORGasLimitReached": "gas limit reached", + "TR_FeeCapLessThanBlocksORNoFunds": "max fee per gas less than block base fee", + "TR_NonceTooHigh": "nonce too high", + "TR_NonceTooLow": "nonce too low", + "TR_TypeNotSupported": "transaction type not supported", + "TR_TipGtFeeCap": "max priority fee per gas higher than max fee per gas", + "TR_TooShort": "typed transaction too short", + "TR_InitCodeLimitExceeded": "max initcode size exceeded", + "1559BaseFeeTooLarge": "TransactionBaseFee convertion error: VALUE >u256", + "1559PriorityFeeGreaterThanBaseFee": "maxFeePerGas \u003c maxPriorityFeePerGas", + "2930AccessListAddressTooLong": "rlp: input string too long for common.Address, decoding into (types.Transaction)(types.AccessListTx).AccessList[0].Address", + "2930AccessListAddressTooShort": "rlp: input string too short for common.Address, decoding into (types.Transaction)(types.AccessListTx).AccessList[0].Address", + "1559LeadingZerosBaseFee": "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.DynamicFeeTx).GasFeeCap", + "1559LeadingZerosPriorityFee": "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.DynamicFeeTx).GasTipCap", + "2930AccessListStorageHashTooShort": "rlp: input string too short for common.Hash, decoding into (types.Transaction)(types.AccessListTx).AccessList[0].StorageKeys[0]", + "2930AccessListStorageHashTooLong": "rlp: input string too long for common.Hash, decoding into (types.Transaction)(types.AccessListTx).AccessList[0].StorageKeys[0]", + "3675PoWBlockRejected": "Invalid block1559: Chain switched to PoS!", + "3675PoSBlockRejected": "Parent (transition) block has not reached TTD", + "3675PreMerge1559BlockRejected": "Trying to import 1559 block on top of PoS block", + "INPUT_UNMARSHAL_ERROR": "cannot unmarshal hex", + "INPUT_UNMARSHAL_SIZE_ERROR": "failed unmarshaling", + "RLP_BODY_UNMARSHAL_ERROR": "Rlp structure is wrong", + "EOF_ConflictingStackHeight": "err: stack_height_mismatch", + "EOF_StackUnderflow": "err: stack_underflow", + "EOF_InvalidCodeTermination": "err: no_terminating_instruction", + "EOF_MaxStackHeightExceeded": "err: max_stack_height_above_limit", + "EOF_UnreachableCode": "err: unreachable_instructions", + "EOF_InvalidCode": "err: invalid_code", + "EOF_TruncatedImmediate": "err: truncated_instruction", + "EOF_InvalidJumpDestination": "err: invalid_rjump_destination", + "EOF_InvalidJumpTableCount": "err: invalid_rjumpv_count", + "EOF_TypeSectionMissing": "err: type_section_missing", + "EOF_CodeSectionMissing": "err: code_section_missing", + "EOF_InvalidTypeSectionSize": "err: invalid_type_section_size", + "EOF_InvalidFirstSectionType": "err: invalid_first_section_type", + "EOF_TooManyCodeSections": "err: too_many_code_sections", + "EOF_InvalidCodeSectionIndex": "err: invalid_code_section_index", + "EOF_UndefinedInstruction": "err: undefined_instruction", + "EOF_ZeroSectionSize": "err: zero_section_size", + "EOF_NonEmptyStackOnTerminatingInstruction": "err: non_empty_stack_on_terminating_instruction", + "EOF_InvalidSectionBodiesSize": "err: invalid_section_bodies_size", + "PostMergeUncleHashIsNotEmpty": "block.uncleHash != empty", + "PostMergeDifficultyIsNot0": "block.difficulty must be 0" + } +} From e6cd64a60c5cbfd56201cd2742751bf407f89004 Mon Sep 17 00:00:00 2001 From: Blazej Kolad Date: Mon, 25 Sep 2023 13:32:56 +0200 Subject: [PATCH 03/34] modules_api: simplify public key definition (#918) * simplify publi key definition * private key --- module-system/sov-modules-api/src/lib.rs | 71 ++++++++---------------- 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/module-system/sov-modules-api/src/lib.rs b/module-system/sov-modules-api/src/lib.rs index 948e4fd3d..d0dcc6f1f 100644 --- a/module-system/sov-modules-api/src/lib.rs +++ b/module-system/sov-modules-api/src/lib.rs @@ -138,7 +138,9 @@ pub enum SigVerificationError { } /// Signature used in the Module System. -pub trait Signature { +pub trait Signature: + borsh::BorshDeserialize + borsh::BorshSerialize + Eq + Clone + Debug + Send + Sync +{ type PublicKey; fn verify(&self, pub_key: &Self::PublicKey, msg: &[u8]) -> Result<(), SigVerificationError>; @@ -150,13 +152,22 @@ pub trait Signature { pub enum NonInstantiable {} /// PublicKey used in the Module System. -pub trait PublicKey { +pub trait PublicKey: + borsh::BorshDeserialize + borsh::BorshSerialize + Eq + Hash + Clone + Debug + Send + Sync +{ fn to_address(&self) -> A; } /// A PrivateKey used in the Module System. #[cfg(feature = "native")] -pub trait PrivateKey { +pub trait PrivateKey: + Debug + + Send + + Sync + + for<'a> TryFrom<&'a [u8], Error = anyhow::Error> + + Serialize + + DeserializeOwned +{ type PublicKey: PublicKey; type Signature: Signature; fn generate() -> Self; @@ -199,69 +210,33 @@ pub trait Spec { /// The public key used for digital signatures #[cfg(feature = "native")] - type PublicKey: borsh::BorshDeserialize - + borsh::BorshSerialize - + Eq - + Hash - + Clone - + Debug - + PublicKey + type PublicKey: PublicKey + Serialize + for<'a> Deserialize<'a> + ::schemars::JsonSchema - + Send - + Sync + FromStr; + #[cfg(not(feature = "native"))] + type PublicKey: PublicKey; + /// The public key used for digital signatures #[cfg(feature = "native")] - type PrivateKey: Debug - + Send - + Sync - + for<'a> TryFrom<&'a [u8], Error = anyhow::Error> - + Serialize - + DeserializeOwned - + PrivateKey; - - #[cfg(not(feature = "native"))] - type PublicKey: borsh::BorshDeserialize - + borsh::BorshSerialize - + Eq - + Hash - + Clone - + Debug - + Send - + Sync - + PublicKey; + type PrivateKey: PrivateKey; /// The hasher preferred by the rollup, such as Sha256 or Poseidon. type Hasher: Digest; /// The digital signature scheme used by the rollup #[cfg(feature = "native")] - type Signature: borsh::BorshDeserialize - + borsh::BorshSerialize + type Signature: Signature + + FromStr + Serialize + for<'a> Deserialize<'a> - + schemars::JsonSchema - + Eq - + Clone - + Debug - + Send - + Sync - + FromStr - + Signature; + + schemars::JsonSchema; /// The digital signature scheme used by the rollup #[cfg(not(feature = "native"))] - type Signature: borsh::BorshDeserialize - + borsh::BorshSerialize - + Eq - + Clone - + Debug - + Signature - + Send - + Sync; + type Signature: Signature; /// A structure containing the non-deterministic inputs from the prover to the zk-circuit type Witness: Witness; From e718a55fe374982275de962ec7feb74e4f794785 Mon Sep 17 00:00:00 2001 From: Blazej Kolad Date: Mon, 25 Sep 2023 14:16:51 +0200 Subject: [PATCH 04/34] `EVM`: Add missing docs (#914) * rename pending_block to block_env * make evm internals private * refactor query.rs * AccountData doc * query.rs docs * fix exports * add missing docs * cargo fmt * Add readme * Update README.md * Update README.md * better docs --- examples/demo-rollup/tests/evm/mod.rs | 2 +- examples/demo-stf/src/hooks_impl.rs | 5 +- full-node/sov-ethereum/src/lib.rs | 12 +- .../src/chain_state/helpers.rs | 2 +- .../sov-chain-state/src/hooks.rs | 2 +- .../module-implementations/sov-evm/README.md | 5 + .../sov-evm/src/call.rs | 5 +- .../sov-evm/src/evm/conversions.rs | 2 +- .../sov-evm/src/evm/mod.rs | 5 +- .../sov-evm/src/evm/primitive_types.rs | 4 + .../sov-evm/src/genesis.rs | 2 +- .../sov-evm/src/hooks.rs | 33 +++-- .../module-implementations/sov-evm/src/lib.rs | 61 ++++++++-- .../sov-evm/src/query.rs | 114 ++++++++++-------- .../sov-evm/src/signer/mod.rs | 1 + .../sov-evm/src/tests/genesis_tests.rs | 2 +- .../sov-evm/src/tests/hooks_tests.rs | 4 +- module-system/sov-modules-api/src/hooks.rs | 2 +- .../sov-modules-stf-template/src/lib.rs | 4 +- 19 files changed, 171 insertions(+), 96 deletions(-) create mode 100644 module-system/module-implementations/sov-evm/README.md diff --git a/examples/demo-rollup/tests/evm/mod.rs b/examples/demo-rollup/tests/evm/mod.rs index c164942f0..8141c4ddd 100644 --- a/examples/demo-rollup/tests/evm/mod.rs +++ b/examples/demo-rollup/tests/evm/mod.rs @@ -12,7 +12,7 @@ use ethers_signers::{LocalWallet, Signer, Wallet}; use jsonrpsee::core::client::ClientT; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::rpc_params; -use sov_evm::smart_contracts::SimpleStorageContract; +use sov_evm::SimpleStorageContract; use sov_risc0_adapter::host::Risc0Host; use super::test_helpers::start_rollup; diff --git a/examples/demo-stf/src/hooks_impl.rs b/examples/demo-stf/src/hooks_impl.rs index 156566a73..c3f55d754 100644 --- a/examples/demo-stf/src/hooks_impl.rs +++ b/examples/demo-stf/src/hooks_impl.rs @@ -104,13 +104,12 @@ impl SlotHooks for Runtime { impl FinalizeHook for Runtime { type Context = C; - fn finalize_slot_hook( + fn finalize_hook( &self, #[allow(unused_variables)] root_hash: &<::Storage as Storage>::Root, #[allow(unused_variables)] accessory_working_set: &mut AccessoryWorkingSet, ) { #[cfg(feature = "experimental")] - self.evm - .finalize_slot_hook(root_hash, accessory_working_set); + self.evm.finalize_hook(root_hash, accessory_working_set); } } diff --git a/full-node/sov-ethereum/src/lib.rs b/full-node/sov-ethereum/src/lib.rs index 53e26c90a..21b460e04 100644 --- a/full-node/sov-ethereum/src/lib.rs +++ b/full-node/sov-ethereum/src/lib.rs @@ -3,7 +3,7 @@ mod batch_builder; #[cfg(feature = "experimental")] pub use experimental::{get_ethereum_rpc, Ethereum}; #[cfg(feature = "experimental")] -pub use sov_evm::signer::DevSigner; +pub use sov_evm::DevSigner; #[cfg(feature = "experimental")] pub mod experimental { @@ -21,9 +21,7 @@ pub mod experimental { Address as RethAddress, TransactionSignedNoHash as RethTransactionSignedNoHash, }; use reth_rpc_types::{TransactionRequest, TypedTransactionRequest}; - use sov_evm::call::CallMessage; - use sov_evm::evm::RlpEvmTransaction; - use sov_evm::Evm; + use sov_evm::{CallMessage, Evm, RlpEvmTransaction}; use sov_modules_api::transaction::Transaction; use sov_modules_api::utils::to_jsonrpsee_error_object; use sov_modules_api::{EncodeCall, WorkingSet}; @@ -93,9 +91,9 @@ pub mod experimental { let signed_transaction: RethTransactionSignedNoHash = raw_tx.clone().try_into()?; let tx_hash = signed_transaction.hash(); - let sender = signed_transaction.recover_signer().ok_or( - sov_evm::evm::primitive_types::RawEvmTxConversionError::FailedToRecoverSigner, - )?; + let sender = signed_transaction + .recover_signer() + .ok_or(sov_evm::RawEvmTxConversionError::FailedToRecoverSigner)?; let mut nonces = self.nonces.lock().unwrap(); let nonce = *nonces.entry(sender).and_modify(|n| *n += 1).or_insert(0); diff --git a/module-system/module-implementations/integration-tests/src/chain_state/helpers.rs b/module-system/module-implementations/integration-tests/src/chain_state/helpers.rs index 0e0ac03c4..8405b2568 100644 --- a/module-system/module-implementations/integration-tests/src/chain_state/helpers.rs +++ b/module-system/module-implementations/integration-tests/src/chain_state/helpers.rs @@ -85,7 +85,7 @@ impl SlotHooks for TestRuntime { impl FinalizeHook for TestRuntime { type Context = C; - fn finalize_slot_hook( + fn finalize_hook( &self, _root_hash: &<::Storage as Storage>::Root, _accesorry_working_set: &mut AccessoryWorkingSet, diff --git a/module-system/module-implementations/sov-chain-state/src/hooks.rs b/module-system/module-implementations/sov-chain-state/src/hooks.rs index 07d99231a..f94110662 100644 --- a/module-system/module-implementations/sov-chain-state/src/hooks.rs +++ b/module-system/module-implementations/sov-chain-state/src/hooks.rs @@ -64,7 +64,7 @@ impl SlotHooks for ChainState FinalizeHook for ChainState { type Context = C; - fn finalize_slot_hook( + fn finalize_hook( &self, _root_hash: &::Root, _accesorry_working_set: &mut AccessoryWorkingSet, diff --git a/module-system/module-implementations/sov-evm/README.md b/module-system/module-implementations/sov-evm/README.md new file mode 100644 index 000000000..8c29e3b27 --- /dev/null +++ b/module-system/module-implementations/sov-evm/README.md @@ -0,0 +1,5 @@ +# `sov-evm` module + +The sov-evm module provides compatibility with the EVM. + +The module `CallMessage` contains `rlp` encoded Ethereum transaction, which is validated & executed immediately after being dispatched from the DA. Once all transactions from the DA slot have been processed, they are grouped into an `Ethereum` block. Users can access information such as receipts, blocks, transactions, and more through standard Ethereum endpoints. diff --git a/module-system/module-implementations/sov-evm/src/call.rs b/module-system/module-implementations/sov-evm/src/call.rs index da89f0f1d..09bbf879b 100644 --- a/module-system/module-implementations/sov-evm/src/call.rs +++ b/module-system/module-implementations/sov-evm/src/call.rs @@ -16,8 +16,11 @@ use crate::Evm; derive(serde::Serialize), derive(serde::Deserialize) )] + +/// EVM call message. #[derive(borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq, Clone)] pub struct CallMessage { + /// RLP encoded transaction. pub tx: RlpEvmTransaction, } @@ -30,7 +33,7 @@ impl Evm { ) -> Result { let evm_tx_recovered: TransactionSignedEcRecovered = tx.try_into()?; let block_env = self - .pending_block + .block_env .get(working_set) .expect("Pending block must be set"); 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 bdc40594a..d63fb4adf 100644 --- a/module-system/module-implementations/sov-evm/src/evm/conversions.rs +++ b/module-system/module-implementations/sov-evm/src/evm/conversions.rs @@ -117,7 +117,7 @@ impl From for TransactionSignedEcRecovered { // TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/576 // https://github.com/paradigmxyz/reth/blob/d8677b4146f77c7c82d659c59b79b38caca78778/crates/rpc/rpc/src/eth/revm_utils.rs#L201 -pub fn prepare_call_env(request: CallRequest) -> TxEnv { +pub(crate) fn prepare_call_env(request: CallRequest) -> TxEnv { TxEnv { caller: request.from.unwrap(), gas_limit: request.gas.map(|p| p.try_into().unwrap()).unwrap(), 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 0d28d5e40..e8db4432e 100644 --- a/module-system/module-implementations/sov-evm/src/evm/mod.rs +++ b/module-system/module-implementations/sov-evm/src/evm/mod.rs @@ -9,11 +9,11 @@ pub(crate) mod db; mod db_commit; pub(crate) mod db_init; pub(crate) mod executor; -pub mod primitive_types; +pub(crate) mod primitive_types; #[cfg(test)] mod tests; -pub use conversions::prepare_call_env; +pub(crate) use conversions::prepare_call_env; pub use primitive_types::RlpEvmTransaction; use sov_state::codec::BcsCodec; @@ -83,6 +83,7 @@ pub struct EvmChainConfig { /// Delta to add to parent block timestamp pub block_timestamp_delta: u64, + /// Base fee params. pub base_fee_params: BaseFeeParams, } diff --git a/module-system/module-implementations/sov-evm/src/evm/primitive_types.rs b/module-system/module-implementations/sov-evm/src/evm/primitive_types.rs index 232773151..4dcb6427b 100644 --- a/module-system/module-implementations/sov-evm/src/evm/primitive_types.rs +++ b/module-system/module-implementations/sov-evm/src/evm/primitive_types.rs @@ -107,12 +107,16 @@ pub(crate) struct Receipt { pub(crate) error: Option>, } +/// Tx conversion error. #[derive(Error, Debug)] pub enum RawEvmTxConversionError { + /// Transaction is empty, #[error("Empty raw transaction data")] EmptyRawTransactionData, + /// Decoding error. #[error("Failed to decode signed transaction")] FailedToDecodeSignedTransaction, + /// Unable to recover signer. #[error("Failed to recover signer")] FailedToRecoverSigner, } diff --git a/module-system/module-implementations/sov-evm/src/genesis.rs b/module-system/module-implementations/sov-evm/src/genesis.rs index bc74a8267..bebd4c6ae 100644 --- a/module-system/module-implementations/sov-evm/src/genesis.rs +++ b/module-system/module-implementations/sov-evm/src/genesis.rs @@ -69,7 +69,7 @@ impl Evm { parent_hash: H256::default(), ommers_hash: EMPTY_OMMER_ROOT, beneficiary: config.coinbase, - // This will be set in finalize_slot_hook or in the next begin_slot_hook + // This will be set in finalize_hook or in the next begin_slot_hook state_root: KECCAK_EMPTY, transactions_root: EMPTY_TRANSACTIONS, receipts_root: EMPTY_RECEIPTS, diff --git a/module-system/module-implementations/sov-evm/src/hooks.rs b/module-system/module-implementations/sov-evm/src/hooks.rs index 4add37213..ad0c28427 100644 --- a/module-system/module-implementations/sov-evm/src/hooks.rs +++ b/module-system/module-implementations/sov-evm/src/hooks.rs @@ -10,17 +10,19 @@ impl Evm where ::Root: Into<[u8; 32]>, { + /// Logic executed at the beginning of the slot. Here we set the root hash of the previous head. pub fn begin_slot_hook(&self, da_root_hash: [u8; 32], working_set: &mut WorkingSet) { let parent_block = self .head .get(working_set) .expect("Head block should always be set"); + // TODO // parent_block.header.state_root = root_hash.into(); // self.head.set(&parent_block, working_set); let cfg = self.cfg.get(working_set).unwrap_or_default(); - let new_pending_block = BlockEnv { + let new_pending_env = BlockEnv { number: parent_block.header.number + 1, coinbase: cfg.coinbase, timestamp: parent_block.header.timestamp + cfg.block_timestamp_delta, @@ -31,14 +33,16 @@ where .unwrap(), gas_limit: cfg.block_gas_limit, }; - self.pending_block.set(&new_pending_block, working_set); + self.block_env.set(&new_pending_env, working_set); } + /// Logic executed at the end of the slot. Here, we generate an authenticated block and set it as the new head of the chain. + /// It's important to note that the state root hash is not known at this moment, so we postpone setting this field until the begin_slot_hook of the next slot. pub fn end_slot_hook(&self, working_set: &mut WorkingSet) { let cfg = self.cfg.get(working_set).unwrap_or_default(); - let pending_block = self - .pending_block + let block_env = self + .block_env .get(working_set) .expect("Pending block should always be set"); @@ -50,9 +54,9 @@ where let expected_block_number = parent_block.header.number + 1; assert_eq!( - pending_block.number, expected_block_number, + block_env.number, expected_block_number, "Pending head must be set to block {}, but found block {}", - expected_block_number, pending_block.number + expected_block_number, block_env.number ); let pending_transactions: Vec = @@ -78,11 +82,11 @@ where let header = reth_primitives::Header { parent_hash: parent_block.header.hash, - timestamp: pending_block.timestamp, - number: pending_block.number, + timestamp: block_env.timestamp, + number: block_env.number, ommers_hash: reth_primitives::constants::EMPTY_OMMER_ROOT, beneficiary: parent_block.header.beneficiary, - // This will be set in finalize_slot_hook or in the next begin_slot_hook + // This will be set in finalize_hook or in the next begin_slot_hook state_root: reth_primitives::constants::KECCAK_EMPTY, transactions_root: reth_primitives::proofs::calculate_transaction_root( transactions.as_slice(), @@ -93,9 +97,9 @@ where .iter() .fold(Bloom::zero(), |bloom, r| bloom | r.bloom), difficulty: U256::ZERO, - gas_limit: pending_block.gas_limit, + gas_limit: block_env.gas_limit, gas_used, - mix_hash: pending_block.prevrandao, + mix_hash: block_env.prevrandao, nonce: 0, base_fee_per_gas: parent_block.header.next_block_base_fee(cfg.base_fee_params), extra_data: Bytes::default(), @@ -139,7 +143,12 @@ where self.pending_transactions.clear(working_set); } - pub fn finalize_slot_hook( + /// This logic is executed after calculating the root hash. + /// At this point, it is impossible to alter state variables because the state root is fixed. + /// However, non-state data can be modified. + /// This function's purpose is to add the block to the (non-authenticated) blocks structure, + /// enabling block-related RPC queries. + pub fn finalize_hook( &self, root_hash: &<::Storage as Storage>::Root, accesorry_working_set: &mut AccessoryWorkingSet, diff --git a/module-system/module-implementations/sov-evm/src/lib.rs b/module-system/module-implementations/sov-evm/src/lib.rs index 72d53d582..44b70d2b8 100644 --- a/module-system/module-implementations/sov-evm/src/lib.rs +++ b/module-system/module-implementations/sov-evm/src/lib.rs @@ -1,11 +1,15 @@ +#![deny(missing_docs)] +#![doc = include_str!("../README.md")] #[cfg(feature = "experimental")] -pub mod call; +mod call; #[cfg(feature = "experimental")] -pub mod evm; +mod evm; #[cfg(feature = "experimental")] -pub mod genesis; +mod genesis; #[cfg(feature = "experimental")] -pub mod hooks; +mod hooks; +#[cfg(feature = "experimental")] +pub use {call::*, evm::*, genesis::*, hooks::*, primitive_types::RawEvmTxConversionError}; #[cfg(feature = "native")] #[cfg(feature = "experimental")] mod query; @@ -13,9 +17,13 @@ mod query; #[cfg(feature = "experimental")] pub use query::*; #[cfg(feature = "experimental")] -pub mod signer; +mod signer; +#[cfg(feature = "experimental")] +pub use signer::DevSigner; #[cfg(feature = "smart_contracts")] -pub mod smart_contracts; +mod smart_contracts; +#[cfg(feature = "smart_contracts")] +pub use smart_contracts::SimpleStorageContract; #[cfg(feature = "experimental")] #[cfg(test)] mod tests; @@ -38,36 +46,56 @@ mod experimental { use crate::evm::primitive_types::{ Block, BlockEnv, Receipt, SealedBlock, TransactionSignedAndRecovered, }; + + /// Evm account. #[derive(Clone, Debug)] pub struct AccountData { + /// Account address. pub address: Address, + /// Account balance. pub balance: U256, + /// Code hash. pub code_hash: H256, + /// Smart contract code. pub code: Bytes, + /// Account nonce. pub nonce: u64, } impl AccountData { + /// Empty code hash. pub fn empty_code() -> H256 { KECCAK_EMPTY } + /// Account balance. pub fn balance(balance: u64) -> U256 { U256::from(balance) } } + /// Genesis configuration. #[derive(Clone, Debug)] pub struct EvmConfig { + /// Genesis accounts. pub data: Vec, + /// Chain id. pub chain_id: u64, + /// Limits size of contract code size. pub limit_contract_code_size: Option, + /// List of EVM hardforks by block number pub spec: HashMap, + /// Coinbase where all the fees go pub coinbase: Address, + /// Starting base fee. pub starting_base_fee: u64, + /// Gas limit for single block pub block_gas_limit: u64, + /// Genesis timestamp. pub genesis_timestamp: u64, + /// Delta to add to parent block timestamp, pub block_timestamp_delta: u64, + /// Base fee params. pub base_fee_params: reth_primitives::BaseFeeParams, } @@ -94,50 +122,69 @@ mod experimental { } } + /// The sov-evm module provides compatibility with the EVM. #[allow(dead_code)] // #[cfg_attr(feature = "native", derive(sov_modules_api::ModuleCallJsonSchema))] #[derive(ModuleInfo, Clone)] pub struct Evm { + /// The address of the evm module. #[address] pub(crate) address: C::Address, + /// Mapping from account address to account state. #[state] pub(crate) accounts: sov_modules_api::StateMap, + /// Mapping from code hash to code. Used for lazy-loading code into a contract account. #[state] pub(crate) code: sov_modules_api::StateMap, + /// Chain configuration. This field is set in genesis. #[state] pub(crate) cfg: sov_modules_api::StateValue, + /// Block environment used by the evm. This field is set in `begin_slot_hook`. #[state] - pub(crate) pending_block: sov_modules_api::StateValue, + pub(crate) block_env: sov_modules_api::StateValue, + /// Transactions that will be added to the current block. + /// A valid transaction is added to the vec on every call message. #[state] pub(crate) pending_transactions: sov_modules_api::StateVec, + /// Head of the chain. The new head is set in `end_slot_hook` but without the inclusion of the `state_root` field. + /// The `state_root` is added in `begin_slot_hook` of the next block because its calculation occurs after the `end_slot_hook`. #[state] pub(crate) head: sov_modules_api::StateValue, + /// Used only by the RPC: This represents the head of the chain and is set in two distinct stages: + /// 1. `end_slot_hook`: the pending head is populated with data from pending_transactions. + /// 2. `finalize_hook` the `root_hash` is populated. + /// Since this value is not authenticated, it can be modified in the `finalize_hook` with the correct `state_root`. #[state] pub(crate) pending_head: sov_modules_api::AccessoryStateValue, + /// Used only by the RPC: The vec is extended with `pending_head` in `finalize_hook`. #[state] pub(crate) blocks: sov_modules_api::AccessoryStateVec, + /// Used only by the RPC: block_hash => block_number mapping, #[state] pub(crate) block_hashes: sov_modules_api::AccessoryStateMap, + /// Used only by the RPC: List of processed transactions. #[state] pub(crate) transactions: sov_modules_api::AccessoryStateVec, + /// Used only by the RPC: transaction_hash => transaction_index mapping. #[state] pub(crate) transaction_hashes: sov_modules_api::AccessoryStateMap, + /// Used only by the RPC: Receipts. #[state] pub(crate) receipts: sov_modules_api::AccessoryStateVec, } diff --git a/module-system/module-implementations/sov-evm/src/query.rs b/module-system/module-implementations/sov-evm/src/query.rs index 5df95006f..65b02711a 100644 --- a/module-system/module-implementations/sov-evm/src/query.rs +++ b/module-system/module-implementations/sov-evm/src/query.rs @@ -15,51 +15,7 @@ use crate::Evm; #[rpc_gen(client, server, namespace = "eth")] impl Evm { - fn get_cfg_env_template(&self) -> revm::primitives::CfgEnv { - let mut cfg_env = revm::primitives::CfgEnv::default(); - // Reth sets this to true and uses only timeout, but other clients use this as a part of DOS attacks protection, with 100mln gas limit - // https://github.com/paradigmxyz/reth/blob/62f39a5a151c5f4ddc9bf0851725923989df0412/crates/rpc/rpc/src/eth/revm_utils.rs#L215 - cfg_env.disable_block_gas_limit = false; - cfg_env.disable_eip3607 = true; - cfg_env.disable_base_fee = true; - cfg_env.chain_id = 0; - // https://github.com/Sovereign-Labs/sovereign-sdk/issues/912 - cfg_env.spec_id = revm::primitives::SpecId::SHANGHAI; - cfg_env.perf_analyse_created_bytecodes = revm::primitives::AnalysisKind::Analyse; - cfg_env.limit_contract_code_size = None; - cfg_env - } - - fn get_sealed_block_by_number( - &self, - block_number: Option, - working_set: &mut WorkingSet, - ) -> SealedBlock { - // safe, finalized, and pending are not supported - match block_number { - Some(ref block_number) if block_number == "earliest" => self - .blocks - .get(0, &mut working_set.accessory_state()) - .expect("Genesis block must be set"), - Some(ref block_number) if block_number == "latest" => self - .blocks - .last(&mut working_set.accessory_state()) - .expect("Head block must be set"), - Some(ref block_number) => { - let block_number = block_number.strip_prefix("0x").unwrap_or(block_number); - let block_number = - usize::from_str_radix(block_number, 16).expect("Block number must be hex"); - self.blocks - .get(block_number, &mut working_set.accessory_state()) - .expect("Block must be set") - } - None => self - .blocks - .last(&mut working_set.accessory_state()) - .expect("Head block must be set"), - } - } - + /// Handler for: `eth_chainId` #[rpc_method(name = "chainId")] pub fn chain_id( &self, @@ -77,6 +33,7 @@ impl Evm { Ok(Some(chain_id)) } + /// Handler for: `eth_getBlockByNumber` #[rpc_method(name = "getBlockByNumber")] pub fn get_block_by_number( &self, @@ -136,6 +93,7 @@ impl Evm { Ok(Some(block.into())) } + /// Handler for: `eth_getTransactionCount` #[rpc_method(name = "getTransactionCount")] pub fn get_transaction_count( &self, @@ -157,6 +115,7 @@ impl Evm { Ok(nonce.into()) } + /// Handler for: `eth_feeHistory` // TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/502 #[rpc_method(name = "feeHistory")] pub fn fee_history( @@ -172,6 +131,7 @@ impl Evm { }) } + /// Handler for: `eth_getTransactionByHash` // TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/502 #[rpc_method(name = "getTransactionByHash")] pub fn get_transaction_by_hash( @@ -212,6 +172,7 @@ impl Evm { Ok(transaction) } + /// Handler for: `eth_getTransactionReceipt` // TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/502 #[rpc_method(name = "getTransactionReceipt")] pub fn get_transaction_receipt( @@ -246,9 +207,9 @@ impl Evm { Ok(receipt) } - //https://github.com/paradigmxyz/reth/blob/f577e147807a783438a3f16aad968b4396274483/crates/rpc/rpc/src/eth/api/transactions.rs#L502 - //https://github.com/paradigmxyz/reth/blob/main/crates/rpc/rpc-types/src/eth/call.rs#L7 - + /// Handler for: `eth_call` + // https://github.com/paradigmxyz/reth/blob/f577e147807a783438a3f16aad968b4396274483/crates/rpc/rpc/src/eth/api/transactions.rs#L502 + // https://github.com/paradigmxyz/reth/blob/main/crates/rpc/rpc-types/src/eth/call.rs#L7 // TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/502 #[rpc_method(name = "call")] pub fn get_call( @@ -262,9 +223,9 @@ impl Evm { info!("evm module: eth_call"); let tx_env = prepare_call_env(request); - let block_env = self.pending_block.get(working_set).unwrap_or_default(); + let block_env = self.block_env.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.get_cfg_env_template())); + let cfg_env = get_cfg_env(&block_env, cfg, Some(get_cfg_env_template())); let evm_db: EvmDb<'_, C> = self.get_db(working_set); @@ -277,6 +238,7 @@ impl Evm { Ok(output.into_data().into()) } + /// Handler for: `eth_blockNumber` #[rpc_method(name = "blockNumber")] pub fn block_number( &self, @@ -292,6 +254,7 @@ impl Evm { Ok(block_number) } + /// Handler for: `eth_estimateGas` // TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/502 #[rpc_method(name = "estimateGas")] pub fn eth_estimate_gas( @@ -300,16 +263,61 @@ impl Evm { _block_number: Option, _working_set: &mut WorkingSet, ) -> RpcResult { - unimplemented!("eth_sendTransaction not implemented") + unimplemented!("eth_estimateGas not implemented") } + /// Handler for: `eth_gasPrice` // TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/502 #[rpc_method(name = "gasPrice")] - pub fn gas_price(&self) -> RpcResult { - unimplemented!("eth_sendTransaction not implemented") + pub fn gas_price(&self, _working_set: &mut WorkingSet) -> RpcResult { + unimplemented!("eth_gasPrice not implemented") + } + + fn get_sealed_block_by_number( + &self, + block_number: Option, + working_set: &mut WorkingSet, + ) -> SealedBlock { + // safe, finalized, and pending are not supported + match block_number { + Some(ref block_number) if block_number == "earliest" => self + .blocks + .get(0, &mut working_set.accessory_state()) + .expect("Genesis block must be set"), + Some(ref block_number) if block_number == "latest" => self + .blocks + .last(&mut working_set.accessory_state()) + .expect("Head block must be set"), + Some(ref block_number) => { + let block_number = + usize::from_str_radix(block_number, 16).expect("Block number must be hex"); + self.blocks + .get(block_number, &mut working_set.accessory_state()) + .expect("Block must be set") + } + None => self + .blocks + .last(&mut working_set.accessory_state()) + .expect("Head block must be set"), + } } } +fn get_cfg_env_template() -> revm::primitives::CfgEnv { + let mut cfg_env = revm::primitives::CfgEnv::default(); + // Reth sets this to true and uses only timeout, but other clients use this as a part of DOS attacks protection, with 100mln gas limit + // https://github.com/paradigmxyz/reth/blob/62f39a5a151c5f4ddc9bf0851725923989df0412/crates/rpc/rpc/src/eth/revm_utils.rs#L215 + cfg_env.disable_block_gas_limit = false; + cfg_env.disable_eip3607 = true; + cfg_env.disable_base_fee = true; + cfg_env.chain_id = 0; + // https://github.com/Sovereign-Labs/sovereign-sdk/issues/912 + cfg_env.spec_id = revm::primitives::SpecId::SHANGHAI; + cfg_env.perf_analyse_created_bytecodes = revm::primitives::AnalysisKind::Analyse; + cfg_env.limit_contract_code_size = None; + cfg_env +} + // modified from: https://github.com/paradigmxyz/reth/blob/cc576bc8690a3e16e6e5bf1cbbbfdd029e85e3d4/crates/rpc/rpc/src/eth/api/transactions.rs#L849 pub(crate) fn build_rpc_receipt( block: SealedBlock, diff --git a/module-system/module-implementations/sov-evm/src/signer/mod.rs b/module-system/module-implementations/sov-evm/src/signer/mod.rs index c98fde337..66f177f7e 100644 --- a/module-system/module-implementations/sov-evm/src/signer/mod.rs +++ b/module-system/module-implementations/sov-evm/src/signer/mod.rs @@ -57,6 +57,7 @@ impl DevSigner { )) } + /// List of signers. pub fn signers(&self) -> Vec
{ self.signers.keys().copied().collect() } 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 index 87ab67230..669c5e86f 100644 --- a/module-system/module-implementations/sov-evm/src/tests/genesis_tests.rs +++ b/module-system/module-implementations/sov-evm/src/tests/genesis_tests.rs @@ -219,7 +219,7 @@ pub(crate) fn get_evm(config: &EvmConfig) -> (Evm, WorkingSet let mut working_set = WorkingSet::new(ProverStorage::with_path(tmpdir.path()).unwrap()); let evm = Evm::::default(); evm.genesis(config, &mut working_set).unwrap(); - evm.finalize_slot_hook(&[10u8; 32].into(), &mut working_set.accessory_state()); + evm.finalize_hook(&[10u8; 32].into(), &mut working_set.accessory_state()); (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 index c27bb87c0..35174c414 100644 --- a/module-system/module-implementations/sov-evm/src/tests/hooks_tests.rs +++ b/module-system/module-implementations/sov-evm/src/tests/hooks_tests.rs @@ -20,7 +20,7 @@ lazy_static! { fn begin_slot_hook_creates_pending_block() { let (evm, mut working_set) = get_evm(&TEST_CONFIG); evm.begin_slot_hook(DA_ROOT_HASH.0, &mut working_set); - let pending_block = evm.pending_block.get(&mut working_set).unwrap(); + let pending_block = evm.block_env.get(&mut working_set).unwrap(); assert_eq!( pending_block, BlockEnv { @@ -193,7 +193,7 @@ fn finalize_hook_creates_final_block() { let mut accessory_state = working_set.accessory_state(); let root_hash = [99u8; 32].into(); - evm.finalize_slot_hook(&root_hash, &mut accessory_state); + evm.finalize_hook(&root_hash, &mut accessory_state); assert_eq!(evm.blocks.len(&mut accessory_state), 2); diff --git a/module-system/sov-modules-api/src/hooks.rs b/module-system/sov-modules-api/src/hooks.rs index ac6bf6325..bbb08bdc9 100644 --- a/module-system/sov-modules-api/src/hooks.rs +++ b/module-system/sov-modules-api/src/hooks.rs @@ -69,7 +69,7 @@ pub trait SlotHooks { pub trait FinalizeHook { type Context: Context; - fn finalize_slot_hook( + fn finalize_hook( &self, root_hash: &<::Storage as Storage>::Root, accesorry_working_set: &mut AccessoryWorkingSet, diff --git a/module-system/sov-modules-stf-template/src/lib.rs b/module-system/sov-modules-stf-template/src/lib.rs index efb25dfe7..f50825d76 100644 --- a/module-system/sov-modules-stf-template/src/lib.rs +++ b/module-system/sov-modules-stf-template/src/lib.rs @@ -126,7 +126,7 @@ where let mut working_set = checkpoint.to_revertable(); self.runtime - .finalize_slot_hook(&root_hash, &mut working_set.accessory_state()); + .finalize_hook(&root_hash, &mut working_set.accessory_state()); let accessory_log = working_set.checkpoint().freeze_non_provable(); @@ -174,7 +174,7 @@ where let mut working_set = checkpoint.to_revertable(); self.runtime - .finalize_slot_hook(&genesis_hash, &mut working_set.accessory_state()); + .finalize_hook(&genesis_hash, &mut working_set.accessory_state()); let accessory_log = working_set.checkpoint().freeze_non_provable(); From f1c399bedeef9d9ab1c47f38b4541210ba0b8347 Mon Sep 17 00:00:00 2001 From: Preston Evans <32944016+preston-evans98@users.noreply.github.com> Date: Mon, 25 Sep 2023 06:04:13 -0700 Subject: [PATCH 05/34] Cleanup prover docs/logs (#919) * Cleanup prever docs/logs * fmt --- examples/demo-prover/src/main.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/examples/demo-prover/src/main.rs b/examples/demo-prover/src/main.rs index 07e401a3c..c37d91d1d 100644 --- a/examples/demo-prover/src/main.rs +++ b/examples/demo-prover/src/main.rs @@ -3,37 +3,42 @@ use std::env; use methods::ROLLUP_ELF; use sov_demo_rollup::{new_rollup_with_celestia_da, DemoProverConfig}; use sov_risc0_adapter::host::Risc0Host; -use tracing::Level; +use tracing::info; +use tracing_subscriber::filter::LevelFilter; +use tracing_subscriber::EnvFilter; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { - // If SKIP_PROVER is set, this means that we still compile and generate the riscV ELF - // We execute the code inside the riscV but we don't prove it. This saves a significant amount of time - // The primary benefit of doing this is to make sure we produce valid code that can run inside the - // riscV virtual machine. Since proving is something we offload entirely to risc0, ensuring that - // we produce valid riscV code and that it can execute is very useful. + // If SKIP_PROVER is set, We still compile and run the zkVM code inside of an emulator without generating + // a proof. This dramatically reduces the runtime of the prover, while still ensuring that our rollup + // code is valid and operates as expected. let prover_config = if env::var("SKIP_PROVER").is_ok() { DemoProverConfig::Execute } else { DemoProverConfig::Prove }; + // Initializing logging let subscriber = tracing_subscriber::fmt() - .with_max_level(Level::INFO) + .with_env_filter( + EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) // If no logging config is set. default to `info` level logs + .from_env_lossy(), // Parse the log level from the RUST_LOG env var if set + ) // Try to override logging config from RUST_LOG env var .finish(); tracing::subscriber::set_global_default(subscriber) .map_err(|_err| eprintln!("Unable to set global default subscriber")) .expect("Cannot fail to set subscriber"); - // Same rollup_config.toml as used for the demo_rollup + // The format of the demo-prover config file is identircal to that of demo-rollup. // When running from the demo-prover folder, the first argument can be pointed to ../demo-rollup/rollup_config.toml let rollup_config_path = env::args() .nth(1) .unwrap_or_else(|| "rollup_config.toml".to_string()); - println!("Read rollup config"); + info!("Reading rollup config from {rollup_config_path}"); + // Initialize the rollup. For this demo, we use Risc0 and Celestia. let prover = Risc0Host::new(ROLLUP_ELF); - let rollup = new_rollup_with_celestia_da(&rollup_config_path, Some((prover, prover_config))).await?; rollup.run().await?; From 09b141a8f173517f972a79364fb464c997de51ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:40:58 +0000 Subject: [PATCH 06/34] Bump markdown from 1.0.0-alpha.13 to 1.0.0-alpha.14 (#924) Bumps [markdown](https://github.com/wooorm/markdown-rs) from 1.0.0-alpha.13 to 1.0.0-alpha.14. - [Release notes](https://github.com/wooorm/markdown-rs/releases) - [Commits](https://github.com/wooorm/markdown-rs/compare/1.0.0-alpha.13...1.0.0-alpha.14) --- updated-dependencies: - dependency-name: markdown dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- utils/bashtestmd/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 521be6e73..ba5b4ab96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4065,9 +4065,9 @@ dependencies = [ [[package]] name = "markdown" -version = "1.0.0-alpha.13" +version = "1.0.0-alpha.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e9ce98969bb1391c8d6fdac320897ea7e86c4d356e8f220a5abd28b142e512" +checksum = "d2a51befc5a2b4a052c473ffbc9ad462e358de59dcc2fde4997fd2a16403dcbd" dependencies = [ "unicode-id", ] diff --git a/utils/bashtestmd/Cargo.toml b/utils/bashtestmd/Cargo.toml index 900b6ea50..8925c0c0d 100644 --- a/utils/bashtestmd/Cargo.toml +++ b/utils/bashtestmd/Cargo.toml @@ -13,5 +13,5 @@ autotests = false [dependencies] clap = { version = "4.4", features = ["derive"] } -markdown = "1.0.0-alpha.12" +markdown = "1.0.0-alpha.14" shell-escape = "0.1.5" From e5d2dd8c0857796cd458a148671e4b215c745467 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:41:19 +0000 Subject: [PATCH 07/34] Bump rayon from 1.7.0 to 1.8.0 (#923) Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.7.0 to 1.8.0. - [Changelog](https://github.com/rayon-rs/rayon/blob/master/RELEASES.md) - [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.7.0...rayon-core-v1.8.0) --- updated-dependencies: - dependency-name: rayon dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 20 ++++---------------- Cargo.toml | 4 ++-- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba5b4ab96..46bb8adb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1450,16 +1450,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-deque" version = "0.8.3" @@ -5511,9 +5501,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -5521,14 +5511,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 60c8ce9a6..572f6c0ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,8 +77,8 @@ prometheus = { version = "0.13.3", default-features = false } proptest = "1.0.0" proptest-derive = "0.3.0" rand = "0.8" -rayon = "1.5.2" -rocksdb = { path="/Users/dubbelosix/rust-rocksdb", features = ["lz4"] } +rayon = "1.8.0" +rocksdb = { version = "0.21.0", features = ["lz4"] } serde = { version = "1.0.188", features = ["derive", "rc"] } serde_json = { version = "1.0" } sha2 = "0.10.6" From 6432983dd8f5a386d6e0dfcdad977ac04672bc70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:50:46 +0000 Subject: [PATCH 08/34] Bump prost-build from 0.11.9 to 0.12.1 (#930) Bumps [prost-build](https://github.com/tokio-rs/prost) from 0.11.9 to 0.12.1. - [Release notes](https://github.com/tokio-rs/prost/releases) - [Commits](https://github.com/tokio-rs/prost/compare/v0.11.9...v0.12.1) --- updated-dependencies: - dependency-name: prost-build dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 44 +++++------------------------------- adapters/celestia/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46bb8adb1..e5a560fbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -598,7 +598,7 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.15", + "prettyplease", "proc-macro2 1.0.67", "quote 1.0.33", "regex", @@ -620,7 +620,7 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "prettyplease 0.2.15", + "prettyplease", "proc-macro2 1.0.67", "quote 1.0.33", "regex", @@ -2349,7 +2349,7 @@ dependencies = [ "ethers-core", "ethers-etherscan", "eyre", - "prettyplease 0.2.15", + "prettyplease", "proc-macro2 1.0.67", "quote 1.0.33", "regex", @@ -5060,16 +5060,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2 1.0.67", - "syn 1.0.109", -] - [[package]] name = "prettyplease" version = "0.2.15" @@ -5249,28 +5239,6 @@ dependencies = [ "prost-derive 0.12.1", ] -[[package]] -name = "prost-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck", - "itertools 0.10.5", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease 0.1.25", - "prost 0.11.9", - "prost-types 0.11.9", - "regex", - "syn 1.0.109", - "tempfile", - "which", -] - [[package]] name = "prost-build" version = "0.12.1" @@ -5284,7 +5252,7 @@ dependencies = [ "multimap", "once_cell", "petgraph", - "prettyplease 0.2.15", + "prettyplease", "prost 0.12.1", "prost-types 0.12.1", "regex", @@ -6299,7 +6267,7 @@ dependencies = [ "num-derive 0.4.0", "num-traits", "prost 0.12.1", - "prost-build 0.12.1", + "prost-build", "protobuf-src", "rand 0.8.5", "rayon", @@ -7198,7 +7166,7 @@ dependencies = [ "postcard", "proptest", "prost 0.11.9", - "prost-build 0.11.9", + "prost-build", "prost-types 0.11.9", "risc0-zkvm", "risc0-zkvm-platform", diff --git a/adapters/celestia/Cargo.toml b/adapters/celestia/Cargo.toml index f827dcb4a..ae2fb4e32 100644 --- a/adapters/celestia/Cargo.toml +++ b/adapters/celestia/Cargo.toml @@ -48,7 +48,7 @@ wiremock = "0.5" [build-dependencies] -prost-build = { version = "0.11" } +prost-build = { version = "0.12" } [features] default = [] From 3b13808f10685d527cc8e735b477498e5171b6fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:53:22 +0000 Subject: [PATCH 09/34] Bump parking_lot from 0.11.2 to 0.12.1 (#927) Bumps [parking_lot](https://github.com/Amanieu/parking_lot) from 0.11.2 to 0.12.1. - [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md) - [Commits](https://github.com/Amanieu/parking_lot/compare/0.11.2...0.12.1) --- updated-dependencies: - dependency-name: parking_lot dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 2 +- examples/demo-prover/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5a560fbf..1fc906e65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7269,7 +7269,7 @@ dependencies = [ "log4rs", "methods", "once_cell", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "prettytable-rs", "regex", "risc0-zkvm", diff --git a/examples/demo-prover/Cargo.toml b/examples/demo-prover/Cargo.toml index e227d5b82..444267b6d 100644 --- a/examples/demo-prover/Cargo.toml +++ b/examples/demo-prover/Cargo.toml @@ -37,7 +37,7 @@ methods = { path = "./methods" } [dev-dependencies] tempfile = { workspace = true } once_cell = "1.7.2" -parking_lot = "0.11.1" +parking_lot = "0.12.1" prettytable-rs = "^0.10" env_logger = "0.10.0" log = "0.4" From 6d81f3a340c10fc359de5d2e9eafaf5ab75c12ce Mon Sep 17 00:00:00 2001 From: Victor Lopes Date: Mon, 25 Sep 2023 23:34:09 +0200 Subject: [PATCH 10/34] feat: use json as manifest constants instead of toml (#922) * feat: use json as manifest constants instead of toml Currently, we parse the manifest file as TOML. However, we will use JSON for the genesis format. For improved consistency, it is desirable to have the constants manifest file with the same format. * fix CI lints * use parent as argument for error reporting * refactor gas config parse to return declaration * fix ci lints --- constants.json | 3 + constants.toml | 1 - module-system/sov-modules-macros/Cargo.toml | 3 +- .../sov-modules-macros/src/manifest.rs | 310 ++++++++++++------ .../sov-modules-macros/src/module_info.rs | 2 +- .../field_missing_attribute.stderr | 2 +- 6 files changed, 220 insertions(+), 101 deletions(-) create mode 100644 constants.json delete mode 100644 constants.toml diff --git a/constants.json b/constants.json new file mode 100644 index 000000000..96620c6e7 --- /dev/null +++ b/constants.json @@ -0,0 +1,3 @@ +{ + "comment": "Sovereign SDK constants" +} diff --git a/constants.toml b/constants.toml deleted file mode 100644 index a174ade80..000000000 --- a/constants.toml +++ /dev/null @@ -1 +0,0 @@ -# Base SDK configuration file diff --git a/module-system/sov-modules-macros/Cargo.toml b/module-system/sov-modules-macros/Cargo.toml index 97c40a7d1..509cdb58c 100644 --- a/module-system/sov-modules-macros/Cargo.toml +++ b/module-system/sov-modules-macros/Cargo.toml @@ -23,7 +23,6 @@ path = "tests/all_tests.rs" clap = { workspace = true } jsonrpsee = { workspace = true, features = ["macros", "http-client", "server"] } serde = { workspace = true } -serde_json = { workspace = true } tempfile = { workspace = true } trybuild = "1.0" @@ -39,8 +38,8 @@ jsonrpsee = { workspace = true, features = ["http-client", "server"], optional = proc-macro2 = "1.0" quote = "1.0" schemars = { workspace = true } +serde_json = { workspace = true } syn = { version = "1.0", features = ["full"] } -toml = "0.8" [features] default = [] diff --git a/module-system/sov-modules-macros/src/manifest.rs b/module-system/sov-modules-macros/src/manifest.rs index b27bf6be2..601010fce 100644 --- a/module-system/sov-modules-macros/src/manifest.rs +++ b/module-system/sov-modules-macros/src/manifest.rs @@ -1,59 +1,87 @@ // TODO remove once consumed #![allow(dead_code)] -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::{env, fmt, fs, ops}; -use proc_macro2::TokenStream; -use toml::{Table, Value}; - -use crate::common::StructDef; -use crate::module_info::parsing::ModuleField; +use proc_macro2::{Ident, TokenStream}; +use serde_json::Value; #[derive(Debug, Clone)] pub struct Manifest { path: PathBuf, - table: Table, + value: Value, } impl ops::Deref for Manifest { - type Target = Table; + type Target = Value; fn deref(&self) -> &Self::Target { - &self.table + &self.value } } impl Manifest { - /// The file name of the manifest. - pub const MANIFEST_NAME: &'static str = "constants.toml"; + /// Parse a manifest file from a string. + /// + /// The provided path will be used to feedback error to the user, if any. + /// + /// The `parent` is used to report the errors to the correct span location. + pub fn read_str(manifest: S, path: PathBuf, parent: &Ident) -> Result + where + S: AsRef, + { + let value = serde_json::from_str(manifest.as_ref()) + .map_err(|e| Self::err(&path, parent, format!("failed to parse manifest: {e}")))?; + + Ok(Self { path, value }) + } - /// Reads a `sovereign.toml` manifest file, recursing from the target directory that builds the + /// Reads a `constants.json` manifest file, recursing from the target directory that builds the /// current implementation. /// /// If the environment variable `CONSTANTS_MANIFEST` is set, it will use that instead. - pub fn read() -> anyhow::Result { + /// + /// The `parent` is used to report the errors to the correct span location. + pub fn read_constants(parent: &Ident) -> Result { + let manifest = "constants.json"; let initial_path = match env::var("CONSTANTS_MANIFEST") { Ok(p) => PathBuf::from(&p).canonicalize().map_err(|e| { - anyhow::anyhow!("failed access base dir for sovereign manifest file `{p}`: {e}",) + Self::err( + &p, + parent, + format!("failed access base dir for sovereign manifest file `{p}`: {e}"), + ) }), Err(_) => { // read the target directory set via build script since `OUT_DIR` is available only at build let initial_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .canonicalize() .map_err(|e| { - anyhow::anyhow!("failed access base dir for sovereign manifest file: {e}") + Self::err( + manifest, + parent, + format!("failed access base dir for sovereign manifest file: {e}"), + ) })? .join("target-path"); let initial_path = fs::read_to_string(&initial_path).map_err(|e| { - anyhow::anyhow!("failed to read target path for sovereign manifest file: {e}") + Self::err( + &initial_path, + parent, + format!("failed to read target path for sovereign manifest file: {e}"), + ) })?; PathBuf::from(initial_path.trim()) .canonicalize() .map_err(|e| { - anyhow::anyhow!("failed access base dir for sovereign manifest file: {e}") + Self::err( + &initial_path, + parent, + format!("failed access base dir for sovereign manifest file: {e}"), + ) }) } }?; @@ -61,116 +89,153 @@ impl Manifest { let path: PathBuf; let mut current_path = initial_path.as_path(); loop { - if current_path.join(Self::MANIFEST_NAME).exists() { - path = current_path.join(Self::MANIFEST_NAME); + if current_path.join(manifest).exists() { + path = current_path.join(manifest); break; } current_path = current_path.parent().ok_or_else(|| { - anyhow::anyhow!("Could not find a parent {}", Self::MANIFEST_NAME) + Self::err( + current_path, + parent, + format!("Could not find a parent `{manifest}`"), + ) })?; } let manifest = fs::read_to_string(&path) - .map_err(|e| anyhow::anyhow!("Could not read the parent `{}`: {e}", path.display()))?; + .map_err(|e| Self::err(current_path, parent, format!("failed to read file: {e}")))?; - let table = toml::from_str(&manifest) - .map_err(|e| anyhow::anyhow!("Could not parse `{}`: {}", path.display(), e))?; - - Ok(Self { path, table }) + Self::read_str(manifest, path, parent) } - /// Parses a module struct from the manifest file. Returns a `TokenStream` with the following - /// structure: + /// Parses a gas config constant from the manifest file. Returns a `TokenStream` with the + /// following structure: /// /// ```rust,ignore - /// let foo = Foo { - /// bar: Into::into("baz"), + /// const GAS_CONFIG: Self::GasConfig = Self::GasConfig { + /// foo: [1u64, 2u64, 3u64, ], + /// bar: [4u64, 5u64, 6u64, ], /// }; /// ``` /// - /// Since this will be static code, the `TokensStream` will act as a constant, if there is no - /// allocation such as with `String`. - /// - /// The routine will first query `section.parent`, and then fallback to `section`. Example - /// - /// ```toml - /// [module] - /// bar = "general" + /// Where `foo` and `bar` are fields of the json constants file under the located `gas` field. /// - /// [module.Foo] - /// bar = "baz" - /// ``` - /// - /// A call with a parent `Foo` will yield `bar = "baz"`, and a call with a parent `Etc` will - /// yield `bar = "general"`. - pub(crate) fn parse_module_struct( + /// The `gas` field resolution will first attempt to query `gas.parent`, and then fallback to + /// `gas`. They must be objects with arrays of integers as fields. + pub fn parse_gas_config( &self, - section: &str, - parent: &StructDef, - field: &ModuleField, - ) -> Result { + parent: &Ident, + ) -> Result<(Ident, TokenStream, TokenStream), syn::Error> { let root = self - .table - .get(section) - .ok_or_else(|| self.err(&field.ident, format!("no `{}` section", section)))? - .as_table() + .value + .as_object() + .ok_or_else(|| Self::err(&self.path, parent, "manifest is not an object"))? + .get("gas") .ok_or_else(|| { - self.err( - &field.ident, - format!("`{}` section must be a table", section), + Self::err( + &self.path, + parent, + "manifest does not contain a `gas` attribute", ) })? - .clone(); - - let root = match root.get(&parent.ident.to_string()) { - Some(Value::Table(t)) => t.clone(), - _ => root - .into_iter() - // skip all tables so other modules are not included - .filter(|(_, v)| !v.is_table()) - .collect(), - }; + .as_object() + .ok_or_else(|| { + Self::err( + &self.path, + parent, + format!("`gas` attribute of `{}` is not an object", parent), + ) + })?; - let struct_fields = root - .iter() - .map(|(f, v)| (quote::format_ident!("{f}"), v)) - .map(|(f, v)| match v { - // TODO this can be optimized to specific cases based on type and avoid - // `Into::into` (i.e. u32 as u64) - Value::String(v) => Ok(quote::quote!(#f: #v)), - Value::Integer(v) => Ok(quote::quote!(#f: #v)), - Value::Float(v) => Ok(quote::quote!(#f: #v)), - Value::Boolean(v) => Ok(quote::quote!(#f: #v)), - _ => Err(self.err( - &field.ident, + let root = match root.get(&parent.to_string()) { + Some(Value::Object(m)) => m, + Some(_) => { + return Err(Self::err( + &self.path, + parent, format!( - "the contents of the section `{}` must be string, integer, float, or boolean", - section + "matching constants entry `{}` is not an object", + &parent.to_string() ), - )), - }) - .collect::, _>>()?; - - let t = &field.ty; - let field_ident = &field.ident; - - Ok(quote::quote! { - let #field_ident = #t { - #(#struct_fields,)* - }; - }) + )) + } + None => root, + }; + + let mut field_values = vec![]; + let mut fields = vec![]; + for (k, v) in root { + let k: Ident = syn::parse_str(k).map_err(|e| { + Self::err( + &self.path, + parent, + format!("failed to parse key attribyte `{}`: {}", k, e), + ) + })?; + + let v = v + .as_array() + .ok_or_else(|| { + Self::err( + &self.path, + parent, + format!("`{}` attribute is not an array", k), + ) + })? + .iter() + .map(|v| { + v.as_u64().ok_or_else(|| { + Self::err( + &self.path, + parent, + format!("`{}` attribute is not an array of integers", k), + ) + }) + }) + .collect::, _>>()?; + + let n = v.len(); + fields.push(quote::quote!(pub #k: [u64; #n])); + field_values.push(quote::quote!(#k: [#(#v,)*])); + } + + let ty = format!("{parent}GasConfig"); + let ty = syn::parse_str(&ty).map_err(|e| { + Self::err( + &self.path, + parent, + format!("failed to parse type name `{}`: {}", ty, e), + ) + })?; + + let def = quote::quote! { + #[allow(missing_docs)] + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct #ty { + #(#fields,)* + } + }; + + let decl = quote::quote! { + #ty { + #(#field_values,)* + } + }; + + Ok((ty, def, decl)) } - fn err(&self, ident: &syn::Ident, msg: T) -> syn::Error + fn err(path: P, ident: &syn::Ident, msg: T) -> syn::Error where + P: AsRef, T: fmt::Display, { syn::Error::new( ident.span(), format!( "failed to parse manifest `{}` for `{}`: {}", - self.path.display(), + path.as_ref().display(), ident, msg ), @@ -186,13 +251,66 @@ fn fetch_manifest_works() { .unwrap() .parent() .unwrap() - .join(Manifest::MANIFEST_NAME) + .join("constants.json") .canonicalize() .unwrap(); let expected = fs::read_to_string(path).unwrap(); - let expected = toml::from_str(&expected).unwrap(); + let expected: Value = serde_json::from_str(&expected).unwrap(); - let manifest = Manifest::read().unwrap(); + let parent = Ident::new("foo", proc_macro2::Span::call_site()); + let manifest = Manifest::read_constants(&parent).unwrap(); assert_eq!(*manifest, expected); } + +#[test] +fn parse_gas_config_works() { + let input = r#"{ + "comment": "Sovereign SDK constants", + "gas": { + "complex_math_operation": [1, 2, 3], + "some_other_operation": [4, 5, 6] + } + }"#; + + let parent = Ident::new("Foo", proc_macro2::Span::call_site()); + let (ty, def, decl) = Manifest::read_str(input, PathBuf::from("foo.json"), &parent) + .unwrap() + .parse_gas_config(&parent) + .unwrap(); + + #[rustfmt::skip] + assert_eq!( + ty.to_string(), + quote::quote!( + FooGasConfig + ) + .to_string() + ); + + #[rustfmt::skip] + assert_eq!( + def.to_string(), + quote::quote!( + #[allow(missing_docs)] + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct FooGasConfig { + pub complex_math_operation: [u64; 3usize], + pub some_other_operation: [u64; 3usize], + } + ) + .to_string() + ); + + #[rustfmt::skip] + assert_eq!( + decl.to_string(), + quote::quote!( + FooGasConfig { + complex_math_operation: [1u64, 2u64, 3u64, ], + some_other_operation: [4u64, 5u64, 6u64, ], + } + ) + .to_string() + ); +} diff --git a/module-system/sov-modules-macros/src/module_info.rs b/module-system/sov-modules-macros/src/module_info.rs index b2fd8dde0..833947824 100644 --- a/module-system/sov-modules-macros/src/module_info.rs +++ b/module-system/sov-modules-macros/src/module_info.rs @@ -449,7 +449,7 @@ pub mod parsing { } else { Err(syn::Error::new_spanned( ident, - "This field is missing an attribute: add `#[module]`, `#[state]` or `#[address]`.", + format!("The field `{}` is missing an attribute: add `#[module]`, `#[state]` or `#[address]`.", ident), )) } } diff --git a/module-system/sov-modules-macros/tests/module_info/field_missing_attribute.stderr b/module-system/sov-modules-macros/tests/module_info/field_missing_attribute.stderr index 5af5342d3..4cdbb45b5 100644 --- a/module-system/sov-modules-macros/tests/module_info/field_missing_attribute.stderr +++ b/module-system/sov-modules-macros/tests/module_info/field_missing_attribute.stderr @@ -1,4 +1,4 @@ -error: This field is missing an attribute: add `#[module]`, `#[state]` or `#[address]`. +error: The field `test_state1` is missing an attribute: add `#[module]`, `#[state]` or `#[address]`. --> tests/module_info/field_missing_attribute.rs:8:5 | 8 | test_state1: StateMap, From 24302815cc6e53ac2040b6239a9df164398a761b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 18:15:48 +0200 Subject: [PATCH 11/34] Bump tungstenite from 0.20.0 to 0.20.1 (#931) Bumps [tungstenite](https://github.com/snapview/tungstenite-rs) from 0.20.0 to 0.20.1. - [Changelog](https://github.com/snapview/tungstenite-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/snapview/tungstenite-rs/compare/v0.20.0...v0.20.1) --- updated-dependencies: - dependency-name: tungstenite dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1fc906e65..e25995da7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7470,7 +7470,6 @@ dependencies = [ "sov-state", "syn 1.0.109", "tempfile", - "toml 0.8.0", "trybuild", ] @@ -8538,9 +8537,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ "byteorder", "bytes", From b406b94648fa698c4a33f45f0ad7b702cb539205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orkun=20Mahir=20K=C4=B1l=C4=B1=C3=A7?= Date: Wed, 27 Sep 2023 11:32:28 +0300 Subject: [PATCH 12/34] EVM: Implement eth_call (#921) * implement eth_call * implement tests and error handling for eth_call * move errors inside evm crate * cleanup result * fix call env * comment test differences * rebase * Revert "rebase" This reverts commit 44e41b23dac2a68839cad896ef6c46c20ca410cc. * fix unused imports * fix review notes * use mix_hash as prevrandao * improve test s --- Cargo.lock | 1 + Cargo.toml | 1 + examples/demo-rollup/tests/evm/mod.rs | 74 +- full-node/sov-ethereum/src/lib.rs | 2 +- .../module-implementations/sov-evm/Cargo.toml | 1 + .../sov-evm/src/evm/call.rs | 192 ++++++ .../sov-evm/src/evm/conversions.rs | 51 +- .../sov-evm/src/evm/error/mod.rs | 3 + .../sov-evm/src/evm/error/pool.rs | 270 ++++++++ .../sov-evm/src/evm/error/result.rs | 90 +++ .../sov-evm/src/evm/error/rpc.rs | 642 ++++++++++++++++++ .../sov-evm/src/evm/mod.rs | 4 +- .../sov-evm/src/evm/primitive_types.rs | 40 +- .../sov-evm/src/evm/tests.rs | 20 +- .../module-implementations/sov-evm/src/lib.rs | 2 +- .../sov-evm/src/query.rs | 31 +- .../simple_storage_contract.rs | 7 + .../sov-evm/src/tests/tx_tests.rs | 27 +- 18 files changed, 1370 insertions(+), 88 deletions(-) create mode 100644 module-system/module-implementations/sov-evm/src/evm/call.rs create mode 100644 module-system/module-implementations/sov-evm/src/evm/error/mod.rs create mode 100644 module-system/module-implementations/sov-evm/src/evm/error/pool.rs create mode 100644 module-system/module-implementations/sov-evm/src/evm/error/result.rs create mode 100644 module-system/module-implementations/sov-evm/src/evm/error/rpc.rs diff --git a/Cargo.lock b/Cargo.lock index e25995da7..12f3cf696 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7377,6 +7377,7 @@ dependencies = [ "hex", "jsonrpsee", "lazy_static", + "reth-interfaces", "reth-primitives", "reth-revm", "reth-rpc-types", diff --git a/Cargo.toml b/Cargo.toml index 572f6c0ee..a0591c027 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,6 +111,7 @@ ethers-signers = { version = "=2.0.10", default-features = false } ethers-middleware = { version = "=2.0.10", default-features = false } reth-primitives = { git = "https://github.com/paradigmxyz/reth", rev = "e83d3aa" } +reth-interfaces = { git = "https://github.com/paradigmxyz/reth", rev = "e83d3aa" } reth-rpc-types = { git = "https://github.com/paradigmxyz/reth", rev = "e83d3aa" } reth-rpc-types-compat = { git = "https://github.com/paradigmxyz/reth", rev = "e83d3aa" } reth-revm = { git = "https://github.com/paradigmxyz/reth", rev = "e83d3aa" } diff --git a/examples/demo-rollup/tests/evm/mod.rs b/examples/demo-rollup/tests/evm/mod.rs index 8141c4ddd..932a5f24a 100644 --- a/examples/demo-rollup/tests/evm/mod.rs +++ b/examples/demo-rollup/tests/evm/mod.rs @@ -5,13 +5,16 @@ use ethereum_types::H160; use ethers_core::abi::Address; use ethers_core::k256::ecdsa::SigningKey; use ethers_core::types::transaction::eip2718::TypedTransaction; -use ethers_core::types::{Block, Eip1559TransactionRequest, Transaction, TxHash}; +use ethers_core::types::{ + Block, Eip1559TransactionRequest, Transaction, TransactionRequest, TxHash, +}; use ethers_middleware::SignerMiddleware; use ethers_providers::{Http, Middleware, PendingTransaction, Provider}; use ethers_signers::{LocalWallet, Signer, Wallet}; use jsonrpsee::core::client::ClientT; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::rpc_params; +use reth_primitives::Bytes; use sov_evm::SimpleStorageContract; use sov_risc0_adapter::host::Risc0Host; @@ -131,6 +134,55 @@ impl TestClient { .unwrap() } + async fn set_value_call( + &self, + contract_address: H160, + set_arg: u32, + ) -> Result> { + let nonce = self.eth_get_transaction_count(self.from_addr).await; + + // Any type of transaction can be used for eth_call + let req = TransactionRequest::new() + .from(self.from_addr) + .to(contract_address) + .chain_id(self.chain_id) + .nonce(nonce) + .data(self.contract.set_call_data(set_arg)) + .gas_price(10u64) + .gas(900000u64); + + let typed_transaction = TypedTransaction::Legacy(req); + + let response = self + .eth_call(typed_transaction, Some("latest".to_owned())) + .await?; + + Ok(response) + } + + async fn failing_call( + &self, + contract_address: H160, + ) -> Result> { + let nonce = self.eth_get_transaction_count(self.from_addr).await; + + // Any type of transaction can be used for eth_call + let req = Eip1559TransactionRequest::new() + .from(self.from_addr) + .to(contract_address) + .chain_id(self.chain_id) + .nonce(nonce) + .data(self.contract.failing_function_call_data()) + .max_priority_fee_per_gas(10u64) + .max_fee_per_gas(MAX_FEE_PER_GAS) + .gas(900000u64); + + let typed_transaction = TypedTransaction::Eip1559(req); + + self.eth_call(typed_transaction, Some("latest".to_owned())) + .await + } + async fn query_contract( &self, contract_address: H160, @@ -207,6 +259,17 @@ impl TestClient { .unwrap() } + async fn eth_call( + &self, + tx: TypedTransaction, + block_number: Option, + ) -> Result> { + self.http_client + .request("eth_call", rpc_params![tx, block_number]) + .await + .map_err(|e| e.into()) + } + async fn execute(self) -> Result<(), Box> { // Nonce should be 0 in genesis let nonce = self.eth_get_transaction_count(self.from_addr).await; @@ -253,6 +316,15 @@ impl TestClient { assert_eq!(latest_block.transactions.len(), 1); assert_eq!(latest_block.transactions[0].hash, tx_hash); + // This should just pass without error + self.set_value_call(contract_address, set_arg) + .await + .unwrap(); + + // This call should fail because function does not exist + let failing_call = self.failing_call(contract_address).await; + assert!(failing_call.is_err()); + // Create a blob with multiple transactions. let mut requests = Vec::default(); for value in 100..103 { diff --git a/full-node/sov-ethereum/src/lib.rs b/full-node/sov-ethereum/src/lib.rs index 21b460e04..b34e57cb3 100644 --- a/full-node/sov-ethereum/src/lib.rs +++ b/full-node/sov-ethereum/src/lib.rs @@ -93,7 +93,7 @@ pub mod experimental { let tx_hash = signed_transaction.hash(); let sender = signed_transaction .recover_signer() - .ok_or(sov_evm::RawEvmTxConversionError::FailedToRecoverSigner)?; + .ok_or(sov_evm::EthApiError::InvalidTransactionSignature)?; let mut nonces = self.nonces.lock().unwrap(); let nonce = *nonces.entry(sender).and_modify(|n| *n += 1).or_insert(0); diff --git a/module-system/module-implementations/sov-evm/Cargo.toml b/module-system/module-implementations/sov-evm/Cargo.toml index 2305148a5..ffa78cf1a 100644 --- a/module-system/module-implementations/sov-evm/Cargo.toml +++ b/module-system/module-implementations/sov-evm/Cargo.toml @@ -49,6 +49,7 @@ revm = { workspace = true, features = [ "optional_no_base_fee", ] } reth-primitives = { workspace = true } +reth-interfaces = { workspace = true } reth-rpc-types = { workspace = true } reth-rpc-types-compat = { workspace = true } reth-revm = { workspace = true } diff --git a/module-system/module-implementations/sov-evm/src/evm/call.rs b/module-system/module-implementations/sov-evm/src/evm/call.rs new file mode 100644 index 000000000..c41f84029 --- /dev/null +++ b/module-system/module-implementations/sov-evm/src/evm/call.rs @@ -0,0 +1,192 @@ +// https://github.com/paradigmxyz/reth/blob/main/crates/rpc/rpc/src/eth/revm_utils.rs + +use reth_primitives::{AccessList, H256, U256}; +use reth_rpc_types::CallRequest; +use revm::primitives::{TransactTo, TxEnv}; + +use crate::error::rpc::{EthApiError, EthResult, RpcInvalidTransactionError}; +use crate::primitive_types::BlockEnv; + +/// Helper type for representing the fees of a [CallRequest] +pub(crate) struct CallFees { + /// EIP-1559 priority fee + max_priority_fee_per_gas: Option, + /// Unified gas price setting + /// + /// Will be the configured `basefee` if unset in the request + /// + /// `gasPrice` for legacy, + /// `maxFeePerGas` for EIP-1559 + gas_price: U256, + /// Max Fee per Blob gas for EIP-4844 transactions + // https://github.com/Sovereign-Labs/sovereign-sdk/issues/912 + #[allow(dead_code)] + max_fee_per_blob_gas: Option, +} + +// === impl CallFees === + +impl CallFees { + /// Ensures the fields of a [CallRequest] are not conflicting. + /// + /// If no `gasPrice` or `maxFeePerGas` is set, then the `gas_price` in the returned `gas_price` + /// will be `0`. See: + /// + /// # EIP-4844 transactions + /// + /// Blob transactions have an additional fee parameter `maxFeePerBlobGas`. + /// If the `maxFeePerBlobGas` or `blobVersionedHashes` are set we treat it as an EIP-4844 + /// transaction. + /// + /// Note: Due to the `Default` impl of [BlockEnv] (Some(0)) this assumes the `block_blob_fee` is + /// always `Some` + fn ensure_fees( + call_gas_price: Option, + call_max_fee: Option, + call_priority_fee: Option, + block_base_fee: U256, + blob_versioned_hashes: Option<&[H256]>, + max_fee_per_blob_gas: Option, + block_blob_fee: Option, + ) -> EthResult { + /// Ensures that the transaction's max fee is lower than the priority fee, if any. + fn ensure_valid_fee_cap( + max_fee: U256, + max_priority_fee_per_gas: Option, + ) -> EthResult<()> { + if let Some(max_priority) = max_priority_fee_per_gas { + if max_priority > max_fee { + // Fail early + return Err( + // `max_priority_fee_per_gas` is greater than the `max_fee_per_gas` + RpcInvalidTransactionError::TipAboveFeeCap.into(), + ); + } + } + Ok(()) + } + + let has_blob_hashes = blob_versioned_hashes + .as_ref() + .map(|blobs| !blobs.is_empty()) + .unwrap_or(false); + + match ( + call_gas_price, + call_max_fee, + call_priority_fee, + max_fee_per_blob_gas, + ) { + (gas_price, None, None, None) => { + // either legacy transaction or no fee fields are specified + // when no fields are specified, set gas price to zero + let gas_price = gas_price.unwrap_or(U256::ZERO); + Ok(CallFees { + gas_price, + max_priority_fee_per_gas: None, + max_fee_per_blob_gas: has_blob_hashes.then_some(block_blob_fee).flatten(), + }) + } + (None, max_fee_per_gas, max_priority_fee_per_gas, None) => { + // request for eip-1559 transaction + let max_fee = max_fee_per_gas.unwrap_or(block_base_fee); + ensure_valid_fee_cap(max_fee, max_priority_fee_per_gas)?; + + let max_fee_per_blob_gas = has_blob_hashes.then_some(block_blob_fee).flatten(); + + Ok(CallFees { + gas_price: max_fee, + max_priority_fee_per_gas, + max_fee_per_blob_gas, + }) + } + (None, max_fee_per_gas, max_priority_fee_per_gas, Some(max_fee_per_blob_gas)) => { + // request for eip-4844 transaction + let max_fee = max_fee_per_gas.unwrap_or(block_base_fee); + ensure_valid_fee_cap(max_fee, max_priority_fee_per_gas)?; + + // Ensure blob_hashes are present + if !has_blob_hashes { + // Blob transaction but no blob hashes + return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into()); + } + + Ok(CallFees { + gas_price: max_fee, + max_priority_fee_per_gas, + max_fee_per_blob_gas: Some(max_fee_per_blob_gas), + }) + } + _ => { + // this fallback covers incompatible combinations of fields + Err(EthApiError::ConflictingFeeFieldsInRequest) + } + } + } +} + +// https://github.com/paradigmxyz/reth/blob/d8677b4146f77c7c82d659c59b79b38caca78778/crates/rpc/rpc/src/eth/revm_utils.rs#L201 +pub(crate) fn prepare_call_env(block_env: &BlockEnv, request: CallRequest) -> EthResult { + let CallRequest { + from, + to, + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + gas, + value, + input, + nonce, + access_list, + chain_id, + .. + } = request; + + let CallFees { + max_priority_fee_per_gas, + gas_price, + // https://github.com/Sovereign-Labs/sovereign-sdk/issues/912 + max_fee_per_blob_gas: _, + } = CallFees::ensure_fees( + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + U256::from(block_env.basefee), + // EIP-4844 related fields + // https://github.com/Sovereign-Labs/sovereign-sdk/issues/912 + None, + None, + None, + )?; + + let gas_limit = gas.unwrap_or(U256::from(block_env.gas_limit.min(u64::MAX))); + + let env = TxEnv { + gas_limit: gas_limit + .try_into() + .map_err(|_| RpcInvalidTransactionError::GasUintOverflow)?, + nonce: nonce + .map(|n| { + n.try_into() + .map_err(|_| RpcInvalidTransactionError::NonceTooHigh) + }) + .transpose()?, + caller: from.unwrap_or_default(), + gas_price, + gas_priority_fee: max_priority_fee_per_gas, + transact_to: to.map(TransactTo::Call).unwrap_or_else(TransactTo::create), + value: value.unwrap_or_default(), + data: input + .try_into_unique_input()? + .map(|data| data.0) + .unwrap_or_default(), + chain_id: chain_id.map(|c| c.as_u64()), + access_list: access_list.map(AccessList::flattened).unwrap_or_default(), + // EIP-4844 related fields + // https://github.com/Sovereign-Labs/sovereign-sdk/issues/912 + blob_hashes: vec![], + max_fee_per_blob_gas: None, + }; + + Ok(env) +} 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 d63fb4adf..3e79e973b 100644 --- a/module-system/module-implementations/sov-evm/src/evm/conversions.rs +++ b/module-system/module-implementations/sov-evm/src/evm/conversions.rs @@ -1,17 +1,14 @@ use bytes::Bytes; use reth_primitives::{ - AccessList, Bytes as RethBytes, TransactionSigned, TransactionSignedEcRecovered, - TransactionSignedNoHash, + Bytes as RethBytes, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, }; -use reth_rpc_types::CallRequest; use revm::primitives::{ AccountInfo as ReVmAccountInfo, BlockEnv as ReVmBlockEnv, CreateScheme, TransactTo, TxEnv, U256, }; -use super::primitive_types::{ - BlockEnv, RawEvmTxConversionError, RlpEvmTransaction, TransactionSignedAndRecovered, -}; +use super::primitive_types::{BlockEnv, RlpEvmTransaction, TransactionSignedAndRecovered}; use super::AccountInfo; +use crate::error::rpc::EthApiError; impl From for ReVmAccountInfo { fn from(info: AccountInfo) -> Self { @@ -77,30 +74,30 @@ pub(crate) fn create_tx_env(tx: &TransactionSignedEcRecovered) -> TxEnv { } impl TryFrom for TransactionSignedNoHash { - type Error = RawEvmTxConversionError; + type Error = EthApiError; fn try_from(data: RlpEvmTransaction) -> Result { let data = RethBytes::from(data.rlp); if data.is_empty() { - return Err(RawEvmTxConversionError::EmptyRawTransactionData); + return Err(EthApiError::EmptyRawTransactionData); } let transaction = TransactionSigned::decode_enveloped(data) - .map_err(|_| RawEvmTxConversionError::FailedToDecodeSignedTransaction)?; + .map_err(|_| EthApiError::FailedToDecodeSignedTransaction)?; Ok(transaction.into()) } } impl TryFrom for TransactionSignedEcRecovered { - type Error = RawEvmTxConversionError; + type Error = EthApiError; fn try_from(evm_tx: RlpEvmTransaction) -> Result { let tx = TransactionSignedNoHash::try_from(evm_tx)?; let tx: TransactionSigned = tx.into(); let tx = tx .into_ecrecovered() - .ok_or(RawEvmTxConversionError::FailedToDecodeSignedTransaction)?; + .ok_or(EthApiError::FailedToDecodeSignedTransaction)?; Ok(tx) } @@ -114,35 +111,3 @@ impl From for TransactionSignedEcRecovered { ) } } - -// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/576 -// https://github.com/paradigmxyz/reth/blob/d8677b4146f77c7c82d659c59b79b38caca78778/crates/rpc/rpc/src/eth/revm_utils.rs#L201 -pub(crate) fn prepare_call_env(request: CallRequest) -> TxEnv { - TxEnv { - caller: request.from.unwrap(), - gas_limit: request.gas.map(|p| p.try_into().unwrap()).unwrap(), - gas_price: request.gas_price.unwrap_or_default(), - gas_priority_fee: request.max_priority_fee_per_gas, - transact_to: request - .to - .map(TransactTo::Call) - .unwrap_or_else(TransactTo::create), - value: request.value.unwrap_or_default(), - data: request - .input - .try_into_unique_input() - .unwrap() - .map(|data| data.0) - .unwrap_or_default(), - chain_id: request.chain_id.map(|c| c.as_u64()), - nonce: request.nonce.map(|n| TryInto::::try_into(n).unwrap()), - access_list: request - .access_list - .map(AccessList::flattened) - .unwrap_or_default(), - // EIP-4844 related fields - // https://github.com/Sovereign-Labs/sovereign-sdk/issues/912 - blob_hashes: request.blob_versioned_hashes, - max_fee_per_blob_gas: request.max_fee_per_blob_gas, - } -} diff --git a/module-system/module-implementations/sov-evm/src/evm/error/mod.rs b/module-system/module-implementations/sov-evm/src/evm/error/mod.rs new file mode 100644 index 000000000..49b89f1ca --- /dev/null +++ b/module-system/module-implementations/sov-evm/src/evm/error/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod pool; +pub(crate) mod result; +pub mod rpc; diff --git a/module-system/module-implementations/sov-evm/src/evm/error/pool.rs b/module-system/module-implementations/sov-evm/src/evm/error/pool.rs new file mode 100644 index 000000000..7be70e1b3 --- /dev/null +++ b/module-system/module-implementations/sov-evm/src/evm/error/pool.rs @@ -0,0 +1,270 @@ +//! Transaction pool errors + +use reth_primitives::{Address, BlobTransactionValidationError, InvalidTransactionError, TxHash}; + +/// A trait for additional errors that can be thrown by the transaction pool. +/// +/// For example during validation +/// [TransactionValidator::validate_transaction](crate::validate::TransactionValidator::validate_transaction) +pub trait PoolTransactionError: std::error::Error + Send + Sync { + /// Returns `true` if the error was caused by a transaction that is considered bad in the + /// context of the transaction pool and warrants peer penalization. + /// + /// See [PoolError::is_bad_transaction]. + fn is_bad_transaction(&self) -> bool; +} + +/// All errors the Transaction pool can throw. +#[derive(Debug, thiserror::Error)] +pub enum PoolError { + /// Same transaction already imported + #[error("[{0:?}] Already imported")] + AlreadyImported(TxHash), + /// Thrown if a replacement transaction's gas price is below the already imported transaction + #[error("[{0:?}]: insufficient gas price to replace existing transaction.")] + ReplacementUnderpriced(TxHash), + /// The fee cap of the transaction is below the minimum fee cap determined by the protocol + #[error("[{0:?}] Transaction feeCap {1} below chain minimum.")] + FeeCapBelowMinimumProtocolFeeCap(TxHash, u128), + /// Thrown when the number of unique transactions of a sender exceeded the slot capacity. + #[error("{0:?} identified as spammer. Transaction {1:?} rejected.")] + SpammerExceededCapacity(Address, TxHash), + /// Thrown when a new transaction is added to the pool, but then immediately discarded to + /// respect the size limits of the pool. + #[error("[{0:?}] Transaction discarded outright due to pool size constraints.")] + DiscardedOnInsert(TxHash), + /// Thrown when the transaction is considered invalid. + #[error("[{0:?}] {1:?}")] + InvalidTransaction(TxHash, InvalidPoolTransactionError), + /// Thrown if the mutual exclusivity constraint (blob vs normal transaction) is violated. + #[error("[{1:?}] Transaction type {2} conflicts with existing transaction for {0:?}")] + ExistingConflictingTransactionType(Address, TxHash, u8), + /// Any other error that occurred while inserting/validating a transaction. e.g. IO database + /// error + #[error("[{0:?}] {1:?}")] + Other(TxHash, Box), +} + +// === impl PoolError === + +impl PoolError { + /// Returns the hash of the transaction that resulted in this error. + pub fn hash(&self) -> &TxHash { + match self { + PoolError::AlreadyImported(hash) => hash, + PoolError::ReplacementUnderpriced(hash) => hash, + PoolError::FeeCapBelowMinimumProtocolFeeCap(hash, _) => hash, + PoolError::SpammerExceededCapacity(_, hash) => hash, + PoolError::DiscardedOnInsert(hash) => hash, + PoolError::InvalidTransaction(hash, _) => hash, + PoolError::Other(hash, _) => hash, + PoolError::ExistingConflictingTransactionType(_, hash, _) => hash, + } + } + + /// Returns `true` if the error was caused by a transaction that is considered bad in the + /// context of the transaction pool and warrants peer penalization. + /// + /// Not all error variants are caused by the incorrect composition of the transaction (See also + /// [InvalidPoolTransactionError]) and can be caused by the current state of the transaction + /// pool. For example the transaction pool is already full or the error was caused my an + /// internal error, such as database errors. + /// + /// This function returns true only if the transaction will never make it into the pool because + /// its composition is invalid and the original sender should have detected this as well. This + /// is used to determine whether the original sender should be penalized for sending an + /// erroneous transaction. + #[inline] + pub fn is_bad_transaction(&self) -> bool { + match self { + PoolError::AlreadyImported(_) => { + // already imported but not bad + false + } + PoolError::ReplacementUnderpriced(_) => { + // already imported but not bad + false + } + PoolError::FeeCapBelowMinimumProtocolFeeCap(_, _) => { + // fee cap of the tx below the technical minimum determined by the protocol, see + // [MINIMUM_PROTOCOL_FEE_CAP](reth_primitives::constants::MIN_PROTOCOL_BASE_FEE) + // although this transaction will always be invalid, we do not want to penalize the + // sender because this check simply could not be implemented by the client + false + } + PoolError::SpammerExceededCapacity(_, _) => { + // the sender exceeded the slot capacity, we should not penalize the peer for + // sending the tx because we don't know if all the transactions are sent from the + // same peer, there's also a chance that old transactions haven't been cleared yet + // (pool lags behind) and old transaction still occupy a slot in the pool + false + } + PoolError::DiscardedOnInsert(_) => { + // valid tx but dropped due to size constraints + false + } + PoolError::InvalidTransaction(_, err) => { + // transaction rejected because it violates constraints + err.is_bad_transaction() + } + PoolError::Other(_, _) => { + // internal error unrelated to the transaction + false + } + PoolError::ExistingConflictingTransactionType(_, _, _) => { + // this is not a protocol error but an implementation error since the pool enforces + // exclusivity (blob vs normal tx) for all senders + false + } + } + } +} + +/// Represents all errors that can happen when validating transactions for the pool for EIP-4844 +/// transactions +#[derive(Debug, thiserror::Error)] +pub enum Eip4844PoolTransactionError { + /// Thrown if we're unable to find the blob for a transaction that was previously extracted + #[error("blob sidecar not found for EIP4844 transaction")] + MissingEip4844BlobSidecar, + /// Thrown if an EIP-4844 without any blobs arrives + #[error("blobless blob transaction")] + NoEip4844Blobs, + /// Thrown if an EIP-4844 without any blobs arrives + #[error("too many blobs in transaction: have {have}, permitted {permitted}")] + TooManyEip4844Blobs { + /// Number of blobs the transaction has + have: usize, + /// Number of maximum blobs the transaction can have + permitted: usize, + }, + /// Thrown if validating the blob sidecar for the transaction failed. + #[error(transparent)] + InvalidEip4844Blob(BlobTransactionValidationError), + /// EIP-4844 transactions are only accepted if they're gapless, meaning the previous nonce of + /// the transaction (`tx.nonce -1`) must either be in the pool or match the on chain nonce of + /// the sender. + /// + /// This error is thrown on validation if a valid blob transaction arrives with a nonce that + /// would introduce gap in the nonce sequence. + #[error("Nonce too high.")] + Eip4844NonceGap, +} + +/// Represents errors that can happen when validating transactions for the pool +/// +/// See [TransactionValidator](crate::TransactionValidator). +#[derive(Debug, thiserror::Error)] +pub enum InvalidPoolTransactionError { + /// Hard consensus errors + #[error(transparent)] + Consensus(#[from] InvalidTransactionError), + /// Thrown when a new transaction is added to the pool, but then immediately discarded to + /// respect the size limits of the pool. + #[error("Transaction's gas limit {0} exceeds block's gas limit {1}.")] + ExceedsGasLimit(u64, u64), + /// Thrown when a new transaction is added to the pool, but then immediately discarded to + /// respect the max_init_code_size. + #[error("Transaction's size {0} exceeds max_init_code_size {1}.")] + ExceedsMaxInitCodeSize(usize, usize), + /// Thrown if the input data of a transaction is greater + /// than some meaningful limit a user might use. This is not a consensus error + /// making the transaction invalid, rather a DOS protection. + #[error("Input data too large")] + OversizedData(usize, usize), + /// Thrown if the transaction's fee is below the minimum fee + #[error("transaction underpriced")] + Underpriced, + /// Thrown if the transaction's would require an account to be overdrawn + #[error("transaction overdraws from account")] + Overdraft, + /// Eip-4844 related errors + #[error(transparent)] + Eip4844(#[from] Eip4844PoolTransactionError), + /// Any other error that occurred while inserting/validating that is transaction specific + #[error("{0:?}")] + Other(Box), +} + +// === impl InvalidPoolTransactionError === + +impl InvalidPoolTransactionError { + /// Returns `true` if the error was caused by a transaction that is considered bad in the + /// context of the transaction pool and warrants peer penalization. + /// + /// See [PoolError::is_bad_transaction]. + #[inline] + fn is_bad_transaction(&self) -> bool { + match self { + InvalidPoolTransactionError::Consensus(err) => { + // transaction considered invalid by the consensus rules + // We do not consider the following errors to be erroneous transactions, since they + // depend on dynamic environmental conditions and should not be assumed to have been + // intentionally caused by the sender + match err { + InvalidTransactionError::InsufficientFunds { .. } + | InvalidTransactionError::NonceNotConsistent => { + // transaction could just have arrived late/early + false + } + InvalidTransactionError::GasTooLow + | InvalidTransactionError::GasTooHigh + | InvalidTransactionError::TipAboveFeeCap => { + // these are technically not invalid + false + } + InvalidTransactionError::FeeCapTooLow => { + // dynamic, but not used during validation + false + } + InvalidTransactionError::Eip2930Disabled + | InvalidTransactionError::Eip1559Disabled + | InvalidTransactionError::Eip4844Disabled => { + // settings + false + } + InvalidTransactionError::OldLegacyChainId => true, + InvalidTransactionError::ChainIdMismatch => true, + InvalidTransactionError::GasUintOverflow => true, + InvalidTransactionError::TxTypeNotSupported => true, + InvalidTransactionError::SignerAccountHasBytecode => true, + } + } + InvalidPoolTransactionError::ExceedsGasLimit(_, _) => true, + InvalidPoolTransactionError::ExceedsMaxInitCodeSize(_, _) => true, + InvalidPoolTransactionError::OversizedData(_, _) => true, + InvalidPoolTransactionError::Underpriced => { + // local setting + false + } + InvalidPoolTransactionError::Overdraft => false, + InvalidPoolTransactionError::Other(err) => err.is_bad_transaction(), + InvalidPoolTransactionError::Eip4844(eip4844_err) => { + match eip4844_err { + Eip4844PoolTransactionError::MissingEip4844BlobSidecar => { + // this is only reachable when blob transactions are reinjected and we're + // unable to find the previously extracted blob + false + } + Eip4844PoolTransactionError::InvalidEip4844Blob(_) => { + // This is only reachable when the blob is invalid + true + } + Eip4844PoolTransactionError::Eip4844NonceGap => { + // it is possible that the pool sees `nonce n` before `nonce n-1` and this + // is only thrown for valid(good) blob transactions + false + } + Eip4844PoolTransactionError::NoEip4844Blobs => { + // this is a malformed transaction and should not be sent over the network + true + } + Eip4844PoolTransactionError::TooManyEip4844Blobs { .. } => { + // this is a malformed transaction and should not be sent over the network + true + } + } + } + } + } +} diff --git a/module-system/module-implementations/sov-evm/src/evm/error/result.rs b/module-system/module-implementations/sov-evm/src/evm/error/result.rs new file mode 100644 index 000000000..b6026b11f --- /dev/null +++ b/module-system/module-implementations/sov-evm/src/evm/error/result.rs @@ -0,0 +1,90 @@ +//! Additional helpers for converting errors. + +use std::fmt::Display; + +use jsonrpsee::core::RpcResult; + +/// Helper trait to easily convert various `Result` types into [`RpcResult`] +pub(crate) trait ToRpcResult { + /// Converts the error of the [Result] to an [RpcResult] via the `Err` [Display] impl. + fn to_rpc_result(self) -> RpcResult + where + Err: Display, + Self: Sized, + { + self.map_internal_err(|err| err.to_string()) + } + + /// Converts this type into an [`RpcResult`] + fn map_rpc_err<'a, F, M>(self, op: F) -> RpcResult + where + F: FnOnce(Err) -> (i32, M, Option<&'a [u8]>), + M: Into; + + /// Converts this type into an [`RpcResult`] with the + /// [`jsonrpsee::types::error::INTERNAL_ERROR_CODE` and the given message. + fn map_internal_err(self, op: F) -> RpcResult + where + F: FnOnce(Err) -> M, + M: Into; + + /// Converts this type into an [`RpcResult`] with the + /// [`jsonrpsee::types::error::INTERNAL_ERROR_CODE`] and given message and data. + fn map_internal_err_with_data<'a, F, M>(self, op: F) -> RpcResult + where + F: FnOnce(Err) -> (M, &'a [u8]), + M: Into; + + /// Adds a message to the error variant and returns an internal Error. + /// + /// This is shorthand for `Self::map_internal_err(|err| format!("{msg}: {err}"))`. + fn with_message(self, msg: &str) -> RpcResult; +} + +/// An extension to used to apply error conversions to various result types +pub(crate) trait ToRpcResultExt { + /// The `Ok` variant of the [RpcResult] + type Ok; + + /// Maps the `Ok` variant of this type into [Self::Ok] and maps the `Err` variant into rpc + /// error. + fn map_ok_or_rpc_err(self) -> RpcResult<::Ok>; +} + +/// Constructs an invalid params JSON-RPC error. +pub(crate) fn invalid_params_rpc_err( + msg: impl Into, +) -> jsonrpsee::types::error::ErrorObject<'static> { + rpc_err(jsonrpsee::types::error::INVALID_PARAMS_CODE, msg, None) +} + +/// Constructs an internal JSON-RPC error. +pub(crate) fn internal_rpc_err( + msg: impl Into, +) -> jsonrpsee::types::error::ErrorObject<'static> { + rpc_err(jsonrpsee::types::error::INTERNAL_ERROR_CODE, msg, None) +} + +/// Constructs an internal JSON-RPC error with code and message +pub(crate) fn rpc_error_with_code( + code: i32, + msg: impl Into, +) -> jsonrpsee::types::error::ErrorObject<'static> { + rpc_err(code, msg, None) +} + +/// Constructs a JSON-RPC error, consisting of `code`, `message` and optional `data`. +pub(crate) fn rpc_err( + code: i32, + msg: impl Into, + data: Option<&[u8]>, +) -> jsonrpsee::types::error::ErrorObject<'static> { + jsonrpsee::types::error::ErrorObject::owned( + code, + msg.into(), + data.map(|data| { + jsonrpsee::core::to_json_raw_value(&format!("0x{}", hex::encode(data))) + .expect("serializing String does fail") + }), + ) +} diff --git a/module-system/module-implementations/sov-evm/src/evm/error/rpc.rs b/module-system/module-implementations/sov-evm/src/evm/error/rpc.rs new file mode 100644 index 000000000..cb78fa04f --- /dev/null +++ b/module-system/module-implementations/sov-evm/src/evm/error/rpc.rs @@ -0,0 +1,642 @@ +//! Implementation specific Errors for the `eth_` namespace. + +use std::time::Duration; + +use jsonrpsee::core::Error as RpcError; +use jsonrpsee::types::error::CALL_EXECUTION_FAILED_CODE; +use jsonrpsee::types::ErrorObject; +use reth_interfaces::RethError; +use reth_primitives::abi::decode_revert_reason; +use reth_primitives::{Address, Bytes, U256}; +use reth_revm::tracing::js::JsInspectorError; +use reth_rpc_types::error::EthRpcErrorCode; +use reth_rpc_types::{BlockError, CallInputError}; +use revm::primitives::{EVMError, ExecutionResult, Halt, InvalidHeader, OutOfGasError}; + +use super::pool::{ + Eip4844PoolTransactionError, InvalidPoolTransactionError, PoolError, PoolTransactionError, +}; +use super::result::{internal_rpc_err, invalid_params_rpc_err, rpc_err, rpc_error_with_code}; + +/// Result alias +pub type EthResult = Result; + +/// Errors that can occur when interacting with the `eth_` namespace +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum EthApiError { + /// When a raw transaction is empty + #[error("Empty transaction data")] + EmptyRawTransactionData, + #[error("Failed to decode signed transaction")] + FailedToDecodeSignedTransaction, + #[error("Invalid transaction signature")] + InvalidTransactionSignature, + #[error(transparent)] + PoolError(RpcPoolError), + #[error("Unknown block number")] + UnknownBlockNumber, + /// Thrown when querying for `finalized` or `safe` block before the merge transition is + /// finalized, + #[error("Unknown block")] + UnknownSafeOrFinalizedBlock, + #[error("Unknown block or tx index")] + UnknownBlockOrTxIndex, + #[error("Invalid block range")] + InvalidBlockRange, + /// An internal error where prevrandao is not set in the evm's environment + #[error("Prevrandao not in th EVM's environment after merge")] + PrevrandaoNotSet, + /// Excess_blob_gas is not set for Cancun and above. + #[error("Excess blob gas missing th EVM's environment after Cancun")] + ExcessBlobGasNotSet, + /// Thrown when a call or transaction request (`eth_call`, `eth_estimateGas`, + /// `eth_sendTransaction`) contains conflicting fields (legacy, EIP-1559) + #[error("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")] + ConflictingFeeFieldsInRequest, + #[error(transparent)] + InvalidTransaction(#[from] RpcInvalidTransactionError), + /// Thrown when constructing an RPC block from a primitive block data failed. + #[error(transparent)] + InvalidBlockData(#[from] BlockError), + /// Thrown when a [AccountOverride](reth_rpc_types::state::AccountOverride) contains + /// conflicting `state` and `stateDiff` fields + #[error("account {0:?} has both 'state' and 'stateDiff'")] + BothStateAndStateDiffInOverride(Address), + /// Other internal error + #[error(transparent)] + Internal(RethError), + /// Error related to signing + #[error(transparent)] + Signing(#[from] SignError), + /// Thrown when a transaction was requested but not matching transaction exists + #[error("transaction not found")] + TransactionNotFound, + /// Some feature is unsupported + #[error("unsupported")] + Unsupported(&'static str), + /// General purpose error for invalid params + #[error("{0}")] + InvalidParams(String), + /// When tracer config does not match the tracer + #[error("invalid tracer config")] + InvalidTracerConfig, + /// Percentile array is invalid + #[error("invalid reward percentiles")] + InvalidRewardPercentiles, + /// Error thrown when a spawned tracing task failed to deliver an anticipated response. + /// + /// This only happens if the tracing task panics and is aborted before it can return a response + /// back to the request handler. + #[error("internal error while tracing")] + InternalTracingError, + /// Error thrown when a spawned blocking task failed to deliver an anticipated response. + #[error("internal eth error")] + InternalEthError, + /// Error thrown when a (tracing) call exceeded the configured timeout. + #[error("execution aborted (timeout = {0:?})")] + ExecutionTimedOut(Duration), + /// Internal Error thrown by the javascript tracer + #[error("{0}")] + InternalJsTracerError(String), + #[error(transparent)] + CallInputError(#[from] CallInputError), +} + +impl From for ErrorObject<'static> { + fn from(error: EthApiError) -> Self { + match error { + EthApiError::FailedToDecodeSignedTransaction + | EthApiError::InvalidTransactionSignature + | EthApiError::EmptyRawTransactionData + | EthApiError::InvalidBlockRange + | EthApiError::ConflictingFeeFieldsInRequest + | EthApiError::Signing(_) + | EthApiError::BothStateAndStateDiffInOverride(_) + | EthApiError::InvalidTracerConfig => invalid_params_rpc_err(error.to_string()), + EthApiError::InvalidTransaction(err) => err.into(), + EthApiError::PoolError(err) => err.into(), + EthApiError::PrevrandaoNotSet + | EthApiError::ExcessBlobGasNotSet + | EthApiError::InvalidBlockData(_) + | EthApiError::Internal(_) + | EthApiError::TransactionNotFound => internal_rpc_err(error.to_string()), + EthApiError::UnknownBlockNumber | EthApiError::UnknownBlockOrTxIndex => { + rpc_error_with_code(EthRpcErrorCode::ResourceNotFound.code(), error.to_string()) + } + EthApiError::UnknownSafeOrFinalizedBlock => { + rpc_error_with_code(EthRpcErrorCode::UnknownBlock.code(), error.to_string()) + } + EthApiError::Unsupported(msg) => internal_rpc_err(msg), + EthApiError::InternalJsTracerError(msg) => internal_rpc_err(msg), + EthApiError::InvalidParams(msg) => invalid_params_rpc_err(msg), + EthApiError::InvalidRewardPercentiles => internal_rpc_err(error.to_string()), + err @ EthApiError::ExecutionTimedOut(_) => { + rpc_error_with_code(CALL_EXECUTION_FAILED_CODE, err.to_string()) + } + err @ EthApiError::InternalTracingError => internal_rpc_err(err.to_string()), + err @ EthApiError::InternalEthError => internal_rpc_err(err.to_string()), + err @ EthApiError::CallInputError(_) => invalid_params_rpc_err(err.to_string()), + } + } +} + +impl From for RpcError { + fn from(error: EthApiError) -> Self { + RpcError::Call(error.into()) + } +} +impl From for EthApiError { + fn from(error: JsInspectorError) -> Self { + match error { + err @ JsInspectorError::JsError(_) => { + EthApiError::InternalJsTracerError(err.to_string()) + } + err => EthApiError::InvalidParams(err.to_string()), + } + } +} + +impl From for EthApiError { + fn from(error: RethError) -> Self { + match error { + RethError::Provider(err) => err.into(), + err => EthApiError::Internal(err), + } + } +} + +impl From for EthApiError { + fn from(error: reth_interfaces::provider::ProviderError) -> Self { + use reth_interfaces::provider::ProviderError; + match error { + ProviderError::HeaderNotFound(_) + | ProviderError::BlockHashNotFound(_) + | ProviderError::BestBlockNotFound + | ProviderError::BlockNumberForTransactionIndexNotFound + | ProviderError::TotalDifficultyNotFound { .. } + | ProviderError::UnknownBlockHash(_) => EthApiError::UnknownBlockNumber, + ProviderError::FinalizedBlockNotFound | ProviderError::SafeBlockNotFound => { + EthApiError::UnknownSafeOrFinalizedBlock + } + err => EthApiError::Internal(err.into()), + } + } +} + +impl From> for EthApiError +where + T: Into, +{ + fn from(err: EVMError) -> Self { + match err { + EVMError::Transaction(err) => RpcInvalidTransactionError::from(err).into(), + EVMError::Header(InvalidHeader::PrevrandaoNotSet) => EthApiError::PrevrandaoNotSet, + EVMError::Header(InvalidHeader::ExcessBlobGasNotSet) => { + EthApiError::ExcessBlobGasNotSet + } + EVMError::Database(err) => err.into(), + } + } +} + +/// An error due to invalid transaction. +/// +/// The only reason this exists is to maintain compatibility with other clients de-facto standard +/// error messages. +/// +/// These error variants can be thrown when the transaction is checked prior to execution. +/// +/// These variants also cover all errors that can be thrown by revm. +/// +/// ## Nomenclature +/// +/// This type is explicitly modeled after geth's error variants and uses +/// `fee cap` for `max_fee_per_gas` +/// `tip` for `max_priority_fee_per_gas` +#[derive(thiserror::Error, Debug)] +pub enum RpcInvalidTransactionError { + /// returned if the nonce of a transaction is lower than the one present in the local chain. + #[error("nonce too low")] + NonceTooLow, + /// returned if the nonce of a transaction is higher than the next one expected based on the + /// local chain. + #[error("nonce too high")] + NonceTooHigh, + /// Returned if the nonce of a transaction is too high + /// Incrementing the nonce would lead to invalid state (overflow) + #[error("nonce has max value")] + NonceMaxValue, + /// thrown if the transaction sender doesn't have enough funds for a transfer + #[error("insufficient funds for transfer")] + InsufficientFundsForTransfer, + /// thrown if creation transaction provides the init code bigger than init code size limit. + #[error("max initcode size exceeded")] + MaxInitCodeSizeExceeded, + /// Represents the inability to cover max cost + value (account balance too low). + #[error("insufficient funds for gas * price + value")] + InsufficientFunds, + /// Thrown when calculating gas usage + #[error("gas uint64 overflow")] + GasUintOverflow, + /// returned if the transaction is specified to use less gas than required to start the + /// invocation. + #[error("intrinsic gas too low")] + GasTooLow, + /// returned if the transaction gas exceeds the limit + #[error("intrinsic gas too high")] + GasTooHigh, + /// thrown if a transaction is not supported in the current network configuration. + #[error("transaction type not supported")] + TxTypeNotSupported, + /// Thrown to ensure no one is able to specify a transaction with a tip higher than the total + /// fee cap. + #[error("max priority fee per gas higher than max fee per gas")] + TipAboveFeeCap, + /// A sanity error to avoid huge numbers specified in the tip field. + #[error("max priority fee per gas higher than 2^256-1")] + TipVeryHigh, + /// A sanity error to avoid huge numbers specified in the fee cap field. + #[error("max fee per gas higher than 2^256-1")] + FeeCapVeryHigh, + /// Thrown post London if the transaction's fee is less than the base fee of the block + #[error("max fee per gas less than block base fee")] + FeeCapTooLow, + /// Thrown if the sender of a transaction is a contract. + #[error("sender not an eoa")] + SenderNoEOA, + /// Thrown during estimate if caller has insufficient funds to cover the tx. + #[error("Out of gas: gas required exceeds allowance: {0:?}")] + BasicOutOfGas(U256), + /// As BasicOutOfGas but thrown when gas exhausts during memory expansion. + #[error("Out of gas: gas exhausts during memory expansion: {0:?}")] + MemoryOutOfGas(U256), + /// As BasicOutOfGas but thrown when gas exhausts during precompiled contract execution. + #[error("Out of gas: gas exhausts during precompiled contract execution: {0:?}")] + PrecompileOutOfGas(U256), + /// revm's Type cast error, U256 casts down to a u64 with overflow + #[error("Out of gas: revm's Type cast error, U256 casts down to a u64 with overflow {0:?}")] + InvalidOperandOutOfGas(U256), + /// Thrown if executing a transaction failed during estimate/call + #[error("{0}")] + Revert(RevertError), + /// Unspecific evm halt error + #[error("EVM error {0:?}")] + EvmHalt(Halt), + /// Invalid chain id set for the transaction. + #[error("Invalid chain id")] + InvalidChainId, + /// The transaction is before Spurious Dragon and has a chain ID + #[error("Transactions before Spurious Dragon should not have a chain ID.")] + OldLegacyChainId, + /// The transitions is before Berlin and has access list + #[error("Transactions before Berlin should not have access list")] + AccessListNotSupported, + /// `max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork. + #[error("max_fee_per_blob_gas is not supported for blocks before the Cancun hardfork.")] + MaxFeePerBlobGasNotSupported, + /// `blob_hashes`/`blob_versioned_hashes` is not supported for blocks before the Cancun + /// hardfork. + #[error("blob_versioned_hashes is not supported for blocks before the Cancun hardfork.")] + BlobVersionedHashesNotSupported, + /// Block `blob_gas_price` is greater than tx-specified `max_fee_per_blob_gas` after Cancun. + #[error("max fee per blob gas less than block blob gas fee")] + BlobFeeCapTooLow, + /// Blob transaction has a versioned hash with an invalid blob + #[error("blob hash version mismatch")] + BlobHashVersionMismatch, + /// Blob transaction has no versioned hashes + #[error("blob transaction missing blob hashes")] + BlobTransactionMissingBlobHashes, + /// Blob transaction has too many blobs + #[error("blob transaction exceeds max blobs per block")] + TooManyBlobs, + /// Blob transaction is a create transaction + #[error("blob transaction is a create transaction")] + BlobTransactionIsCreate, +} + +impl RpcInvalidTransactionError { + /// Returns the rpc error code for this error. + fn error_code(&self) -> i32 { + match self { + RpcInvalidTransactionError::InvalidChainId + | RpcInvalidTransactionError::GasTooLow + | RpcInvalidTransactionError::GasTooHigh => EthRpcErrorCode::InvalidInput.code(), + RpcInvalidTransactionError::Revert(_) => EthRpcErrorCode::ExecutionError.code(), + _ => EthRpcErrorCode::TransactionRejected.code(), + } + } + + /// Converts the halt error + /// + /// Takes the configured gas limit of the transaction which is attached to the error + pub(crate) fn halt(reason: Halt, gas_limit: u64) -> Self { + match reason { + Halt::OutOfGas(err) => RpcInvalidTransactionError::out_of_gas(err, gas_limit), + Halt::NonceOverflow => RpcInvalidTransactionError::NonceMaxValue, + err => RpcInvalidTransactionError::EvmHalt(err), + } + } + + /// Converts the out of gas error + pub(crate) fn out_of_gas(reason: OutOfGasError, gas_limit: u64) -> Self { + let gas_limit = U256::from(gas_limit); + match reason { + OutOfGasError::BasicOutOfGas => RpcInvalidTransactionError::BasicOutOfGas(gas_limit), + OutOfGasError::Memory => RpcInvalidTransactionError::MemoryOutOfGas(gas_limit), + OutOfGasError::Precompile => RpcInvalidTransactionError::PrecompileOutOfGas(gas_limit), + OutOfGasError::InvalidOperand => { + RpcInvalidTransactionError::InvalidOperandOutOfGas(gas_limit) + } + OutOfGasError::MemoryLimit => RpcInvalidTransactionError::MemoryOutOfGas(gas_limit), + } + } +} + +impl From for ErrorObject<'static> { + fn from(err: RpcInvalidTransactionError) -> Self { + match err { + RpcInvalidTransactionError::Revert(revert) => { + // include out data if some + rpc_err( + revert.error_code(), + revert.to_string(), + revert.output.as_ref().map(|out| out.as_ref()), + ) + } + err => rpc_err(err.error_code(), err.to_string(), None), + } + } +} + +impl From for RpcInvalidTransactionError { + fn from(err: revm::primitives::InvalidTransaction) -> Self { + use revm::primitives::InvalidTransaction; + match err { + InvalidTransaction::InvalidChainId => RpcInvalidTransactionError::InvalidChainId, + InvalidTransaction::PriorityFeeGreaterThanMaxFee => { + RpcInvalidTransactionError::TipAboveFeeCap + } + InvalidTransaction::GasPriceLessThanBasefee => RpcInvalidTransactionError::FeeCapTooLow, + InvalidTransaction::CallerGasLimitMoreThanBlock => { + RpcInvalidTransactionError::GasTooHigh + } + InvalidTransaction::CallGasCostMoreThanGasLimit => { + RpcInvalidTransactionError::GasTooHigh + } + InvalidTransaction::RejectCallerWithCode => RpcInvalidTransactionError::SenderNoEOA, + InvalidTransaction::LackOfFundForMaxFee { .. } => { + RpcInvalidTransactionError::InsufficientFunds + } + InvalidTransaction::OverflowPaymentInTransaction => { + RpcInvalidTransactionError::GasUintOverflow + } + InvalidTransaction::NonceOverflowInTransaction => { + RpcInvalidTransactionError::NonceMaxValue + } + InvalidTransaction::CreateInitcodeSizeLimit => { + RpcInvalidTransactionError::MaxInitCodeSizeExceeded + } + InvalidTransaction::NonceTooHigh { .. } => RpcInvalidTransactionError::NonceTooHigh, + InvalidTransaction::NonceTooLow { .. } => RpcInvalidTransactionError::NonceTooLow, + InvalidTransaction::AccessListNotSupported => { + RpcInvalidTransactionError::AccessListNotSupported + } + InvalidTransaction::MaxFeePerBlobGasNotSupported => { + RpcInvalidTransactionError::MaxFeePerBlobGasNotSupported + } + InvalidTransaction::BlobVersionedHashesNotSupported => { + RpcInvalidTransactionError::BlobVersionedHashesNotSupported + } + InvalidTransaction::BlobGasPriceGreaterThanMax => { + RpcInvalidTransactionError::BlobFeeCapTooLow + } + InvalidTransaction::EmptyBlobs => { + RpcInvalidTransactionError::BlobTransactionMissingBlobHashes + } + InvalidTransaction::BlobVersionNotSupported => { + RpcInvalidTransactionError::BlobHashVersionMismatch + } + InvalidTransaction::TooManyBlobs => RpcInvalidTransactionError::TooManyBlobs, + InvalidTransaction::BlobCreateTransaction => { + RpcInvalidTransactionError::BlobTransactionIsCreate + } + } + } +} + +impl From for RpcInvalidTransactionError { + fn from(err: reth_primitives::InvalidTransactionError) -> Self { + use reth_primitives::InvalidTransactionError; + // This conversion is used to convert any transaction errors that could occur inside the + // txpool (e.g. `eth_sendRawTransaction`) to their corresponding RPC + match err { + InvalidTransactionError::InsufficientFunds { .. } => { + RpcInvalidTransactionError::InsufficientFunds + } + InvalidTransactionError::NonceNotConsistent => RpcInvalidTransactionError::NonceTooLow, + InvalidTransactionError::OldLegacyChainId => { + // Note: this should be unreachable since Spurious Dragon now enabled + RpcInvalidTransactionError::OldLegacyChainId + } + InvalidTransactionError::ChainIdMismatch => RpcInvalidTransactionError::InvalidChainId, + InvalidTransactionError::Eip2930Disabled + | InvalidTransactionError::Eip1559Disabled + | InvalidTransactionError::Eip4844Disabled => { + RpcInvalidTransactionError::TxTypeNotSupported + } + InvalidTransactionError::TxTypeNotSupported => { + RpcInvalidTransactionError::TxTypeNotSupported + } + InvalidTransactionError::GasUintOverflow => RpcInvalidTransactionError::GasUintOverflow, + InvalidTransactionError::GasTooLow => RpcInvalidTransactionError::GasTooLow, + InvalidTransactionError::GasTooHigh => RpcInvalidTransactionError::GasTooHigh, + InvalidTransactionError::TipAboveFeeCap => RpcInvalidTransactionError::TipAboveFeeCap, + InvalidTransactionError::FeeCapTooLow => RpcInvalidTransactionError::FeeCapTooLow, + InvalidTransactionError::SignerAccountHasBytecode => { + RpcInvalidTransactionError::SenderNoEOA + } + } + } +} + +/// Represents a reverted transaction and its output data. +/// +/// Displays "execution reverted(: reason)?" if the reason is a string. +#[derive(Debug, Clone)] +pub struct RevertError { + /// The transaction output data + /// + /// Note: this is `None` if output was empty + output: Option, +} + +// === impl RevertError == + +impl RevertError { + /// Wraps the output bytes + /// + /// Note: this is intended to wrap an revm output + pub fn new(output: bytes::Bytes) -> Self { + if output.is_empty() { + Self { output: None } + } else { + Self { + output: Some(output), + } + } + } + + fn error_code(&self) -> i32 { + EthRpcErrorCode::ExecutionError.code() + } +} + +impl std::fmt::Display for RevertError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("execution reverted")?; + if let Some(reason) = self.output.as_ref().and_then(decode_revert_reason) { + write!(f, ": {reason}")?; + } + Ok(()) + } +} + +impl std::error::Error for RevertError {} + +/// A helper error type that's mainly used to mirror `geth` Txpool's error messages +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum RpcPoolError { + #[error("already known")] + AlreadyKnown, + #[error("invalid sender")] + InvalidSender, + #[error("transaction underpriced")] + Underpriced, + #[error("txpool is full")] + TxPoolOverflow, + #[error("replacement transaction underpriced")] + ReplaceUnderpriced, + #[error("exceeds block gas limit")] + ExceedsGasLimit, + #[error("negative value")] + NegativeValue, + #[error("oversized data")] + OversizedData, + #[error("max initcode size exceeded")] + ExceedsMaxInitCodeSize, + #[error(transparent)] + Invalid(#[from] RpcInvalidTransactionError), + /// Custom pool error + #[error("{0:?}")] + PoolTransactionError(Box), + /// Eip-4844 related error + #[error(transparent)] + Eip4844(#[from] Eip4844PoolTransactionError), + /// Thrown if a conflicting transaction type is already in the pool + /// + /// In other words, thrown if a transaction with the same sender that violates the exclusivity + /// constraint (blob vs normal tx) + #[error("address already reserved")] + AddressAlreadyReserved, + #[error(transparent)] + Other(Box), +} + +impl From for ErrorObject<'static> { + fn from(error: RpcPoolError) -> Self { + match error { + RpcPoolError::Invalid(err) => err.into(), + error => internal_rpc_err(error.to_string()), + } + } +} + +impl From for RpcPoolError { + fn from(err: PoolError) -> RpcPoolError { + match err { + PoolError::ReplacementUnderpriced(_) => RpcPoolError::ReplaceUnderpriced, + PoolError::FeeCapBelowMinimumProtocolFeeCap(_, _) => RpcPoolError::Underpriced, + PoolError::SpammerExceededCapacity(_, _) => RpcPoolError::TxPoolOverflow, + PoolError::DiscardedOnInsert(_) => RpcPoolError::TxPoolOverflow, + PoolError::InvalidTransaction(_, err) => err.into(), + PoolError::Other(_, err) => RpcPoolError::Other(err), + PoolError::AlreadyImported(_) => RpcPoolError::AlreadyKnown, + PoolError::ExistingConflictingTransactionType(_, _, _) => { + RpcPoolError::AddressAlreadyReserved + } + } + } +} + +impl From for RpcPoolError { + fn from(err: InvalidPoolTransactionError) -> RpcPoolError { + match err { + InvalidPoolTransactionError::Consensus(err) => RpcPoolError::Invalid(err.into()), + InvalidPoolTransactionError::ExceedsGasLimit(_, _) => RpcPoolError::ExceedsGasLimit, + InvalidPoolTransactionError::ExceedsMaxInitCodeSize(_, _) => { + RpcPoolError::ExceedsMaxInitCodeSize + } + InvalidPoolTransactionError::OversizedData(_, _) => RpcPoolError::OversizedData, + InvalidPoolTransactionError::Underpriced => RpcPoolError::Underpriced, + InvalidPoolTransactionError::Other(err) => RpcPoolError::PoolTransactionError(err), + InvalidPoolTransactionError::Eip4844(err) => RpcPoolError::Eip4844(err), + InvalidPoolTransactionError::Overdraft => { + RpcPoolError::Invalid(RpcInvalidTransactionError::InsufficientFunds) + } + } + } +} + +impl From for EthApiError { + fn from(err: PoolError) -> Self { + EthApiError::PoolError(RpcPoolError::from(err)) + } +} + +/// Errors returned from a sign request. +#[derive(Debug, thiserror::Error)] +pub enum SignError { + /// Error occured while trying to sign data. + #[error("Could not sign")] + CouldNotSign, + /// Signer for requested account not found. + #[error("Unknown account")] + NoAccount, + /// TypedData has invalid format. + #[error("Given typed data is not valid")] + InvalidTypedData, + /// Invalid transaction request in `sign_transaction`. + #[error("Invalid transaction request")] + InvalidTransactionRequest, + /// No chain ID was given. + #[error("No chainid")] + NoChainId, +} + +/// Converts the evm [ExecutionResult] into a result where `Ok` variant is the output bytes if it is +/// [ExecutionResult::Success]. +pub(crate) fn ensure_success(result: ExecutionResult) -> EthResult { + match result { + ExecutionResult::Success { output, .. } => Ok(output.into_data().into()), + ExecutionResult::Revert { output, .. } => { + Err(RpcInvalidTransactionError::Revert(RevertError::new(output)).into()) + } + ExecutionResult::Halt { reason, gas_used } => { + Err(RpcInvalidTransactionError::halt(reason, gas_used).into()) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn timed_out_error() { + let err = EthApiError::ExecutionTimedOut(Duration::from_secs(10)); + assert_eq!(err.to_string(), "execution aborted (timeout = 10s)"); + } +} 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 e8db4432e..3beaa1945 100644 --- a/module-system/module-implementations/sov-evm/src/evm/mod.rs +++ b/module-system/module-implementations/sov-evm/src/evm/mod.rs @@ -4,16 +4,18 @@ use serde::{Deserialize, Serialize}; use sov_modules_api::StateMap; use sov_state::Prefix; +pub(crate) mod call; pub(crate) mod conversions; pub(crate) mod db; mod db_commit; pub(crate) mod db_init; +pub(crate) mod error; pub(crate) mod executor; pub(crate) mod primitive_types; #[cfg(test)] mod tests; -pub(crate) use conversions::prepare_call_env; +pub(crate) use call::prepare_call_env; pub use primitive_types::RlpEvmTransaction; use sov_state::codec::BcsCodec; diff --git a/module-system/module-implementations/sov-evm/src/evm/primitive_types.rs b/module-system/module-implementations/sov-evm/src/evm/primitive_types.rs index 4dcb6427b..a384f99a8 100644 --- a/module-system/module-implementations/sov-evm/src/evm/primitive_types.rs +++ b/module-system/module-implementations/sov-evm/src/evm/primitive_types.rs @@ -1,9 +1,7 @@ use std::ops::Range; -use jsonrpsee::types::ErrorObject; use reth_primitives::{Address, Header, SealedHeader, TransactionSigned, H256}; use revm::primitives::EVMError; -use thiserror::Error; #[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Clone)] pub(crate) struct BlockEnv { @@ -30,6 +28,20 @@ impl Default for BlockEnv { } } +// BlockEnv from SealedBlock +impl From<&SealedBlock> for BlockEnv { + fn from(block: &SealedBlock) -> Self { + Self { + number: block.header.number, + coinbase: block.header.beneficiary, + timestamp: block.header.timestamp, + prevrandao: block.header.mix_hash, + basefee: block.header.base_fee_per_gas.unwrap_or_default(), + gas_limit: block.header.gas_limit, + } + } +} + /// Rlp encoded evm transaction. #[cfg_attr( feature = "native", @@ -106,27 +118,3 @@ pub(crate) struct Receipt { pub(crate) log_index_start: u64, pub(crate) error: Option>, } - -/// Tx conversion error. -#[derive(Error, Debug)] -pub enum RawEvmTxConversionError { - /// Transaction is empty, - #[error("Empty raw transaction data")] - EmptyRawTransactionData, - /// Decoding error. - #[error("Failed to decode signed transaction")] - FailedToDecodeSignedTransaction, - /// Unable to recover signer. - #[error("Failed to recover signer")] - FailedToRecoverSigner, -} - -impl From for jsonrpsee::core::Error { - fn from(error: RawEvmTxConversionError) -> Self { - jsonrpsee::core::Error::Call(ErrorObject::owned::<&[u8]>( - jsonrpsee::types::error::INVALID_PARAMS_CODE, - error.to_string(), - None, - )) - } -} 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 e2eca6c27..a5778ec3f 100644 --- a/module-system/module-implementations/sov-evm/src/evm/tests.rs +++ b/module-system/module-implementations/sov-evm/src/evm/tests.rs @@ -118,7 +118,25 @@ fn simple_contract_execution + DatabaseCommit + ethereum_types::U256::from(out.as_ref()) }; - assert_eq!(set_arg, get_res.as_u32()) + assert_eq!(set_arg, get_res.as_u32()); + + { + let failing_call_data = contract.failing_function_call_data(); + + let tx = dev_signer + .sign_default_transaction( + TransactionKind::Call(contract_address.into()), + hex::decode(hex::encode(&failing_call_data)).unwrap(), + 4, + ) + .unwrap(); + + let tx = &tx.try_into().unwrap(); + let result = + executor::execute_tx(&mut evm_db, &BlockEnv::default(), tx, cfg_env.clone()).unwrap(); + + assert!(matches!(result, ExecutionResult::Revert { .. })); + } } fn contract_address(result: &ExecutionResult) -> Option { diff --git a/module-system/module-implementations/sov-evm/src/lib.rs b/module-system/module-implementations/sov-evm/src/lib.rs index 44b70d2b8..ea65e39be 100644 --- a/module-system/module-implementations/sov-evm/src/lib.rs +++ b/module-system/module-implementations/sov-evm/src/lib.rs @@ -9,7 +9,7 @@ mod genesis; #[cfg(feature = "experimental")] mod hooks; #[cfg(feature = "experimental")] -pub use {call::*, evm::*, genesis::*, hooks::*, primitive_types::RawEvmTxConversionError}; +pub use {call::*, error::rpc::EthApiError, evm::*, genesis::*, hooks::*}; #[cfg(feature = "native")] #[cfg(feature = "experimental")] mod query; diff --git a/module-system/module-implementations/sov-evm/src/query.rs b/module-system/module-implementations/sov-evm/src/query.rs index 65b02711a..e2314465a 100644 --- a/module-system/module-implementations/sov-evm/src/query.rs +++ b/module-system/module-implementations/sov-evm/src/query.rs @@ -8,8 +8,9 @@ use sov_modules_api::WorkingSet; use tracing::info; use crate::call::get_cfg_env; +use crate::error::rpc::ensure_success; use crate::evm::db::EvmDb; -use crate::evm::primitive_types::{Receipt, SealedBlock, TransactionSignedAndRecovered}; +use crate::evm::primitive_types::{BlockEnv, Receipt, SealedBlock, TransactionSignedAndRecovered}; use crate::evm::{executor, prepare_call_env}; use crate::Evm; @@ -208,34 +209,38 @@ impl Evm { } /// Handler for: `eth_call` - // https://github.com/paradigmxyz/reth/blob/f577e147807a783438a3f16aad968b4396274483/crates/rpc/rpc/src/eth/api/transactions.rs#L502 - // https://github.com/paradigmxyz/reth/blob/main/crates/rpc/rpc-types/src/eth/call.rs#L7 - // TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/502 + //https://github.com/paradigmxyz/reth/blob/f577e147807a783438a3f16aad968b4396274483/crates/rpc/rpc/src/eth/api/transactions.rs#L502 + //https://github.com/paradigmxyz/reth/blob/main/crates/rpc/rpc-types/src/eth/call.rs#L7 #[rpc_method(name = "call")] pub fn get_call( &self, request: reth_rpc_types::CallRequest, - _block_number: Option, + block_number: Option, _state_overrides: Option, _block_overrides: Option>, working_set: &mut WorkingSet, ) -> RpcResult { info!("evm module: eth_call"); - let tx_env = prepare_call_env(request); + let block_env = match block_number { + Some(ref block_number) if block_number == "pending" => { + self.block_env.get(working_set).unwrap_or_default().clone() + } + _ => { + let block = self.get_sealed_block_by_number(block_number, working_set); + BlockEnv::from(&block) + } + }; + + let tx_env = prepare_call_env(&block_env, request.clone()).unwrap(); - let block_env = self.block_env.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(get_cfg_env_template())); let evm_db: EvmDb<'_, C> = self.get_db(working_set); - // TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/505 let result = executor::inspect(evm_db, &block_env, tx_env, cfg_env).unwrap(); - let output = match result.result { - revm::primitives::ExecutionResult::Success { output, .. } => output, - _ => todo!(), - }; - Ok(output.into_data().into()) + + Ok(ensure_success(result.result)?) } /// Handler for: `eth_blockNumber` diff --git a/module-system/module-implementations/sov-evm/src/smart_contracts/simple_storage_contract.rs b/module-system/module-implementations/sov-evm/src/smart_contracts/simple_storage_contract.rs index aba5f290e..288049a5a 100644 --- a/module-system/module-implementations/sov-evm/src/smart_contracts/simple_storage_contract.rs +++ b/module-system/module-implementations/sov-evm/src/smart_contracts/simple_storage_contract.rs @@ -64,4 +64,11 @@ impl SimpleStorageContract { pub fn get_call_data(&self) -> Bytes { self.base_contract.encode("get", ()).unwrap() } + + /// Failing call data to test revert. + pub fn failing_function_call_data(&self) -> Bytes { + // Some random function signature. + let data = hex::decode("a5643bf2").unwrap(); + Bytes::from(data) + } } diff --git a/module-system/module-implementations/sov-evm/src/tests/tx_tests.rs b/module-system/module-implementations/sov-evm/src/tests/tx_tests.rs index 45c26da62..c6ce6239a 100644 --- a/module-system/module-implementations/sov-evm/src/tests/tx_tests.rs +++ b/module-system/module-implementations/sov-evm/src/tests/tx_tests.rs @@ -11,6 +11,7 @@ use revm::primitives::{TransactTo, TxEnv}; use crate::evm::prepare_call_env; use crate::evm::primitive_types::TransactionSignedAndRecovered; +use crate::primitive_types::{Block, BlockEnv}; #[tokio::test] async fn tx_rlp_encoding_test() -> Result<(), Box> { @@ -89,7 +90,9 @@ fn prepare_call_env_conversion() { max_fee_per_blob_gas: None, }; - let tx_env = prepare_call_env(request); + let block_env = BlockEnv::default(); + + let tx_env = prepare_call_env(&block_env, request).unwrap(); let expected = TxEnv { caller: from, gas_price: U256::from(100u64), @@ -119,3 +122,25 @@ fn prepare_call_env_conversion() { assert_eq!(tx_env.nonce, expected.nonce); assert_eq!(tx_env.access_list, expected.access_list); } + +#[test] +fn prepare_call_block_env() { + let block = Block { + header: Default::default(), + transactions: Default::default(), + }; + + let sealed_block = &block.clone().seal(); + + let block_env = BlockEnv::from(sealed_block); + + assert_eq!(block_env.number, block.header.number); + assert_eq!(block_env.coinbase, block.header.beneficiary); + assert_eq!(block_env.timestamp, block.header.timestamp); + assert_eq!( + block_env.basefee, + block.header.base_fee_per_gas.unwrap_or_default() + ); + assert_eq!(block_env.gas_limit, block.header.gas_limit); + assert_eq!(block_env.prevrandao, block.header.mix_hash); +} From 7d08dcee8cddd48766d54a222b073ee9ccc08ca8 Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Wed, 27 Sep 2023 18:32:56 +0530 Subject: [PATCH 13/34] cargo lock --- Cargo.lock | 24 +--- examples/demo-prover/methods/guest/Cargo.lock | 130 +++++++----------- 2 files changed, 56 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12f3cf696..3e9c01731 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3943,6 +3943,8 @@ checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "librocksdb-sys" version = "0.11.0+8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" dependencies = [ "bindgen 0.65.1", "bzip2-sys", @@ -4276,26 +4278,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" -[[package]] -name = "nft-utils" -version = "0.2.0" -dependencies = [ - "anyhow", - "borsh", - "demo-stf", - "jsonrpsee", - "schemars", - "serde", - "serde_json", - "sov-cli", - "sov-modules-api", - "sov-modules-macros", - "sov-nft-module", - "sov-rollup-interface", - "sov-sequencer", - "tokio", -] - [[package]] name = "nibble_vec" version = "0.1.0" @@ -6316,6 +6298,8 @@ dependencies = [ [[package]] name = "rocksdb" version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" dependencies = [ "libc", "librocksdb-sys", diff --git a/examples/demo-prover/methods/guest/Cargo.lock b/examples/demo-prover/methods/guest/Cargo.lock index ee279c238..3bdff2f9a 100644 --- a/examples/demo-prover/methods/guest/Cargo.lock +++ b/examples/demo-prover/methods/guest/Cargo.lock @@ -620,7 +620,7 @@ dependencies = [ "bytes", "hex", "informalsystems-pbjson", - "prost", + "prost 0.11.9", "ripemd", "serde", "sha2 0.10.6", @@ -698,12 +698,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.148" @@ -846,12 +840,12 @@ checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" [[package]] name = "prettyplease" -version = "0.1.25" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 1.0.109", + "syn 2.0.37", ] [[package]] @@ -860,7 +854,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml 0.5.11", + "toml", ] [[package]] @@ -879,27 +873,37 @@ 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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fdd22f3b9c31b53c060df4a0613a1c7f062d4115a2b984dd15b1858f7e340d" +dependencies = [ + "bytes", + "prost-derive 0.12.1", ] [[package]] name = "prost-build" -version = "0.11.9" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac" dependencies = [ "bytes", "heck", "itertools", - "lazy_static", "log", "multimap", + "once_cell", "petgraph", "prettyplease", - "prost", - "prost-types", + "prost 0.12.1", + "prost-types 0.12.1", "regex", - "syn 1.0.109", + "syn 2.0.37", "tempfile", "which", ] @@ -917,13 +921,35 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "prost-types" version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "prost", + "prost 0.11.9", +] + +[[package]] +name = "prost-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" +dependencies = [ + "prost 0.12.1", ] [[package]] @@ -1198,15 +1224,6 @@ dependencies = [ "syn 2.0.37", ] -[[package]] -name = "serde_spanned" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" -dependencies = [ - "serde", -] - [[package]] name = "sha2" version = "0.9.9" @@ -1296,9 +1313,9 @@ dependencies = [ "hex", "hex-literal", "nmt-rs", - "prost", + "prost 0.11.9", "prost-build", - "prost-types", + "prost-types 0.11.9", "risc0-zkvm", "risc0-zkvm-platform", "serde", @@ -1387,8 +1404,8 @@ dependencies = [ "proc-macro2", "quote", "schemars", + "serde_json", "syn 1.0.109", - "toml 0.8.0", ] [[package]] @@ -1599,8 +1616,8 @@ dependencies = [ "futures", "num-traits", "once_cell", - "prost", - "prost-types", + "prost 0.11.9", + "prost-types 0.11.9", "serde", "serde_bytes", "serde_json", @@ -1624,8 +1641,8 @@ dependencies = [ "flex-error", "num-derive 0.3.3", "num-traits", - "prost", - "prost-types", + "prost 0.11.9", + "prost-types 0.11.9", "serde", "serde_bytes", "subtle-encoding", @@ -1688,40 +1705,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - [[package]] name = "tracing" version = "0.1.37" @@ -1856,15 +1839,6 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" -[[package]] -name = "winnow" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" -dependencies = [ - "memchr", -] - [[package]] name = "zeroize" version = "1.6.0" From 425c4802c09a3dd2293a0d440214c831bd73e38e Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Thu, 28 Sep 2023 11:13:18 +0530 Subject: [PATCH 14/34] some fixes --- Cargo.lock | 21 ++ Cargo.toml | 1 + full-node/sov-sequencer/src/utils.rs | 4 +- .../sov-nft-module/Cargo.toml | 7 +- .../sov-nft-module/offchain_readme.md | 4 + .../sov-nft-module/src/call.rs | 27 +- .../sov-nft-module/src/init_db.sql | 51 ++-- .../sov-nft-module/src/offchain.rs | 168 ++++++------- module-system/sov-modules-macros/src/lib.rs | 29 +++ utils/nft-utils/src/main.rs | 233 +++++++++++------- 10 files changed, 317 insertions(+), 228 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e9c01731..e81a633a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4278,6 +4278,26 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nft-utils" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "demo-stf", + "jsonrpsee", + "schemars", + "serde", + "serde_json", + "sov-cli", + "sov-modules-api", + "sov-modules-macros", + "sov-nft-module", + "sov-rollup-interface", + "sov-sequencer", + "tokio", +] + [[package]] name = "nibble_vec" version = "0.1.0" @@ -7497,6 +7517,7 @@ dependencies = [ "sov-state", "tempfile", "tokio", + "tracing", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a0591c027..aa556b226 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ "utils/zk-cycle-macros", "utils/zk-cycle-utils", + "utils/nft-utils", "utils/bashtestmd", "utils/nft-utils", diff --git a/full-node/sov-sequencer/src/utils.rs b/full-node/sov-sequencer/src/utils.rs index 8c21860c4..56a42311e 100644 --- a/full-node/sov-sequencer/src/utils.rs +++ b/full-node/sov-sequencer/src/utils.rs @@ -42,14 +42,14 @@ impl SimpleClient { pub async fn send_transactions( &self, txs: Vec, - micro_batch: Option, + chunk_size: Option, ) -> Result<(), anyhow::Error> { let serialized_txs: Vec> = txs .into_iter() .map(|tx| tx.try_to_vec()) .collect::>()?; - match micro_batch { + match chunk_size { Some(batch_size) => { for chunk in serialized_txs.chunks(batch_size) { let response: String = self diff --git a/module-system/module-implementations/sov-nft-module/Cargo.toml b/module-system/module-implementations/sov-nft-module/Cargo.toml index 165745de2..5732923fb 100644 --- a/module-system/module-implementations/sov-nft-module/Cargo.toml +++ b/module-system/module-implementations/sov-nft-module/Cargo.toml @@ -21,8 +21,9 @@ sov-modules-api = { path = "../../sov-modules-api" } sov-modules-macros = {path = "../../sov-modules-macros"} sov-state = { path = "../../sov-state" } -postgres = {version = "0.19.7", optional = true} -tokio = {version = "1.32.0", features=["full"],optional = true} +postgres = { version = "0.19.7", optional = true } +tokio = { version = "1.32.0", features=["full"], optional = true } +tracing = { workspace = true, optional = true } [dev-dependencies] sov-rollup-interface = { path = "../../../rollup-interface" } @@ -32,7 +33,7 @@ sov-nft-module = { version = "*", features = ["native"], path = "." } [features] default = [] -offchain = ["postgres","tokio"] +offchain = ["postgres","tokio","tracing"] serde = ["dep:serde"] native = ["serde", "serde_json", "jsonrpsee", "schemars", "sov-state/native", "sov-modules-api/native", ] test = ["native"] diff --git a/module-system/module-implementations/sov-nft-module/offchain_readme.md b/module-system/module-implementations/sov-nft-module/offchain_readme.md index 340238117..3a4345ddf 100644 --- a/module-system/module-implementations/sov-nft-module/offchain_readme.md +++ b/module-system/module-implementations/sov-nft-module/offchain_readme.md @@ -1,5 +1,9 @@ ## Offchain testing +### Introduction +This readme outlines the steps to demonstrate the offchain processing functionality that is part of the `sov-nft-module` + +### Steps * Install postgres on your system * Start the postgres terminal * Create the tables necessary for offchain processing diff --git a/module-system/module-implementations/sov-nft-module/src/call.rs b/module-system/module-implementations/sov-nft-module/src/call.rs index ce0fc232b..028d62498 100644 --- a/module-system/module-implementations/sov-nft-module/src/call.rs +++ b/module-system/module-implementations/sov-nft-module/src/call.rs @@ -2,7 +2,7 @@ use anyhow::Result; use sov_modules_api::{CallResponse, Context, WorkingSet}; use crate::address::UserAddress; -use crate::offchain::{track_collection, track_nft, update_top_owners}; +use crate::offchain::{update_collection, update_nft}; use crate::{Collection, CollectionAddress, Nft, NftIdentifier, NonFungibleToken, TokenId}; #[cfg_attr( @@ -88,8 +88,7 @@ impl NonFungibleToken { )?; self.collections .set(&collection_address, &collection, working_set); - std::thread::sleep(std::time::Duration::from_millis(300)); - track_collection(&collection); + update_collection(&collection); Ok(CallResponse::default()) } @@ -110,7 +109,7 @@ impl NonFungibleToken { collection.set_collection_uri(collection_uri); self.collections .set(&collection_address, collection.inner(), working_set); - track_collection(collection.inner()); + update_collection(collection.inner()); Ok(CallResponse::default()) } @@ -130,7 +129,7 @@ impl NonFungibleToken { collection.freeze(); self.collections .set(&collection_address, collection.inner(), working_set); - track_collection(collection.inner()); + update_collection(collection.inner()); Ok(CallResponse::default()) } @@ -170,13 +169,8 @@ impl NonFungibleToken { self.collections .set(&collection_address, collection.inner(), working_set); - track_collection(collection.inner()); - track_nft(&new_nft); - update_top_owners( - &collection_address, - Some(&[(mint_to_address.to_string(), 1)]), - None, - ); + update_collection(collection.inner()); + update_nft(&new_nft, None); Ok(CallResponse::default()) } @@ -198,12 +192,7 @@ impl NonFungibleToken { owned_nft.inner(), working_set, ); - track_nft(owned_nft.inner()); - update_top_owners( - collection_address, - Some(&[(to.to_string(), 1)]), - Some(&[(original_owner.to_string(), 1)]), - ); + update_nft(owned_nft.inner(), Some(original_owner.clone())); Ok(CallResponse::default()) } @@ -235,7 +224,7 @@ impl NonFungibleToken { mutable_nft.inner(), working_set, ); - track_nft(mutable_nft.inner()); + update_nft(mutable_nft.inner(), None); Ok(CallResponse::default()) } } diff --git a/module-system/module-implementations/sov-nft-module/src/init_db.sql b/module-system/module-implementations/sov-nft-module/src/init_db.sql index 8caa89a72..ed89fddad 100644 --- a/module-system/module-implementations/sov-nft-module/src/init_db.sql +++ b/module-system/module-implementations/sov-nft-module/src/init_db.sql @@ -4,44 +4,47 @@ DROP TABLE IF EXISTS nfts CASCADE; DROP TABLE IF EXISTS collections CASCADE; -- Create collection table -CREATE TABLE collections ( - collection_address TEXT PRIMARY KEY, - collection_name TEXT NOT NULL, - creator_address TEXT NOT NULL, - frozen BOOLEAN NOT NULL, - metadata_url TEXT, - supply BIGINT NOT NULL +CREATE TABLE collections +( + collection_address TEXT PRIMARY KEY, + collection_name TEXT NOT NULL, + creator_address TEXT NOT NULL, + frozen BOOLEAN NOT NULL, + metadata_url TEXT, + supply BIGINT NOT NULL ); -- Create index on creator_address to quickly find collections by a creator -CREATE INDEX idx_creator ON collections(creator_address); +CREATE INDEX idx_creator ON collections (creator_address); -- Create nft table -CREATE TABLE nfts ( - collection_address TEXT NOT NULL, - nft_id BIGINT NOT NULL, - metadata_url TEXT, - owner TEXT NOT NULL, - frozen BOOLEAN NOT NULL, - PRIMARY KEY (collection_address, nft_id) +CREATE TABLE nfts +( + collection_address TEXT NOT NULL, + nft_id BIGINT NOT NULL, + metadata_url TEXT, + owner TEXT NOT NULL, + frozen BOOLEAN NOT NULL, + PRIMARY KEY (collection_address, nft_id) ); -- Create index on owner to quickly find NFTs owned by a particular address -CREATE INDEX idx_nft_owner ON nfts(owner); +CREATE INDEX idx_nft_owner ON nfts (owner); -- Create index on collection_address to quickly find NFTs belonging to a particular collection -CREATE INDEX idx_nft_collection ON nfts(collection_address); +CREATE INDEX idx_nft_collection ON nfts (collection_address); -- Create top_owners table -CREATE TABLE top_owners ( - owner TEXT NOT NULL, - collection_address TEXT NOT NULL, - count BIGINT NOT NULL, - PRIMARY KEY (owner, collection_address) +CREATE TABLE top_owners +( + owner TEXT NOT NULL, + collection_address TEXT NOT NULL, + count BIGINT NOT NULL, + PRIMARY KEY (owner, collection_address) ); -- Create index on collection_address to quickly find top owners in a particular collection -CREATE INDEX idx_top_owners_collection ON top_owners(collection_address); +CREATE INDEX idx_top_owners_collection ON top_owners (collection_address); -- Create index on count to quickly find top owners by count (optional) -CREATE INDEX idx_top_owners_count ON top_owners(count); +CREATE INDEX idx_top_owners_count ON top_owners (count); diff --git a/module-system/module-implementations/sov-nft-module/src/offchain.rs b/module-system/module-implementations/sov-nft-module/src/offchain.rs index 01cc2c654..6117214b8 100644 --- a/module-system/module-implementations/sov-nft-module/src/offchain.rs +++ b/module-system/module-implementations/sov-nft-module/src/offchain.rs @@ -4,20 +4,14 @@ use sov_modules_macros::offchain; #[cfg(feature = "offchain")] use crate::utils::get_collection_address; -use crate::{Collection, CollectionAddress, Nft}; - -// CREATE TABLE collection ( -// collection_address TEXT PRIMARY KEY, -// collection_name TEXT NOT NULL, -// creator_address TEXT NOT NULL, -// frozen BOOLEAN NOT NULL, -// metadata_url TEXT, -// supply BIGINT NOT NULL -// ); +#[cfg(feature = "offchain")] +use crate::CollectionAddress; +use crate::{Collection, Nft, OwnerAddress}; +/// Syncs a collection to the corresponding table "collections" in postgres #[offchain] -pub fn track_collection(collection: &Collection) { - // data extraction +pub fn update_collection(collection: &Collection) { + // Extract the necessary metadata from the collection let collection_name = collection.get_name(); let creator_address = collection.get_creator(); let frozen = collection.is_frozen(); @@ -53,116 +47,96 @@ pub fn track_collection(collection: &Collection) ], ); if let Err(e) = result { - println!("Failed to execute query: {}", e); + tracing::error!("Failed to execute query: {}", e); } } Err(e) => { - println!("Failed to connect to the database: {}", e); + tracing::error!("Failed to connect to the database: {}", e); } } } else { - println!("Environment variable POSTGRES_CONNECTION_STRING is not set"); + tracing::error!("Environment variable POSTGRES_CONNECTION_STRING is not set"); } }) } -// CREATE TABLE nft ( -// collection_address TEXT NOT NULL, -// nft_id BIGINT NOT NULL, -// metadata_url TEXT, -// owner TEXT NOT NULL, -// frozen BOOLEAN NOT NULL, -// PRIMARY KEY (collection_address, nft_id) -// ); - +/// Syncs an NFT to the corresponding table "nfts" in postgres +/// Additionally, this function also has logic to track the counts of NFTs held by each user +/// in each collection. #[offchain] -pub fn track_nft(nft: &Nft) { +pub fn update_nft(nft: &Nft, old_owner: Option>) { let collection_address = nft.get_collection_address().to_string(); let nft_id = nft.get_token_id(); - let owner = nft.get_owner().to_string(); + let new_owner_str = nft.get_owner().to_string(); let frozen = nft.is_frozen(); let metadata_url = nft.get_token_uri(); - tokio::task::block_in_place(|| { - if let Ok(conn_string) = std::env::var("POSTGRES_CONNECTION_STRING") { - match postgres::Client::connect(&conn_string, NoTls) { - Ok(mut client) => { - let result = client.execute( - "INSERT INTO nfts (\ - collection_address, nft_id, metadata_url,\ - owner, frozen)\ - VALUES ($1, $2, $3, $4, $5)\ - ON CONFLICT (collection_address, nft_id)\ - DO UPDATE SET metadata_url = EXCLUDED.metadata_url,\ - owner = EXCLUDED.owner,\ - frozen = EXCLUDED.frozen", - &[ - &collection_address, - &(nft_id as i64), - &metadata_url, - &owner, - &frozen, - ], - ); - if let Err(e) = result { - println!("Failed to execute query: {}", e); - } - } - Err(e) => { - println!("Failed to connect to the database: {}", e); - } - } - } else { - println!("Environment variable POSTGRES_CONNECTION_STRING is not set"); - } - }) -} - -// CREATE TABLE top_owners ( -// owner TEXT NOT NULL, -// collection_address TEXT NOT NULL, -// count BIGINT NOT NULL, -// PRIMARY KEY (owner, collection_address) -// ); + let old_owner_address = old_owner.map(|x| x.to_string()); -#[offchain] -pub fn update_top_owners( - collection_address: &CollectionAddress, - owner_count_incr: Option<&[(String, u64)]>, - owner_count_decr: Option<&[(String, u64)]>, -) { - let collection_address_str = collection_address.to_string(); tokio::task::block_in_place(|| { if let Ok(conn_string) = std::env::var("POSTGRES_CONNECTION_STRING") { let mut client = postgres::Client::connect(&conn_string, NoTls).unwrap(); - // Handle increments if provided - if let Some(increments) = owner_count_incr { - for (owner, increment_value) in increments { - let result = client.execute( - "INSERT INTO top_owners (owner, collection_address, count) VALUES ($1, $2, $3) \ - ON CONFLICT (owner, collection_address) \ - DO UPDATE SET count = top_owners.count + EXCLUDED.count", - &[&&owner, &collection_address_str, &(*increment_value as i64)], + // Check current owner in the database for the NFT + let rows = client + .query( + "SELECT owner FROM nfts WHERE collection_address = $1 AND nft_id = $2", + &[&collection_address, &(nft_id as i64)], + ) + .unwrap(); + + let db_owner: Option = rows.get(0).map(|row| row.get(0)); + + // Handle ownership change logic for top_owners table + if let Some(db_owner_str) = db_owner { + if old_owner_address.is_none() { + // This means it's a mint operation but the NFT already exists in the table. + // Do nothing as we shouldn't increment in this scenario. + } else if old_owner_address.as_ref() != Some(&new_owner_str) { + // Transfer occurred + + // Decrement count for the database owner (which would be the old owner in a transfer scenario) + let _ = client.execute( + "UPDATE top_owners SET count = count - 1 \ + WHERE owner = $1 AND collection_address = $2 AND count > 0", + &[&db_owner_str, &collection_address], ); - if let Err(e) = result { - eprintln!("Failed to execute query: {}", e); - } - } - } - // Handle decrements if provided - if let Some(decrements) = owner_count_decr { - for (owner, decrement_value) in decrements { - let result = client.execute( - "UPDATE top_owners SET count = count - $3 \ - WHERE owner = $1 AND collection_address = $2 AND count >= $3", - &[&owner, &collection_address_str, &(*decrement_value as i64)], + // Increment count for new owner + let _ = client.execute( + "INSERT INTO top_owners (owner, collection_address, count) VALUES ($1, $2, 1) \ + ON CONFLICT (owner, collection_address) \ + DO UPDATE SET count = top_owners.count + 1", + &[&new_owner_str, &collection_address], ); - if let Err(e) = result { - eprintln!("Failed to execute query: {}", e); - } } + } else if old_owner_address.is_none() { + // Mint operation, and NFT doesn't exist in the database. Increment for the new owner. + let _ = client.execute( + "INSERT INTO top_owners (owner, collection_address, count) VALUES ($1, $2, 1) \ + ON CONFLICT (owner, collection_address) \ + DO UPDATE SET count = top_owners.count + 1", + &[&new_owner_str, &collection_address], + ); } + + // Update NFT information after handling top_owners logic + let _ = client.execute( + "INSERT INTO nfts (\ + collection_address, nft_id, metadata_url,\ + owner, frozen)\ + VALUES ($1, $2, $3, $4, $5)\ + ON CONFLICT (collection_address, nft_id)\ + DO UPDATE SET metadata_url = EXCLUDED.metadata_url,\ + owner = EXCLUDED.owner,\ + frozen = EXCLUDED.frozen", + &[ + &collection_address, + &(nft_id as i64), + &metadata_url, + &new_owner_str, + &frozen, + ], + ); } }) } diff --git a/module-system/sov-modules-macros/src/lib.rs b/module-system/sov-modules-macros/src/lib.rs index f44b6cda4..4a52beeb5 100644 --- a/module-system/sov-modules-macros/src/lib.rs +++ b/module-system/sov-modules-macros/src/lib.rs @@ -266,6 +266,35 @@ pub fn address_type(_attr: TokenStream, item: TokenStream) -> TokenStream { handle_macro_error(address_type_helper(input)) } +/// The offchain macro is used to annotate functions that should only be executed by the rollup +/// when the "offchain" feature flag is passed. The macro produces one of two functions depending on +/// the presence flag. +/// "offchain" feature enabled: function is present as defined +/// "offchain" feature absent: function body is replaced with an empty definition +/// +/// The idea here is that offchain computation is optionally enabled for a full node and is not +/// part of chain state and does not impact consensus, prover or anything else. +/// +/// ## Example +/// ``` +/// use sov_modules_macros::offchain; +/// #[offchain] +/// fn redis_insert(count: u64){ +/// println!("Inserting {} to redis", count); +/// } +/// ``` +/// +/// This is exactly equivalent to hand-writing +///``` +/// #[cfg(feature = "offchain")] +/// fn redis_insert(count: u64){ +/// println!("Inserting {} to redis", count); +/// } +/// +/// #[cfg(not(feature = "offchain"))] +/// fn redis_insert(count: u64){ +/// } +///``` #[proc_macro_attribute] pub fn offchain(_attr: TokenStream, item: TokenStream) -> TokenStream { let input = parse_macro_input!(item as ItemFn); diff --git a/utils/nft-utils/src/main.rs b/utils/nft-utils/src/main.rs index 5799c620a..321f27c6f 100644 --- a/utils/nft-utils/src/main.rs +++ b/utils/nft-utils/src/main.rs @@ -39,7 +39,34 @@ fn get_nft_metadata_url(collection_address: &str, nft_id: u64) -> String { format!("{}/nft/{}/{}", DUMMY_URL, collection_address, nft_id) } -async fn create_collection( +/// Convenience and readability wrapper for build_create_collection_transaction +fn build_create_collection_transactions( + creator_pk: &DefaultPrivateKey, + start_nonce: &mut u64, + collections: &[&str], +) -> Vec> { + collections + .iter() + .map(|&collection| { + let tx = build_create_collection_transaction(creator_pk, *start_nonce, collection); + *start_nonce += 1; + tx + }) + .collect() +} + +/// Constructs a transaction to create a new NFT collection. +/// +/// # Arguments +/// +/// * `signer`: The private key used for signing the transaction. +/// * `nonce`: The nonce to be used for the transaction. +/// * `collection_name`: The name of the collection to be created. +/// +/// # Returns +/// +/// Returns a signed transaction for creating a new NFT collection. +fn build_create_collection_transaction( signer: &DefaultPrivateKey, nonce: u64, collection_name: &str, @@ -63,7 +90,45 @@ async fn create_collection( ) } -async fn mint_nft( +/// Convenience and readability wrapper for build_mint_nft_transaction +fn build_mint_transactions( + creator_pk: &DefaultPrivateKey, + start_nonce: &mut u64, + collection: &str, + start_nft_id: &mut u64, + num: usize, + owner_pk: &DefaultPrivateKey, +) -> Vec> { + (0..num) + .map(|_| { + let tx = build_mint_nft_transaction( + creator_pk, + *start_nonce, + collection, + *start_nft_id, + &owner_pk.default_address(), + ); + *start_nft_id += 1; + *start_nonce += 1; + tx + }) + .collect() +} + +/// Constructs a transaction to mint a new NFT. +/// +/// # Arguments +/// +/// * `signer`: The private key used for signing the transaction. +/// * `nonce`: The nonce to be used for the transaction. +/// * `collection_name`: The name of the collection to which the NFT belongs. +/// * `token_id`: The unique identifier for the new NFT. +/// * `owner`: The address of the user to whom the NFT will be minted. +/// +/// # Returns +/// +/// Returns a signed transaction for minting a new NFT to a specified user. +fn build_mint_nft_transaction( signer: &DefaultPrivateKey, nonce: u64, collection_name: &str, @@ -90,7 +155,44 @@ async fn mint_nft( ) } -async fn transfer_nft( +/// Convenience and readability wrapper for build_transfer_nft_transaction +fn build_transfer_transactions( + signer: &DefaultPrivateKey, + start_nonce: &mut u64, + collection_address: &CollectionAddress, + nft_ids: Vec, +) -> Vec> { + nft_ids + .into_iter() + .map(|nft_id| { + let new_owner = DefaultPrivateKey::generate().default_address(); + let tx = build_transfer_nft_transaction( + signer, + *start_nonce, + collection_address, + nft_id, + &new_owner, + ); + *start_nonce += 1; + tx + }) + .collect() +} + +/// Constructs a transaction to transfer an NFT to another user. +/// +/// # Arguments +/// +/// * `signer`: The private key used for signing the transaction. +/// * `nonce`: The nonce to be used for the transaction. +/// * `collection_address`: The address of the collection to which the NFT belongs. +/// * `token_id`: The unique identifier for the NFT being transferred. +/// * `to`: The address of the user to whom the NFT will be transferred. +/// +/// # Returns +/// +/// Returns a signed transaction for transferring an NFT to a specified user. +fn build_transfer_nft_transaction( signer: &DefaultPrivateKey, nonce: u64, collection_address: &CollectionAddress, @@ -120,98 +222,63 @@ async fn main() { let client = SimpleClient::new("localhost", 12345).await.unwrap(); let mut nonce = 0; - let mut nft_id = 1; - let mut transactions = vec![]; - transactions.push(create_collection(&creator_pk, nonce, COLLECTION_1).await); - nonce += 1; - transactions.push(create_collection(&creator_pk, nonce, COLLECTION_2).await); - nonce += 1; - transactions.push(create_collection(&creator_pk, nonce, COLLECTION_3).await); - + let collections = [COLLECTION_1, COLLECTION_2, COLLECTION_3]; + let transactions = build_create_collection_transactions(&creator_pk, &mut nonce, &collections); client.send_transactions(transactions, None).await.unwrap(); + // sleep is necessary because of how the sequencer currently works // without the sleep, there is a concurrency issue and some transactions would be ignored thread::sleep(Duration::from_millis(1000)); - let mut transactions = vec![]; - for _ in 0..15 { - nonce += 1; - transactions.push( - mint_nft( - &creator_pk, - nonce, - COLLECTION_1, - nft_id, - &owner_1_pk.default_address(), - ) - .await, - ); - nft_id += 1; - } - - client.send_transactions(transactions, None).await.unwrap(); - thread::sleep(Duration::from_millis(1000)); - - let mut transactions = vec![]; - for _ in 0..5 { - nonce += 1; - transactions.push( - mint_nft( - &creator_pk, - nonce, - COLLECTION_1, - nft_id, - &owner_2_pk.default_address(), - ) - .await, - ); - nft_id += 1; - } - let mut nft_id = 1; - for _ in 0..20 { - nonce += 1; - transactions.push( - mint_nft( - &creator_pk, - nonce, - COLLECTION_2, - nft_id, - &owner_1_pk.default_address(), - ) - .await, - ); - nft_id += 1; - } + let mut transactions = build_mint_transactions( + &creator_pk, + &mut nonce, + COLLECTION_1, + &mut nft_id, + 15, + &owner_1_pk, + ); - client.send_transactions(transactions, None).await.unwrap(); - thread::sleep(Duration::from_millis(1000)); + transactions.extend(build_mint_transactions( + &creator_pk, + &mut nonce, + COLLECTION_1, + &mut nft_id, + 5, + &owner_2_pk, + )); + let mut nft_id = 1; + transactions.extend(build_mint_transactions( + &creator_pk, + &mut nonce, + COLLECTION_2, + &mut nft_id, + 20, + &owner_1_pk, + )); - let mut transactions = vec![]; + client + .send_transactions(transactions.clone(), None) + .await + .unwrap(); + thread::sleep(Duration::from_millis(3000)); - let mut owner_1_nonce = 0; - let mut nft_id = 1; let collection_1_address = get_collection_address::( COLLECTION_1, creator_pk.default_address().as_ref(), ); - #[allow(clippy::explicit_counter_loop)] - for _ in 1..7 { - let new_owner = DefaultPrivateKey::generate().default_address(); - transactions.push( - transfer_nft( - &owner_1_pk, - owner_1_nonce, - &collection_1_address, - nft_id, - &new_owner, - ) - .await, - ); - owner_1_nonce += 1; - nft_id += 1; - } - - client.send_transactions(transactions, None).await.unwrap(); + let mut owner_1_nonce = 0; + let nft_ids_to_transfer: Vec = (1..=6).collect(); + transactions = build_transfer_transactions( + &owner_1_pk, + &mut owner_1_nonce, + &collection_1_address, + nft_ids_to_transfer, + ); + client + .send_transactions(transactions.clone(), None) + .await + .unwrap(); } From 76c65a0a8b4ad73efdfc422a63c7e88e0c6cc4d9 Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Thu, 28 Sep 2023 12:19:26 +0530 Subject: [PATCH 15/34] move common code into lib --- utils/nft-utils/src/lib.rs | 201 ++++++++++++++++++++++++++++++++++++ utils/nft-utils/src/main.rs | 197 ++--------------------------------- 2 files changed, 209 insertions(+), 189 deletions(-) create mode 100644 utils/nft-utils/src/lib.rs diff --git a/utils/nft-utils/src/lib.rs b/utils/nft-utils/src/lib.rs new file mode 100644 index 000000000..bb280d598 --- /dev/null +++ b/utils/nft-utils/src/lib.rs @@ -0,0 +1,201 @@ +use borsh::ser::BorshSerialize; +use demo_stf::runtime::RuntimeCall; +use sov_modules_api::default_context::DefaultContext; +use sov_modules_api::default_signature::private_key::DefaultPrivateKey; +use sov_modules_api::transaction::Transaction; +use sov_modules_api::{Address, PrivateKey}; +use sov_nft_module::utils::get_collection_address; +use sov_nft_module::{CallMessage, CollectionAddress, UserAddress}; +use sov_rollup_interface::mocks::MockDaSpec; + +fn get_collection_metadata_url(base_url: &str, collection_address: &str) -> String { + format!("{}/collection/{}", base_url, collection_address) +} + +fn get_nft_metadata_url(base_url: &str, collection_address: &str, nft_id: u64) -> String { + format!("{}/nft/{}/{}", base_url, collection_address, nft_id) +} + +/// Convenience and readability wrapper for build_create_collection_transaction +pub fn build_create_collection_transactions( + creator_pk: &DefaultPrivateKey, + start_nonce: &mut u64, + base_uri: &str, + collections: &[&str], +) -> Vec> { + collections + .iter() + .map(|&collection_name| { + let tx = build_create_collection_transaction( + creator_pk, + *start_nonce, + collection_name, + base_uri, + ); + *start_nonce += 1; + tx + }) + .collect() +} + +/// Constructs a transaction to create a new NFT collection. +/// +/// # Arguments +/// +/// * `signer`: The private key used for signing the transaction. +/// * `nonce`: The nonce to be used for the transaction. +/// * `collection_name`: The name of the collection to be created. +/// +/// # Returns +/// +/// Returns a signed transaction for creating a new NFT collection. +pub fn build_create_collection_transaction( + signer: &DefaultPrivateKey, + nonce: u64, + collection_name: &str, + base_uri: &str, +) -> Transaction { + let collection_address = get_collection_address::( + collection_name, + signer.default_address().as_ref(), + ); + + let collection_uri = get_collection_metadata_url(base_uri, &collection_address.to_string()); + let create_collection_message = RuntimeCall::::nft( + CallMessage::::CreateCollection { + name: collection_name.to_string(), + collection_uri, + }, + ); + Transaction::::new_signed_tx( + signer, + create_collection_message.try_to_vec().unwrap(), + nonce, + ) +} + +/// Convenience and readability wrapper for build_mint_nft_transaction +pub fn build_mint_transactions( + creator_pk: &DefaultPrivateKey, + start_nonce: &mut u64, + collection: &str, + start_nft_id: &mut u64, + num: usize, + base_uri: &str, + owner_pk: &DefaultPrivateKey, +) -> Vec> { + (0..num) + .map(|_| { + let tx = build_mint_nft_transaction( + creator_pk, + *start_nonce, + collection, + *start_nft_id, + base_uri, + &owner_pk.default_address(), + ); + *start_nft_id += 1; + *start_nonce += 1; + tx + }) + .collect() +} + +/// Constructs a transaction to mint a new NFT. +/// +/// # Arguments +/// +/// * `signer`: The private key used for signing the transaction. +/// * `nonce`: The nonce to be used for the transaction. +/// * `collection_name`: The name of the collection to which the NFT belongs. +/// * `token_id`: The unique identifier for the new NFT. +/// * `owner`: The address of the user to whom the NFT will be minted. +/// +/// # Returns +/// +/// Returns a signed transaction for minting a new NFT to a specified user. +pub fn build_mint_nft_transaction( + signer: &DefaultPrivateKey, + nonce: u64, + collection_name: &str, + token_id: u64, + base_uri: &str, + owner: &Address, +) -> Transaction { + let collection_address = get_collection_address::( + collection_name, + signer.default_address().as_ref(), + ); + let token_uri = get_nft_metadata_url(base_uri, &collection_address.to_string(), token_id); + let mint_nft_message = + RuntimeCall::::nft(CallMessage::::MintNft { + collection_name: collection_name.to_string(), + token_uri, + token_id, + owner: UserAddress::new(owner), + frozen: false, + }); + Transaction::::new_signed_tx( + signer, + mint_nft_message.try_to_vec().unwrap(), + nonce, + ) +} + +/// Convenience and readability wrapper for build_transfer_nft_transaction +pub fn build_transfer_transactions( + signer: &DefaultPrivateKey, + start_nonce: &mut u64, + collection_address: &CollectionAddress, + nft_ids: Vec, +) -> Vec> { + nft_ids + .into_iter() + .map(|nft_id| { + let new_owner = DefaultPrivateKey::generate().default_address(); + let tx = build_transfer_nft_transaction( + signer, + *start_nonce, + collection_address, + nft_id, + &new_owner, + ); + *start_nonce += 1; + tx + }) + .collect() +} + +/// Constructs a transaction to transfer an NFT to another user. +/// +/// # Arguments +/// +/// * `signer`: The private key used for signing the transaction. +/// * `nonce`: The nonce to be used for the transaction. +/// * `collection_address`: The address of the collection to which the NFT belongs. +/// * `token_id`: The unique identifier for the NFT being transferred. +/// * `to`: The address of the user to whom the NFT will be transferred. +/// +/// # Returns +/// +/// Returns a signed transaction for transferring an NFT to a specified user. +pub fn build_transfer_nft_transaction( + signer: &DefaultPrivateKey, + nonce: u64, + collection_address: &CollectionAddress, + token_id: u64, + to: &Address, +) -> Transaction { + let transfer_message = RuntimeCall::::nft(CallMessage::< + DefaultContext, + >::TransferNft { + collection_address: collection_address.clone(), + token_id, + to: UserAddress::new(to), + }); + Transaction::::new_signed_tx( + signer, + transfer_message.try_to_vec().unwrap(), + nonce, + ) +} diff --git a/utils/nft-utils/src/main.rs b/utils/nft-utils/src/main.rs index 321f27c6f..f1e986a34 100644 --- a/utils/nft-utils/src/main.rs +++ b/utils/nft-utils/src/main.rs @@ -1,15 +1,12 @@ use std::thread; use std::time::Duration; -use borsh::ser::BorshSerialize; -use demo_stf::runtime::RuntimeCall; +use nft_utils::{ + build_create_collection_transactions, build_mint_transactions, build_transfer_transactions, +}; use sov_modules_api::default_context::DefaultContext; use sov_modules_api::default_signature::private_key::DefaultPrivateKey; -use sov_modules_api::transaction::Transaction; -use sov_modules_api::{Address, PrivateKey}; use sov_nft_module::utils::get_collection_address; -use sov_nft_module::{CallMessage, CollectionAddress, UserAddress}; -use sov_rollup_interface::mocks::MockDaSpec; use sov_sequencer::utils::SimpleClient; const COLLECTION_1: &str = "Sovereign Squirrel Syndicate"; @@ -31,188 +28,6 @@ const PK3: [u8; 32] = [ 161, 208, 245, 116, 93, 84, 37, 87, 171, 44, 30, 239, ]; -fn get_collection_metadata_url(collection_address: &str) -> String { - format!("{}/collection/{}", DUMMY_URL, collection_address) -} - -fn get_nft_metadata_url(collection_address: &str, nft_id: u64) -> String { - format!("{}/nft/{}/{}", DUMMY_URL, collection_address, nft_id) -} - -/// Convenience and readability wrapper for build_create_collection_transaction -fn build_create_collection_transactions( - creator_pk: &DefaultPrivateKey, - start_nonce: &mut u64, - collections: &[&str], -) -> Vec> { - collections - .iter() - .map(|&collection| { - let tx = build_create_collection_transaction(creator_pk, *start_nonce, collection); - *start_nonce += 1; - tx - }) - .collect() -} - -/// Constructs a transaction to create a new NFT collection. -/// -/// # Arguments -/// -/// * `signer`: The private key used for signing the transaction. -/// * `nonce`: The nonce to be used for the transaction. -/// * `collection_name`: The name of the collection to be created. -/// -/// # Returns -/// -/// Returns a signed transaction for creating a new NFT collection. -fn build_create_collection_transaction( - signer: &DefaultPrivateKey, - nonce: u64, - collection_name: &str, -) -> Transaction { - let collection_address = get_collection_address::( - collection_name, - signer.default_address().as_ref(), - ); - - let collection_uri = get_collection_metadata_url(&collection_address.to_string()); - let create_collection_message = RuntimeCall::::nft( - CallMessage::::CreateCollection { - name: collection_name.to_string(), - collection_uri, - }, - ); - Transaction::::new_signed_tx( - signer, - create_collection_message.try_to_vec().unwrap(), - nonce, - ) -} - -/// Convenience and readability wrapper for build_mint_nft_transaction -fn build_mint_transactions( - creator_pk: &DefaultPrivateKey, - start_nonce: &mut u64, - collection: &str, - start_nft_id: &mut u64, - num: usize, - owner_pk: &DefaultPrivateKey, -) -> Vec> { - (0..num) - .map(|_| { - let tx = build_mint_nft_transaction( - creator_pk, - *start_nonce, - collection, - *start_nft_id, - &owner_pk.default_address(), - ); - *start_nft_id += 1; - *start_nonce += 1; - tx - }) - .collect() -} - -/// Constructs a transaction to mint a new NFT. -/// -/// # Arguments -/// -/// * `signer`: The private key used for signing the transaction. -/// * `nonce`: The nonce to be used for the transaction. -/// * `collection_name`: The name of the collection to which the NFT belongs. -/// * `token_id`: The unique identifier for the new NFT. -/// * `owner`: The address of the user to whom the NFT will be minted. -/// -/// # Returns -/// -/// Returns a signed transaction for minting a new NFT to a specified user. -fn build_mint_nft_transaction( - signer: &DefaultPrivateKey, - nonce: u64, - collection_name: &str, - token_id: u64, - owner: &Address, -) -> Transaction { - let collection_address = get_collection_address::( - collection_name, - signer.default_address().as_ref(), - ); - let token_uri = get_nft_metadata_url(&collection_address.to_string(), token_id); - let mint_nft_message = - RuntimeCall::::nft(CallMessage::::MintNft { - collection_name: collection_name.to_string(), - token_uri, - token_id, - owner: UserAddress::new(owner), - frozen: false, - }); - Transaction::::new_signed_tx( - signer, - mint_nft_message.try_to_vec().unwrap(), - nonce, - ) -} - -/// Convenience and readability wrapper for build_transfer_nft_transaction -fn build_transfer_transactions( - signer: &DefaultPrivateKey, - start_nonce: &mut u64, - collection_address: &CollectionAddress, - nft_ids: Vec, -) -> Vec> { - nft_ids - .into_iter() - .map(|nft_id| { - let new_owner = DefaultPrivateKey::generate().default_address(); - let tx = build_transfer_nft_transaction( - signer, - *start_nonce, - collection_address, - nft_id, - &new_owner, - ); - *start_nonce += 1; - tx - }) - .collect() -} - -/// Constructs a transaction to transfer an NFT to another user. -/// -/// # Arguments -/// -/// * `signer`: The private key used for signing the transaction. -/// * `nonce`: The nonce to be used for the transaction. -/// * `collection_address`: The address of the collection to which the NFT belongs. -/// * `token_id`: The unique identifier for the NFT being transferred. -/// * `to`: The address of the user to whom the NFT will be transferred. -/// -/// # Returns -/// -/// Returns a signed transaction for transferring an NFT to a specified user. -fn build_transfer_nft_transaction( - signer: &DefaultPrivateKey, - nonce: u64, - collection_address: &CollectionAddress, - token_id: u64, - to: &Address, -) -> Transaction { - let transfer_message = RuntimeCall::::nft(CallMessage::< - DefaultContext, - >::TransferNft { - collection_address: collection_address.clone(), - token_id, - to: UserAddress::new(to), - }); - Transaction::::new_signed_tx( - signer, - transfer_message.try_to_vec().unwrap(), - nonce, - ) -} - #[tokio::main] async fn main() { let creator_pk = DefaultPrivateKey::try_from(&PK1[..]).unwrap(); @@ -223,7 +38,8 @@ async fn main() { let mut nonce = 0; let collections = [COLLECTION_1, COLLECTION_2, COLLECTION_3]; - let transactions = build_create_collection_transactions(&creator_pk, &mut nonce, &collections); + let transactions = + build_create_collection_transactions(&creator_pk, &mut nonce, DUMMY_URL, &collections); client.send_transactions(transactions, None).await.unwrap(); // sleep is necessary because of how the sequencer currently works @@ -237,6 +53,7 @@ async fn main() { COLLECTION_1, &mut nft_id, 15, + DUMMY_URL, &owner_1_pk, ); @@ -246,6 +63,7 @@ async fn main() { COLLECTION_1, &mut nft_id, 5, + DUMMY_URL, &owner_2_pk, )); let mut nft_id = 1; @@ -255,6 +73,7 @@ async fn main() { COLLECTION_2, &mut nft_id, 20, + DUMMY_URL, &owner_1_pk, )); From 5d2b9817d41463e59996e3385a1384b036e0a87f Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Thu, 28 Sep 2023 17:30:39 +0530 Subject: [PATCH 16/34] added issue to the nft script --- utils/nft-utils/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/nft-utils/src/main.rs b/utils/nft-utils/src/main.rs index f1e986a34..15d01ae71 100644 --- a/utils/nft-utils/src/main.rs +++ b/utils/nft-utils/src/main.rs @@ -44,6 +44,7 @@ async fn main() { // sleep is necessary because of how the sequencer currently works // without the sleep, there is a concurrency issue and some transactions would be ignored + // TODO: remove after https://github.com/Sovereign-Labs/sovereign-sdk/issues/949 is fixed thread::sleep(Duration::from_millis(1000)); let mut nft_id = 1; @@ -81,6 +82,7 @@ async fn main() { .send_transactions(transactions.clone(), None) .await .unwrap(); + // TODO: remove after https://github.com/Sovereign-Labs/sovereign-sdk/issues/949 is fixed thread::sleep(Duration::from_millis(3000)); let collection_1_address = get_collection_address::( From 37c43b75e69fc0ad807126956ba14e0dc5fdbf16 Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Thu, 28 Sep 2023 19:55:16 +0530 Subject: [PATCH 17/34] add nft cli binary to nft-utils --- Cargo.lock | 21 +------------ Cargo.toml | 1 - examples/demo-prover/methods/guest/Cargo.lock | 1 + examples/demo-rollup/Cargo.toml | 8 ++++- examples/demo-rollup/src/lib.rs | 2 ++ examples/demo-rollup/src/nft_utils/lib.rs | 1 + .../demo-rollup/src/nft_utils}/main.rs | 2 +- .../demo-rollup/src/nft_utils/mod.rs | 2 ++ utils/nft-utils/Cargo.toml | 31 ------------------- 9 files changed, 15 insertions(+), 54 deletions(-) create mode 100644 examples/demo-rollup/src/nft_utils/lib.rs rename {utils/nft-utils/src => examples/demo-rollup/src/nft_utils}/main.rs (99%) rename utils/nft-utils/src/lib.rs => examples/demo-rollup/src/nft_utils/mod.rs (98%) delete mode 100644 utils/nft-utils/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index e81a633a9..b807e5167 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4278,26 +4278,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" -[[package]] -name = "nft-utils" -version = "0.2.0" -dependencies = [ - "anyhow", - "borsh", - "demo-stf", - "jsonrpsee", - "schemars", - "serde", - "serde_json", - "sov-cli", - "sov-modules-api", - "sov-modules-macros", - "sov-nft-module", - "sov-rollup-interface", - "sov-sequencer", - "tokio", -] - [[package]] name = "nibble_vec" version = "0.1.0" @@ -7333,6 +7313,7 @@ dependencies = [ "sov-evm", "sov-modules-api", "sov-modules-stf-template", + "sov-nft-module", "sov-risc0-adapter", "sov-rollup-interface", "sov-sequencer", diff --git a/Cargo.toml b/Cargo.toml index aa556b226..a0591c027 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,6 @@ members = [ "utils/zk-cycle-macros", "utils/zk-cycle-utils", - "utils/nft-utils", "utils/bashtestmd", "utils/nft-utils", diff --git a/examples/demo-prover/methods/guest/Cargo.lock b/examples/demo-prover/methods/guest/Cargo.lock index 3bdff2f9a..776b23483 100644 --- a/examples/demo-prover/methods/guest/Cargo.lock +++ b/examples/demo-prover/methods/guest/Cargo.lock @@ -1363,6 +1363,7 @@ dependencies = [ "sov-celestia-adapter", "sov-modules-api", "sov-modules-stf-template", + "sov-nft-module", "sov-risc0-adapter", "sov-rollup-interface", "sov-state", diff --git a/examples/demo-rollup/Cargo.toml b/examples/demo-rollup/Cargo.toml index 2c3a0a2c2..313ab7b59 100644 --- a/examples/demo-rollup/Cargo.toml +++ b/examples/demo-rollup/Cargo.toml @@ -42,6 +42,7 @@ sov-risc0-adapter = { path = "../../adapters/risc0" } sov-state = { path = "../../module-system/sov-state" } sov-cli = { path = "../../module-system/sov-cli", optional = true } clap = { workspace = true, optional = true} +sov-nft-module = { path = "../../module-system/module-implementations/sov-nft-module" } [dev-dependencies] @@ -77,7 +78,7 @@ experimental = ["sov-ethereum/experimental", "reth-primitives", "secp256k1", "de native = ["anyhow", "jsonrpsee", "serde", "serde_json", "tracing", "tokio", "tracing-subscriber", "demo-stf/native", "sov-modules-stf-template/native", "sov-risc0-adapter/native", "sov-modules-api/native", "sov-state/native", "sov-cli", "clap", "sov-celestia-adapter/native", "sov-db", "sov-sequencer", "sov-stf-runner/native", - "sov-modules-api/native"] + "sov-modules-api/native", "borsh"] bench = ["native", "async-trait", "borsh", "hex"] local = ["sov-ethereum/local"] offchain = ["demo-stf/offchain"] @@ -101,6 +102,11 @@ name = "sov-cli" path = "src/sov-cli/main.rs" required-features = ["native"] +[[bin]] +name = "nft-cli" +path = "src/nft_utils/main.rs" +required-features = ["native"] + [[bin]] name = "sov-demo-rollup" path = "src/main.rs" diff --git a/examples/demo-rollup/src/lib.rs b/examples/demo-rollup/src/lib.rs index 68d2030bc..bf3b8380b 100644 --- a/examples/demo-rollup/src/lib.rs +++ b/examples/demo-rollup/src/lib.rs @@ -1,6 +1,8 @@ #![deny(missing_docs)] #![doc = include_str!("../README.md")] +#[cfg(feature = "native")] +pub mod nft_utils; #[cfg(feature = "native")] pub mod register_rpc; #[cfg(feature = "native")] diff --git a/examples/demo-rollup/src/nft_utils/lib.rs b/examples/demo-rollup/src/nft_utils/lib.rs new file mode 100644 index 000000000..f95255113 --- /dev/null +++ b/examples/demo-rollup/src/nft_utils/lib.rs @@ -0,0 +1 @@ +pub use sov_demo_rollup::nft_utils::*; diff --git a/utils/nft-utils/src/main.rs b/examples/demo-rollup/src/nft_utils/main.rs similarity index 99% rename from utils/nft-utils/src/main.rs rename to examples/demo-rollup/src/nft_utils/main.rs index 15d01ae71..201466b1b 100644 --- a/utils/nft-utils/src/main.rs +++ b/examples/demo-rollup/src/nft_utils/main.rs @@ -1,7 +1,7 @@ use std::thread; use std::time::Duration; -use nft_utils::{ +use sov_demo_rollup::nft_utils::{ build_create_collection_transactions, build_mint_transactions, build_transfer_transactions, }; use sov_modules_api::default_context::DefaultContext; diff --git a/utils/nft-utils/src/lib.rs b/examples/demo-rollup/src/nft_utils/mod.rs similarity index 98% rename from utils/nft-utils/src/lib.rs rename to examples/demo-rollup/src/nft_utils/mod.rs index bb280d598..ffcc32520 100644 --- a/utils/nft-utils/src/lib.rs +++ b/examples/demo-rollup/src/nft_utils/mod.rs @@ -1,3 +1,5 @@ +//! Utility functions to construct transactions for the sov-nft-module + use borsh::ser::BorshSerialize; use demo_stf::runtime::RuntimeCall; use sov_modules_api::default_context::DefaultContext; diff --git a/utils/nft-utils/Cargo.toml b/utils/nft-utils/Cargo.toml deleted file mode 100644 index 3ef68cfb3..000000000 --- a/utils/nft-utils/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "nft-utils" -authors = { workspace = true } -description = "Utils for NFTs" -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } -version = { workspace = true } -readme = "README.md" -resolver = "2" -autotests = false - -[dependencies] -anyhow = { workspace = true } -borsh = { workspace = true, features = ["rc"] } -serde = { workspace = true} -schemars = { workspace = true} -serde_json = { workspace = true} -jsonrpsee = { workspace = true, features = ["macros", "client-core", "server"]} - -sov-nft-module = { path = "../../module-system/module-implementations/sov-nft-module", features = ["native"]} -sov-modules-macros = {path = "../../module-system/sov-modules-macros"} -sov-sequencer = { path = "../../full-node/sov-sequencer"} -demo-stf = {path = "../../examples/demo-stf"} -sov-rollup-interface = {path = "../../rollup-interface"} - -sov-modules-api = {path = "../../module-system/sov-modules-api", features = ["native"]} -sov-cli = {path = "../../module-system/sov-cli"} -tokio = {version = "1.32.0"} \ No newline at end of file From ee4f343d6d18f905eb2e96d892088e2d4f16b84a Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Fri, 29 Sep 2023 15:02:04 +0530 Subject: [PATCH 18/34] Revert "add nft cli binary to nft-utils" This reverts commit d443009db9b438112d388e4e78e19d2dfc19fe3f. --- Cargo.lock | 21 ++++++++++++- Cargo.toml | 1 + examples/demo-prover/methods/guest/Cargo.lock | 1 - examples/demo-rollup/Cargo.toml | 8 +---- examples/demo-rollup/src/lib.rs | 2 -- examples/demo-rollup/src/nft_utils/lib.rs | 1 - utils/nft-utils/Cargo.toml | 31 +++++++++++++++++++ .../mod.rs => utils/nft-utils/src/lib.rs | 2 -- .../nft_utils => utils/nft-utils/src}/main.rs | 2 +- 9 files changed, 54 insertions(+), 15 deletions(-) delete mode 100644 examples/demo-rollup/src/nft_utils/lib.rs create mode 100644 utils/nft-utils/Cargo.toml rename examples/demo-rollup/src/nft_utils/mod.rs => utils/nft-utils/src/lib.rs (98%) rename {examples/demo-rollup/src/nft_utils => utils/nft-utils/src}/main.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index b807e5167..e81a633a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4278,6 +4278,26 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nft-utils" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "demo-stf", + "jsonrpsee", + "schemars", + "serde", + "serde_json", + "sov-cli", + "sov-modules-api", + "sov-modules-macros", + "sov-nft-module", + "sov-rollup-interface", + "sov-sequencer", + "tokio", +] + [[package]] name = "nibble_vec" version = "0.1.0" @@ -7313,7 +7333,6 @@ dependencies = [ "sov-evm", "sov-modules-api", "sov-modules-stf-template", - "sov-nft-module", "sov-risc0-adapter", "sov-rollup-interface", "sov-sequencer", diff --git a/Cargo.toml b/Cargo.toml index a0591c027..aa556b226 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ "utils/zk-cycle-macros", "utils/zk-cycle-utils", + "utils/nft-utils", "utils/bashtestmd", "utils/nft-utils", diff --git a/examples/demo-prover/methods/guest/Cargo.lock b/examples/demo-prover/methods/guest/Cargo.lock index 776b23483..3bdff2f9a 100644 --- a/examples/demo-prover/methods/guest/Cargo.lock +++ b/examples/demo-prover/methods/guest/Cargo.lock @@ -1363,7 +1363,6 @@ dependencies = [ "sov-celestia-adapter", "sov-modules-api", "sov-modules-stf-template", - "sov-nft-module", "sov-risc0-adapter", "sov-rollup-interface", "sov-state", diff --git a/examples/demo-rollup/Cargo.toml b/examples/demo-rollup/Cargo.toml index 313ab7b59..2c3a0a2c2 100644 --- a/examples/demo-rollup/Cargo.toml +++ b/examples/demo-rollup/Cargo.toml @@ -42,7 +42,6 @@ sov-risc0-adapter = { path = "../../adapters/risc0" } sov-state = { path = "../../module-system/sov-state" } sov-cli = { path = "../../module-system/sov-cli", optional = true } clap = { workspace = true, optional = true} -sov-nft-module = { path = "../../module-system/module-implementations/sov-nft-module" } [dev-dependencies] @@ -78,7 +77,7 @@ experimental = ["sov-ethereum/experimental", "reth-primitives", "secp256k1", "de native = ["anyhow", "jsonrpsee", "serde", "serde_json", "tracing", "tokio", "tracing-subscriber", "demo-stf/native", "sov-modules-stf-template/native", "sov-risc0-adapter/native", "sov-modules-api/native", "sov-state/native", "sov-cli", "clap", "sov-celestia-adapter/native", "sov-db", "sov-sequencer", "sov-stf-runner/native", - "sov-modules-api/native", "borsh"] + "sov-modules-api/native"] bench = ["native", "async-trait", "borsh", "hex"] local = ["sov-ethereum/local"] offchain = ["demo-stf/offchain"] @@ -102,11 +101,6 @@ name = "sov-cli" path = "src/sov-cli/main.rs" required-features = ["native"] -[[bin]] -name = "nft-cli" -path = "src/nft_utils/main.rs" -required-features = ["native"] - [[bin]] name = "sov-demo-rollup" path = "src/main.rs" diff --git a/examples/demo-rollup/src/lib.rs b/examples/demo-rollup/src/lib.rs index bf3b8380b..68d2030bc 100644 --- a/examples/demo-rollup/src/lib.rs +++ b/examples/demo-rollup/src/lib.rs @@ -1,8 +1,6 @@ #![deny(missing_docs)] #![doc = include_str!("../README.md")] -#[cfg(feature = "native")] -pub mod nft_utils; #[cfg(feature = "native")] pub mod register_rpc; #[cfg(feature = "native")] diff --git a/examples/demo-rollup/src/nft_utils/lib.rs b/examples/demo-rollup/src/nft_utils/lib.rs deleted file mode 100644 index f95255113..000000000 --- a/examples/demo-rollup/src/nft_utils/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub use sov_demo_rollup::nft_utils::*; diff --git a/utils/nft-utils/Cargo.toml b/utils/nft-utils/Cargo.toml new file mode 100644 index 000000000..3ef68cfb3 --- /dev/null +++ b/utils/nft-utils/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "nft-utils" +authors = { workspace = true } +description = "Utils for NFTs" +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +rust-version = { workspace = true } +version = { workspace = true } +readme = "README.md" +resolver = "2" +autotests = false + +[dependencies] +anyhow = { workspace = true } +borsh = { workspace = true, features = ["rc"] } +serde = { workspace = true} +schemars = { workspace = true} +serde_json = { workspace = true} +jsonrpsee = { workspace = true, features = ["macros", "client-core", "server"]} + +sov-nft-module = { path = "../../module-system/module-implementations/sov-nft-module", features = ["native"]} +sov-modules-macros = {path = "../../module-system/sov-modules-macros"} +sov-sequencer = { path = "../../full-node/sov-sequencer"} +demo-stf = {path = "../../examples/demo-stf"} +sov-rollup-interface = {path = "../../rollup-interface"} + +sov-modules-api = {path = "../../module-system/sov-modules-api", features = ["native"]} +sov-cli = {path = "../../module-system/sov-cli"} +tokio = {version = "1.32.0"} \ No newline at end of file diff --git a/examples/demo-rollup/src/nft_utils/mod.rs b/utils/nft-utils/src/lib.rs similarity index 98% rename from examples/demo-rollup/src/nft_utils/mod.rs rename to utils/nft-utils/src/lib.rs index ffcc32520..bb280d598 100644 --- a/examples/demo-rollup/src/nft_utils/mod.rs +++ b/utils/nft-utils/src/lib.rs @@ -1,5 +1,3 @@ -//! Utility functions to construct transactions for the sov-nft-module - use borsh::ser::BorshSerialize; use demo_stf::runtime::RuntimeCall; use sov_modules_api::default_context::DefaultContext; diff --git a/examples/demo-rollup/src/nft_utils/main.rs b/utils/nft-utils/src/main.rs similarity index 99% rename from examples/demo-rollup/src/nft_utils/main.rs rename to utils/nft-utils/src/main.rs index 201466b1b..15d01ae71 100644 --- a/examples/demo-rollup/src/nft_utils/main.rs +++ b/utils/nft-utils/src/main.rs @@ -1,7 +1,7 @@ use std::thread; use std::time::Duration; -use sov_demo_rollup::nft_utils::{ +use nft_utils::{ build_create_collection_transactions, build_mint_transactions, build_transfer_transactions, }; use sov_modules_api::default_context::DefaultContext; From 317417d8996114c43754fadf73d8e0a00bc8e443 Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Fri, 29 Sep 2023 16:02:54 +0530 Subject: [PATCH 19/34] move nft-utils back to sovereign/nft-utils and move sql to sql.rs --- .../sov-nft-module/src/offchain.rs | 36 +++++-------------- .../sov-nft-module/src/sql.rs | 33 +++++++++++++++++ 2 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 module-system/module-implementations/sov-nft-module/src/sql.rs diff --git a/module-system/module-implementations/sov-nft-module/src/offchain.rs b/module-system/module-implementations/sov-nft-module/src/offchain.rs index 6117214b8..a6460d775 100644 --- a/module-system/module-implementations/sov-nft-module/src/offchain.rs +++ b/module-system/module-implementations/sov-nft-module/src/offchain.rs @@ -8,6 +8,9 @@ use crate::utils::get_collection_address; use crate::CollectionAddress; use crate::{Collection, Nft, OwnerAddress}; +#[cfg(feature = "offchain")] +include!("sql.rs"); + /// Syncs a collection to the corresponding table "collections" in postgres #[offchain] pub fn update_collection(collection: &Collection) { @@ -27,16 +30,7 @@ pub fn update_collection(collection: &Collection match postgres::Client::connect(&conn_string, NoTls) { Ok(mut client) => { let result = client.execute( - "INSERT INTO collections (\ - collection_address, collection_name, creator_address,\ - frozen, metadata_url, supply)\ - VALUES ($1, $2, $3, $4, $5, $6)\ - ON CONFLICT (collection_address)\ - DO UPDATE SET collection_name = EXCLUDED.collection_name,\ - creator_address = EXCLUDED.creator_address,\ - frozen = EXCLUDED.frozen,\ - metadata_url = EXCLUDED.metadata_url,\ - supply = EXCLUDED.supply", + INSERT_OR_UPDATE_COLLECTION, &[ &collection_address_str, &collection_name, @@ -79,7 +73,7 @@ pub fn update_nft(nft: &Nft, old_owner: Option(nft: &Nft, old_owner: Option 0", + DECREMENT_COUNT_FOR_OLD_OWNER, &[&db_owner_str, &collection_address], ); // Increment count for new owner let _ = client.execute( - "INSERT INTO top_owners (owner, collection_address, count) VALUES ($1, $2, 1) \ - ON CONFLICT (owner, collection_address) \ - DO UPDATE SET count = top_owners.count + 1", + INCREMENT_OR_UPDATE_COUNT_FOR_NEW_OWNER, &[&new_owner_str, &collection_address], ); } } else if old_owner_address.is_none() { // Mint operation, and NFT doesn't exist in the database. Increment for the new owner. let _ = client.execute( - "INSERT INTO top_owners (owner, collection_address, count) VALUES ($1, $2, 1) \ - ON CONFLICT (owner, collection_address) \ - DO UPDATE SET count = top_owners.count + 1", + INCREMENT_OR_UPDATE_COUNT_FOR_NEW_OWNER, &[&new_owner_str, &collection_address], ); } // Update NFT information after handling top_owners logic let _ = client.execute( - "INSERT INTO nfts (\ - collection_address, nft_id, metadata_url,\ - owner, frozen)\ - VALUES ($1, $2, $3, $4, $5)\ - ON CONFLICT (collection_address, nft_id)\ - DO UPDATE SET metadata_url = EXCLUDED.metadata_url,\ - owner = EXCLUDED.owner,\ - frozen = EXCLUDED.frozen", + INSERT_OR_UPDATE_NFT, &[ &collection_address, &(nft_id as i64), diff --git a/module-system/module-implementations/sov-nft-module/src/sql.rs b/module-system/module-implementations/sov-nft-module/src/sql.rs new file mode 100644 index 000000000..0a29a90ec --- /dev/null +++ b/module-system/module-implementations/sov-nft-module/src/sql.rs @@ -0,0 +1,33 @@ +pub const INSERT_OR_UPDATE_COLLECTION: &str = + "INSERT INTO collections (\ + collection_address, collection_name, creator_address,\ + frozen, metadata_url, supply)\ + VALUES ($1, $2, $3, $4, $5, $6)\ + ON CONFLICT (collection_address)\ + DO UPDATE SET collection_name = EXCLUDED.collection_name,\ + creator_address = EXCLUDED.creator_address,\ + frozen = EXCLUDED.frozen,\ + metadata_url = EXCLUDED.metadata_url,\ + supply = EXCLUDED.supply"; + +pub const QUERY_OWNER_FROM_NFTS: &str = + "SELECT owner FROM nfts WHERE collection_address = $1 AND nft_id = $2"; + +pub const DECREMENT_COUNT_FOR_OLD_OWNER: &str = + "UPDATE top_owners SET count = count - 1 \ + WHERE owner = $1 AND collection_address = $2 AND count > 0"; + +pub const INCREMENT_OR_UPDATE_COUNT_FOR_NEW_OWNER: &str = + "INSERT INTO top_owners (owner, collection_address, count) VALUES ($1, $2, 1) \ + ON CONFLICT (owner, collection_address) \ + DO UPDATE SET count = top_owners.count + 1"; + +pub const INSERT_OR_UPDATE_NFT: &str = + "INSERT INTO nfts (\ + collection_address, nft_id, metadata_url,\ + owner, frozen)\ + VALUES ($1, $2, $3, $4, $5)\ + ON CONFLICT (collection_address, nft_id)\ + DO UPDATE SET metadata_url = EXCLUDED.metadata_url,\ + owner = EXCLUDED.owner,\ + frozen = EXCLUDED.frozen"; From 85ed254e1b30cf875342c4359e3341e777bd4ef8 Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Fri, 29 Sep 2023 16:08:57 +0530 Subject: [PATCH 20/34] remove un-necessary deps in nft-utils --- Cargo.lock | 7 ------- utils/nft-utils/Cargo.toml | 11 ++--------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e81a633a9..fa03da594 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4282,16 +4282,9 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" name = "nft-utils" version = "0.2.0" dependencies = [ - "anyhow", "borsh", "demo-stf", - "jsonrpsee", - "schemars", - "serde", - "serde_json", - "sov-cli", "sov-modules-api", - "sov-modules-macros", "sov-nft-module", "sov-rollup-interface", "sov-sequencer", diff --git a/utils/nft-utils/Cargo.toml b/utils/nft-utils/Cargo.toml index 3ef68cfb3..2aaa4bc2e 100644 --- a/utils/nft-utils/Cargo.toml +++ b/utils/nft-utils/Cargo.toml @@ -13,19 +13,12 @@ resolver = "2" autotests = false [dependencies] -anyhow = { workspace = true } -borsh = { workspace = true, features = ["rc"] } -serde = { workspace = true} -schemars = { workspace = true} -serde_json = { workspace = true} -jsonrpsee = { workspace = true, features = ["macros", "client-core", "server"]} +borsh = { workspace = true } +tokio = { workspace = true } sov-nft-module = { path = "../../module-system/module-implementations/sov-nft-module", features = ["native"]} -sov-modules-macros = {path = "../../module-system/sov-modules-macros"} sov-sequencer = { path = "../../full-node/sov-sequencer"} demo-stf = {path = "../../examples/demo-stf"} sov-rollup-interface = {path = "../../rollup-interface"} sov-modules-api = {path = "../../module-system/sov-modules-api", features = ["native"]} -sov-cli = {path = "../../module-system/sov-cli"} -tokio = {version = "1.32.0"} \ No newline at end of file From d1eec17a9c525a4002426c7aa766a241e89322de Mon Sep 17 00:00:00 2001 From: Blazej Kolad Date: Wed, 27 Sep 2023 19:11:22 +0200 Subject: [PATCH 21/34] Run tests with prover enabled. (#932) * Remove demo-rollup dep from demo-prover * add methods to demo-rollup * fix cargo hack * fix cargo hack * Cargo.toml * install toolchain * install toolchain * modify workflow * update cargo.toml * rust.yaml * Add guest-mock * use real prover in bank tests * native * fix lint * update yaml * fic coverage * ELF in CI * update bench get_guest_options() * update build.rs * remove local from demo-rollup * fix lint * remove ide_setup * Remove riscv32im-risc0-zkvm-elf --- .github/workflows/rust.yml | 25 +- Cargo.lock | 1 + examples/demo-prover/README.md | 3 - examples/demo-prover/ide_setup.md | 26 - examples/demo-prover/methods/Cargo.toml | 2 +- examples/demo-prover/methods/build.rs | 11 +- .../{guest => guest-celestia}/Cargo.lock | 20 +- .../{guest => guest-celestia}/Cargo.toml | 5 +- .../src/bin/rollup.rs | 2 +- .../demo-prover/methods/guest-mock/Cargo.lock | 1262 +++++++++++++++++ .../demo-prover/methods/guest-mock/Cargo.toml | 33 + .../methods/guest-mock/src/bin/mock_da.rs | 20 + .../demo-prover/methods/guest/download_std.rs | 175 --- .../guest/riscv32im-risc0-zkvm-elf.json | 21 - examples/demo-rollup/Cargo.toml | 7 +- examples/demo-rollup/src/lib.rs | 2 - examples/demo-rollup/src/rollup.rs | 5 +- examples/demo-rollup/src/verifier.rs | 17 - examples/demo-rollup/tests/bank/mod.rs | 8 +- examples/demo-rollup/tests/evm/mod.rs | 16 +- examples/demo-rollup/tests/test_helpers.rs | 4 +- examples/demo-stf/Cargo.toml | 6 +- examples/demo-stf/src/lib.rs | 17 + full-node/sov-stf-runner/Cargo.toml | 2 +- rollup-interface/Cargo.toml | 2 +- .../src/state_machine/mocks/da.rs | 11 +- .../src/state_machine/mocks/mod.rs | 6 +- 27 files changed, 1404 insertions(+), 305 deletions(-) delete mode 100644 examples/demo-prover/ide_setup.md rename examples/demo-prover/methods/{guest => guest-celestia}/Cargo.lock (99%) rename examples/demo-prover/methods/{guest => guest-celestia}/Cargo.toml (80%) rename examples/demo-prover/methods/{guest => guest-celestia}/src/bin/rollup.rs (96%) create mode 100644 examples/demo-prover/methods/guest-mock/Cargo.lock create mode 100644 examples/demo-prover/methods/guest-mock/Cargo.toml create mode 100644 examples/demo-prover/methods/guest-mock/src/bin/mock_da.rs delete mode 100644 examples/demo-prover/methods/guest/download_std.rs delete mode 100644 examples/demo-prover/methods/guest/riscv32im-risc0-zkvm-elf.json delete mode 100644 examples/demo-rollup/src/verifier.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 502ae161e..1b90e7b04 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -113,16 +113,19 @@ jobs: - name: cargo hack run: make check-features test: + name: test runs-on: buildjet-4vcpu-ubuntu-2204 timeout-minutes: 60 - env: - CI_SKIP_GUEST_BUILD: "1" steps: - uses: actions/checkout@v3 - uses: rui314/setup-mold@v1 - name: Install Rust run: rustup show # `cargo-nextest` is much faster than standard `cargo test`. + - name: Install cargo-risc0 # Risc0 v0.17 and higher require a cargo extension to build the guest code + run: cargo install cargo-risczero + - name: Install risc0-zkvm toolchain # Use the risc0 cargo extension to install the risc0 std library for the current toolchain + run: cargo risczero install - uses: taiki-e/install-action@nextest - uses: Swatinem/rust-cache@v2 with: @@ -144,6 +147,10 @@ jobs: - uses: rui314/setup-mold@v1 - name: Install Rust run: rustup show + - name: Install cargo-risc0 # Risc0 v0.17 and higher require a cargo extension to build the guest code + run: cargo install cargo-risczero + - name: Install risc0-zkvm toolchain # Use the risc0 cargo extension to install the risc0 std library for the current toolchain + run: cargo risczero install - uses: Swatinem/rust-cache@v2 with: cache-provider: "buildjet" @@ -193,10 +200,9 @@ jobs: echo "demo-rollup took too long to process transaction; exiting" exit 1 coverage: + name: coverage runs-on: buildjet-8vcpu-ubuntu-2204 timeout-minutes: 90 - env: - CI_SKIP_GUEST_BUILD: "1" steps: - uses: actions/checkout@v3 with: @@ -204,6 +210,10 @@ jobs: - uses: rui314/setup-mold@v1 - name: Install Rust run: rustup show + - name: Install cargo-risc0 # Risc0 v0.17 and higher require a cargo extension to build the guest code + run: cargo install cargo-risczero + - name: Install risc0-zkvm toolchain # Use the risc0 cargo extension to install the risc0 std library for the current toolchain + run: cargo risczero install - name: add llvm component run: rustup component add llvm-tools-preview - name: cargo install cargo-llvm-cov @@ -295,11 +305,18 @@ jobs: run: git diff --exit-code check-demo-rollup-bash-commands: runs-on: ubuntu-latest + # `test` has already built dependencies, so we can share + # caches (the profile is `dev` in both cases). + needs: test steps: - uses: actions/checkout@v3 - uses: rui314/setup-mold@v1 - name: Install Rust run: rustup show + - name: Install cargo-risc0 # Risc0 v0.17 and higher require a cargo extension to build the guest code + run: cargo install cargo-risczero + - name: Install risc0-zkvm toolchain # Use the risc0 cargo extension to install the risc0 std library for the current toolchain + run: cargo risczero install - uses: Swatinem/rust-cache@v2 with: cache-provider: "buildjet" diff --git a/Cargo.lock b/Cargo.lock index fa03da594..2d13c986c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7307,6 +7307,7 @@ dependencies = [ "ethers-signers", "hex", "jsonrpsee", + "methods", "prettytable-rs", "prometheus 0.11.0", "proptest", diff --git a/examples/demo-prover/README.md b/examples/demo-prover/README.md index 861c11da3..460995456 100644 --- a/examples/demo-prover/README.md +++ b/examples/demo-prover/README.md @@ -37,9 +37,6 @@ You'll need at least 96GiB of RAM to run this example on a x86_64 CPU. If you do 3. Make sure that there's no data from previous runs `rm -rf demo_data` 4. Execute `cargo run -- ../demo-rollup/rollup_config.toml`. -## Development - -Follow our [IDE integration](./ide_setup.md) guide document. ## License diff --git a/examples/demo-prover/ide_setup.md b/examples/demo-prover/ide_setup.md deleted file mode 100644 index b22cbb4ae..000000000 --- a/examples/demo-prover/ide_setup.md +++ /dev/null @@ -1,26 +0,0 @@ -# Getting Started - -## Setting up your IDE - -To get Rust Analyzer (or other language plugins) to work with Risc0, you'll need to set up the risc0 stdlib binaries in your sysroot. - -0. `cd methods/guest`. All future instructions assume that you're at the root of the guest crate. -1. Use `download_std.rs` to fetch the modified std lib code. One easy way to run this code is to rename it to `build.rs` and then - `cargo build`. The downloaded code will be stored in a directory called "riscv-guest-shim", which should be placed in the - `methods/guest` directory. -2. Build `.rlib` binaries from the modified stdlib. To build the standard library, use the command below (replacing the path). - To minimize extraneous code, you may want to comment out the dependencies section of `Cargo.toml` and the body of - `rollup.rs` before continuing. - -``` -__CARGO_TESTS_ONLY_SRC_ROOT="/path/to/riscv-guest-shim/rust-std" cargo run -Zbuild-std=core,alloc,proc_macro,panic_abort,std -Zbuild-std-features=compiler-builtins-mem --test --release -``` - -The output of this build will be stored in `target/riscv32im-risc0-zkvm-elf/release/deps`. - -Identify the `SYSROOT` from your default rust compiler using the command `rustc --print sysyroot`. Make a new directory -`lib/rustlib/riscv32im-risc0-zkvm-elf/lib` under your sysroot, and copy all of the stdlib `*.rlib` files into it. Now, your -toolchain will recognize `riscv32im-risc0-zkvm-elf` as an installed target. - -If you encounter any problems, try re-running above `cargo run -Zbuildstd ...` command without the `--test` flag, copy any additional -`.rlib`s into the `sysroot/lib/rustlib/riscv32im-risc0-zkvm-elf/lib` directory. diff --git a/examples/demo-prover/methods/Cargo.toml b/examples/demo-prover/methods/Cargo.toml index 166071cad..06e539cb1 100644 --- a/examples/demo-prover/methods/Cargo.toml +++ b/examples/demo-prover/methods/Cargo.toml @@ -10,7 +10,7 @@ publish = false risc0-build = { workspace = true } [package.metadata.risc0] -methods = ["guest"] +methods = ["guest-celestia", "guest-mock"] [features] bench = [] diff --git a/examples/demo-prover/methods/build.rs b/examples/demo-prover/methods/build.rs index 15d8003f1..79be247d9 100644 --- a/examples/demo-prover/methods/build.rs +++ b/examples/demo-prover/methods/build.rs @@ -6,8 +6,13 @@ fn main() { let out_dir = std::env::var_os("OUT_DIR").unwrap(); let out_dir = std::path::Path::new(&out_dir); let methods_path = out_dir.join("methods.rs"); - std::fs::write(methods_path, "pub const ROLLUP_ELF: &[u8] = &[];") - .expect("Failed to write mock rollup elf"); + + let elf = r#" + pub const ROLLUP_ELF: &[u8] = &[]; + pub const MOCK_DA_ELF: &[u8] = &[]; + "#; + + std::fs::write(methods_path, elf).expect("Failed to write mock rollup elf"); } else { let guest_pkg_to_options = get_guest_options(); risc0_build::embed_methods_with_options(guest_pkg_to_options); @@ -23,7 +28,7 @@ fn get_guest_options() -> HashMap<&'static str, risc0_build::GuestOptions> { fn get_guest_options() -> HashMap<&'static str, risc0_build::GuestOptions> { let mut guest_pkg_to_options = HashMap::new(); guest_pkg_to_options.insert( - "sov-demo-prover-guest", + "sov-demo-prover-guest-celestia", risc0_build::GuestOptions { features: vec!["bench".to_string()], }, diff --git a/examples/demo-prover/methods/guest/Cargo.lock b/examples/demo-prover/methods/guest-celestia/Cargo.lock similarity index 99% rename from examples/demo-prover/methods/guest/Cargo.lock rename to examples/demo-prover/methods/guest-celestia/Cargo.lock index 3bdff2f9a..414c154a8 100644 --- a/examples/demo-prover/methods/guest/Cargo.lock +++ b/examples/demo-prover/methods/guest-celestia/Cargo.lock @@ -312,6 +312,7 @@ dependencies = [ "sov-rollup-interface", "sov-sequencer-registry", "sov-state", + "sov-stf-runner", "sov-value-setter", "tracing", ] @@ -1341,7 +1342,7 @@ dependencies = [ ] [[package]] -name = "sov-demo-prover-guest" +name = "sov-demo-prover-guest-celestia" version = "0.2.0" dependencies = [ "anyhow", @@ -1350,23 +1351,7 @@ dependencies = [ "risc0-zkvm", "risc0-zkvm-platform", "sov-celestia-adapter", - "sov-demo-rollup", - "sov-risc0-adapter", -] - -[[package]] -name = "sov-demo-rollup" -version = "0.2.0" -dependencies = [ - "const-rollup-config", - "demo-stf", - "sov-celestia-adapter", - "sov-modules-api", - "sov-modules-stf-template", "sov-risc0-adapter", - "sov-rollup-interface", - "sov-state", - "sov-stf-runner", ] [[package]] @@ -1500,7 +1485,6 @@ dependencies = [ "borsh", "hex", "serde", - "sov-celestia-adapter", "sov-modules-api", "sov-rollup-interface", "sov-state", diff --git a/examples/demo-prover/methods/guest/Cargo.toml b/examples/demo-prover/methods/guest-celestia/Cargo.toml similarity index 80% rename from examples/demo-prover/methods/guest/Cargo.toml rename to examples/demo-prover/methods/guest-celestia/Cargo.toml index c0945ff9c..3927761c6 100644 --- a/examples/demo-prover/methods/guest/Cargo.toml +++ b/examples/demo-prover/methods/guest-celestia/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "sov-demo-prover-guest" +name = "sov-demo-prover-guest-celestia" version = "0.2.0" edition = "2021" resolver = "2" @@ -10,8 +10,7 @@ resolver = "2" anyhow = "1.0.68" risc0-zkvm = { version = "0.18", default-features = false, features = ["std"] } risc0-zkvm-platform = "0.18" -demo-stf = { path = "../../../demo-stf", default-features = false } -sov-demo-rollup = { path = "../../../demo-rollup", default-features = false } +demo-stf = { path = "../../../demo-stf" } sov-risc0-adapter = { path = "../../../../adapters/risc0" } const-rollup-config = { path = "../../../const-rollup-config" } sov-celestia-adapter = { path = "../../../../adapters/celestia" } diff --git a/examples/demo-prover/methods/guest/src/bin/rollup.rs b/examples/demo-prover/methods/guest-celestia/src/bin/rollup.rs similarity index 96% rename from examples/demo-prover/methods/guest/src/bin/rollup.rs rename to examples/demo-prover/methods/guest-celestia/src/bin/rollup.rs index 4d8c6f17a..729d0d734 100644 --- a/examples/demo-prover/methods/guest/src/bin/rollup.rs +++ b/examples/demo-prover/methods/guest-celestia/src/bin/rollup.rs @@ -6,7 +6,7 @@ use const_rollup_config::ROLLUP_NAMESPACE_RAW; use demo_stf::app::create_zk_app_template; use sov_celestia_adapter::types::NamespaceId; use sov_celestia_adapter::verifier::CelestiaVerifier; -use sov_demo_rollup::AppVerifier; +use demo_stf::AppVerifier; use sov_risc0_adapter::guest::Risc0Guest; // The rollup stores its data in the namespace b"sov-test" on Celestia diff --git a/examples/demo-prover/methods/guest-mock/Cargo.lock b/examples/demo-prover/methods/guest-mock/Cargo.lock new file mode 100644 index 000000000..5e881876d --- /dev/null +++ b/examples/demo-prover/methods/guest-mock/Cargo.lock @@ -0,0 +1,1262 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bcs" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd3ffe8b19a604421a5d461d4a70346223e535903fbc3067138bddbebddcf77" +dependencies = [ + "serde", + "thiserror", +] + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive", + "bytes", + "hashbrown", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "git+https://github.com/rust-lang/cc-rs?rev=e5bbdfa#e5bbdfa1fa468c028cb38fee6c35a3cf2e5a2736" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "const-rollup-config" +version = "0.2.0" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622178105f911d937a42cdb140730ba4a3ed2becd8ae6ce39c7d28b5d75d4588" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "demo-stf" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "hex", + "serde", + "sov-accounts", + "sov-bank", + "sov-blob-storage", + "sov-chain-state", + "sov-modules-api", + "sov-modules-stf-template", + "sov-nft-module", + "sov-rollup-interface", + "sov-sequencer-registry", + "sov-state", + "sov-stf-runner", + "sov-value-setter", + "tracing", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clone" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" + +[[package]] +name = "ed25519" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" +dependencies = [ + "curve25519-dalek", + "ed25519", + "sha2", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elf" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b183d6ce6ca4cf30e3db37abf5b52568b5f9015c97d9fbdd7026aa5dcdd758" + +[[package]] +name = "errno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "fiat-crypto" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "ics23" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442d4bab37956e76f739c864f246c825d87c0bb7f9afa65660c57833c91bf6d4" +dependencies = [ + "anyhow", + "bytes", + "hex", + "informalsystems-pbjson", + "prost", + "ripemd", + "serde", + "sha2", + "sha3", +] + +[[package]] +name = "informalsystems-pbjson" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eecd90f87bea412eac91c6ef94f6b1e390128290898cbe14f2b926787ae1fb" +dependencies = [ + "base64", + "serde", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "jmt" +version = "0.7.0" +source = "git+https://github.com/penumbra-zone/jmt.git?rev=199ae1c185e74ee2133db773efacff56706085aa#199ae1c185e74ee2133db773efacff56706085aa" +dependencies = [ + "anyhow", + "borsh", + "digest", + "hashbrown", + "hex", + "ics23", + "itertools", + "mirai-annotations", + "num-derive 0.3.3", + "num-traits", + "serde", + "sha2", + "thiserror", + "tracing", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "linux-raw-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "platforms" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest", +] + +[[package]] +name = "risc0-binfmt" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede27631e6b2a946a43db812063453c9701d5d2544d82f9abec2cc12574ebb8e" +dependencies = [ + "anyhow", + "elf", + "log", + "risc0-zkp", + "risc0-zkvm-platform", + "serde", +] + +[[package]] +name = "risc0-circuit-rv32im" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68e00222152fdc94cacc9b6682b5c0cbe8138f1ee82e80c24a64d9ad2c6d7415" +dependencies = [ + "anyhow", + "log", + "risc0-core", + "risc0-zkp", + "risc0-zkvm-platform", + "tracing", +] + +[[package]] +name = "risc0-core" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08605aec93ea22ed83f7f81f42e2d7287a5b0c749d8671f94de9d5994020045c" +dependencies = [ + "bytemuck", + "rand_core", +] + +[[package]] +name = "risc0-zkp" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28166926bb177824939f4e91083198f9f3da8137aeac32361bd34548c0526fa5" +dependencies = [ + "anyhow", + "blake2", + "bytemuck", + "digest", + "hex", + "log", + "paste", + "rand_core", + "risc0-core", + "risc0-zkvm-platform", + "serde", + "sha2", + "tracing", +] + +[[package]] +name = "risc0-zkvm" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec972152bcaa1a2967e412e22a84f6e2984a95c701bcc7943ca8ca10126ee0a2" +dependencies = [ + "anyhow", + "bytemuck", + "cfg-if", + "getrandom", + "hex", + "libm", + "log", + "num-derive 0.4.0", + "num-traits", + "risc0-binfmt", + "risc0-circuit-rv32im", + "risc0-core", + "risc0-zkp", + "risc0-zkvm-platform", + "serde", + "tempfile", + "tracing", +] + +[[package]] +name = "risc0-zkvm-platform" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8524b46783b58b00e9b2a4712e837093c975b23cf25bfaf99e1cf69e9011bf6b" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schemars" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "git+https://github.com/risc0/RustCrypto-hashes?tag=sha2/v0.10.6-risc0#e75cafd9f55da196061f6fadf8bc8a86778192b7" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" + +[[package]] +name = "sov-accounts" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "sov-modules-api", + "sov-state", + "thiserror", +] + +[[package]] +name = "sov-bank" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "sov-modules-api", + "sov-rollup-interface", + "sov-state", + "thiserror", +] + +[[package]] +name = "sov-blob-storage" +version = "0.2.0" +dependencies = [ + "anyhow", + "bincode", + "borsh", + "hex", + "sov-chain-state", + "sov-modules-api", + "sov-sequencer-registry", + "sov-state", + "tracing", +] + +[[package]] +name = "sov-chain-state" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "serde", + "sov-modules-api", + "sov-rollup-interface", + "sov-state", +] + +[[package]] +name = "sov-demo-prover-guest-mock" +version = "0.2.0" +dependencies = [ + "anyhow", + "const-rollup-config", + "demo-stf", + "risc0-zkvm", + "risc0-zkvm-platform", + "sov-risc0-adapter", + "sov-rollup-interface", +] + +[[package]] +name = "sov-first-read-last-write-cache" +version = "0.2.0" +dependencies = [ + "thiserror", +] + +[[package]] +name = "sov-modules-api" +version = "0.2.0" +dependencies = [ + "anyhow", + "bech32", + "borsh", + "derive_more", + "ed25519-dalek", + "jmt", + "serde", + "sha2", + "sov-first-read-last-write-cache", + "sov-modules-macros", + "sov-rollup-interface", + "sov-state", + "thiserror", +] + +[[package]] +name = "sov-modules-macros" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "proc-macro2", + "quote", + "schemars", + "serde_json", + "syn 1.0.109", +] + +[[package]] +name = "sov-modules-stf-template" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "hex", + "jmt", + "serde", + "sov-modules-api", + "sov-rollup-interface", + "sov-state", + "thiserror", + "tracing", +] + +[[package]] +name = "sov-nft-module" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "sov-modules-api", + "sov-modules-macros", + "sov-state", +] + +[[package]] +name = "sov-risc0-adapter" +version = "0.2.0" +dependencies = [ + "anyhow", + "bincode", + "bytemuck", + "risc0-zkvm", + "risc0-zkvm-platform", + "serde", + "sov-rollup-interface", + "sov-zk-cycle-utils", +] + +[[package]] +name = "sov-rollup-interface" +version = "0.2.0" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "borsh", + "bytes", + "digest", + "hex", + "serde", + "sha2", + "thiserror", +] + +[[package]] +name = "sov-sequencer-registry" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "sov-bank", + "sov-modules-api", + "sov-state", +] + +[[package]] +name = "sov-state" +version = "0.2.0" +dependencies = [ + "anyhow", + "bcs", + "borsh", + "hex", + "jmt", + "serde", + "serde_json", + "sha2", + "sov-first-read-last-write-cache", + "sov-rollup-interface", + "thiserror", +] + +[[package]] +name = "sov-stf-runner" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "hex", + "serde", + "sov-modules-api", + "sov-rollup-interface", + "sov-state", +] + +[[package]] +name = "sov-value-setter" +version = "0.2.0" +dependencies = [ + "anyhow", + "borsh", + "sov-modules-api", + "sov-state", + "thiserror", +] + +[[package]] +name = "sov-zk-cycle-utils" +version = "0.2.0" +dependencies = [ + "risc0-zkvm", + "risc0-zkvm-platform", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/examples/demo-prover/methods/guest-mock/Cargo.toml b/examples/demo-prover/methods/guest-mock/Cargo.toml new file mode 100644 index 000000000..3daff6a75 --- /dev/null +++ b/examples/demo-prover/methods/guest-mock/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "sov-demo-prover-guest-mock" +version = "0.2.0" +edition = "2021" +resolver = "2" + +[workspace] + +[dependencies] +anyhow = "1.0.68" +risc0-zkvm = { version = "0.18", default-features = false, features = ["std"] } +risc0-zkvm-platform = "0.18" +sov-rollup-interface = { path = "../../../../rollup-interface", features = ["mocks"] } +demo-stf = { path = "../../../demo-stf" } +sov-risc0-adapter = { path = "../../../../adapters/risc0" } +const-rollup-config = { path = "../../../const-rollup-config" } + +[patch.crates-io] +sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2/v0.10.6-risc0" } + +[profile.dev] +opt-level = 3 + +[profile.dev.build-override] +opt-level = 3 + +[profile.release] +debug = 1 +lto = true + +[profile.release.build-override] +opt-level = 3 + diff --git a/examples/demo-prover/methods/guest-mock/src/bin/mock_da.rs b/examples/demo-prover/methods/guest-mock/src/bin/mock_da.rs new file mode 100644 index 000000000..dc24324a0 --- /dev/null +++ b/examples/demo-prover/methods/guest-mock/src/bin/mock_da.rs @@ -0,0 +1,20 @@ +#![no_main] + +use demo_stf::app::create_zk_app_template; +use demo_stf::AppVerifier; +use sov_risc0_adapter::guest::Risc0Guest; + +use sov_rollup_interface::mocks::MockDaVerifier; + +risc0_zkvm::guest::entry!(main); + +pub fn main() { + let guest = Risc0Guest::new(); + + let mut stf_verifier = + AppVerifier::new(create_zk_app_template::(), MockDaVerifier {}); + + stf_verifier + .run_block(guest) + .expect("Prover must be honest"); +} diff --git a/examples/demo-prover/methods/guest/download_std.rs b/examples/demo-prover/methods/guest/download_std.rs deleted file mode 100644 index d6aba603b..000000000 --- a/examples/demo-prover/methods/guest/download_std.rs +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2023 RISC Zero, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::{ - default::Default, - fs::{self, File}, - path::{Path, PathBuf}, -}; - -use downloader::{Download, Downloader}; -// use sha2::{Digest as ShaDigest, Sha256}; -use tempfile::tempdir_in; -use zip::ZipArchive; - -#[derive(Debug)] -struct ZipMapEntry { - filename: &'static str, - zip_url: &'static str, - src_prefix: &'static str, - dst_prefix: &'static str, -} - -// Sources for standard library, and where they should be mapped to. -const RUST_LIB_MAP : &[ZipMapEntry] = &[ - ZipMapEntry { - filename: "53bbc8fc2afb2e10e3a90d7bf188bfd6598374ab.zip", - zip_url: "https://github.com/risc0/rust/archive/53bbc8fc2afb2e10e3a90d7bf188bfd6598374ab.zip", - src_prefix: "rust-53bbc8fc2afb2e10e3a90d7bf188bfd6598374ab/library", - dst_prefix: "library" - }, - ZipMapEntry { - filename: "790411f93c4b5eada3c23abb4c9a063fb0b24d99.zip", - zip_url: "https://github.com/rust-lang/stdarch/archive/790411f93c4b5eada3c23abb4c9a063fb0b24d99.zip", - src_prefix:"stdarch-790411f93c4b5eada3c23abb4c9a063fb0b24d99", - dst_prefix: "library/stdarch" - }, - ZipMapEntry { - filename: "07872f28cd8a65c3c7428811548dc85f1f2fb05b.zip", - zip_url: "https://github.com/rust-lang/backtrace-rs/archive/07872f28cd8a65c3c7428811548dc85f1f2fb05b.zip", - src_prefix:"backtrace-rs-07872f28cd8a65c3c7428811548dc85f1f2fb05b", - dst_prefix: "library/backtrace" - }, -]; - -fn setup_guest_build_env

(out_dir: P) -where - P: AsRef, -{ - // Rust standard library. If any of the RUST_LIB_MAP changed, we - // want to have a different hash so that we make sure we recompile. - // let (_, _) = sha_digest_with_hex(format!("{:?}", RUST_LIB_MAP).as_bytes()); - // TODO: This breaks change detection for the std source. Fix it - let rust_lib_path = out_dir.as_ref().join("rust-std"); - if !rust_lib_path.exists() { - println!( - "Standard library {} does not exist; downloading", - rust_lib_path.display() - ); - - download_zip_map(RUST_LIB_MAP, &rust_lib_path); - } -} - -fn risc0_cache() -> PathBuf { - directories::ProjectDirs::from("com.risczero", "RISC Zero", "risc0") - .unwrap() - .cache_dir() - .into() -} - -fn download_zip_map

(zip_map: &[ZipMapEntry], dest_base: P) -where - P: AsRef, -{ - let cache_dir = risc0_cache(); - if !cache_dir.is_dir() { - fs::create_dir_all(&cache_dir).unwrap(); - } - - let temp_dir = tempdir_in(&cache_dir).unwrap(); - let mut downloader = Downloader::builder() - .download_folder(temp_dir.path()) - .build() - .unwrap(); - - let tmp_dest_base = dest_base.as_ref().with_extension("downloadtmp"); - if tmp_dest_base.exists() { - fs::remove_dir_all(&tmp_dest_base).unwrap(); - } - - for zm in zip_map.iter() { - let src_prefix = Path::new(&zm.src_prefix); - let dst_prefix = tmp_dest_base.join(&zm.dst_prefix); - fs::create_dir_all(&dst_prefix).unwrap(); - - let zip_path = cache_dir.join(zm.filename); - if !zip_path.is_file() { - println!( - "Downloading {}, mapping {} to {}", - zm.zip_url, - zm.src_prefix, - dst_prefix.display() - ); - let dl = Download::new(zm.zip_url); - downloader.download(&[dl]).unwrap().iter().for_each(|x| { - let summary = x.as_ref().unwrap(); - println!("Downloaded: {}", summary.file_name.display()); - }); - fs::rename(temp_dir.path().join(zm.filename), &zip_path).unwrap(); - } - - let zip_file = File::open(zip_path).unwrap(); - let mut zip = ZipArchive::new(zip_file).unwrap(); - println!("Got zip with {} files", zip.len()); - - let mut nwrote: u32 = 0; - for i in 0..zip.len() { - let mut f = zip.by_index(i).unwrap(); - let name = f.enclosed_name().unwrap(); - if let Ok(relative_src) = name.strip_prefix(src_prefix) { - let dest_name = dst_prefix.join(relative_src); - if f.is_dir() { - fs::create_dir_all(dest_name).unwrap(); - continue; - } - if !f.is_file() { - continue; - } - std::io::copy(&mut f, &mut File::create(&dest_name).unwrap()).unwrap(); - nwrote += 1; - } - } - println!("Wrote {} files", nwrote); - } - fs::rename(&tmp_dest_base, dest_base.as_ref()).unwrap(); -} - -/// Options defining how to embed a guest package in -/// [`embed_methods_with_options`]. -pub struct GuestOptions { - /// Features for cargo to build the guest with. - pub features: Vec, - - /// Enable standard library support - pub std: bool, -} - -impl Default for GuestOptions { - fn default() -> Self { - GuestOptions { - features: vec![], - std: true, - } - } -} - -/// Embeds methods built for RISC-V for use by host-side dependencies. -/// Specify custom options for a guest package by defining its [GuestOptions]. -/// See [embed_methods]. -fn main() { - let guest_dir = Path::new("riscv-guest-shim"); - - setup_guest_build_env(&guest_dir); -} diff --git a/examples/demo-prover/methods/guest/riscv32im-risc0-zkvm-elf.json b/examples/demo-prover/methods/guest/riscv32im-risc0-zkvm-elf.json deleted file mode 100644 index d97ac0feb..000000000 --- a/examples/demo-prover/methods/guest/riscv32im-risc0-zkvm-elf.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "arch": "riscv32", - "cpu": "generic-rv32", - "data-layout": "e-m:e-p:32:32-i64:64-n32-S128", - "eh-frame-header": false, - "emit-debug-gdb-scripts": false, - "executables": true, - "features": "+m", - "is-builtin": false, - "linker": "rust-lld", - "linker-flavor": "ld.lld", - "link-script": "OUTPUT_FORMAT(\"elf32-littleriscv\", \"elf32-littleriscv\", \"elf32-littleriscv\")\nOUTPUT_ARCH(riscv)\nENTRY(_start)\nEXTERN(__start)\n\n/* Must match risc0/zkvm/platform/src/memory.rs */\nMEMORY {\n stack : ORIGIN = 0x00000400, LENGTH = 8M - 1K\n data (RW) : ORIGIN = 0x00080000, LENGTH = 24M\n heap : ORIGIN = 0x02000000, LENGTH = 80M\n prog (X) : ORIGIN = 0x07000000, LENGTH = 80M\n}\n\nSECTIONS {\n .text : {\n *(.text._start)\n *(.text.__start)\n *(.text*)\n *(.rodata*)\n *(.srodata*)\n } >prog\n\n .data : {\n *(.data .data.*)\n *(.gnu.linkonce.d.*)\n __global_pointer$ = . + 0x800;\n *(.sdata .sdata.* .sdata2.*)\n *(.gnu.linkonce.s.*)\n } >data\n\n . = ALIGN(4);\n\n .bss (NOLOAD) : {\n __bss_begin = .;\n *(.sbss*)\n *(.gnu.linkonce.sb.*)\n *(.bss .bss.*)\n *(.gnu.linkonce.b.*)\n *(COMMON)\n . = ALIGN(4);\n __bss_end = .;\n } >data\n\n __bss_size = __bss_end - __bss_begin;\n\n __heap_start = ORIGIN(heap);\n __heap_end = __heap_start + LENGTH(heap);\n __heap_size = LENGTH(heap);\n\n __stack_init$ = ORIGIN(stack) + LENGTH(stack) - 4;\n\n /DISCARD/ : {\n *(.rel*)\n *(.comment)\n *(.eh_frame)\n *(.riscv.attributes)\n }\n}", - "llvm-target": "riscv32", - "max-atomic-width": 32, - "os": "zkvm", - "panic-strategy": "abort", - "relocation-model": "static", - "singlethread": true, - "target-pointer-width": "32", - "vendor": "risc0" -} diff --git a/examples/demo-rollup/Cargo.toml b/examples/demo-rollup/Cargo.toml index 2c3a0a2c2..2814df35e 100644 --- a/examples/demo-rollup/Cargo.toml +++ b/examples/demo-rollup/Cargo.toml @@ -20,7 +20,7 @@ sov-rollup-interface = { path = "../../rollup-interface" } sov-modules-stf-template = { path = "../../module-system/sov-modules-stf-template" } sov-modules-api = { path = "../../module-system/sov-modules-api" } demo-stf = { path = "../demo-stf" } - +methods = { path = "../demo-prover/methods" } async-trait = { workspace = true, optional = true } anyhow = { workspace = true, optional = true } borsh = { workspace = true, features = ["bytes"], optional = true } @@ -73,13 +73,12 @@ revm = { workspace = true } [features] default = ["native"] # Deviate from convention by making the "native" feature active by default. This aligns with how this package is meant to be used (as a binary first, library second). -experimental = ["sov-ethereum/experimental", "reth-primitives", "secp256k1", "demo-stf/experimental", "local"] +experimental = ["default", "sov-ethereum/experimental", "reth-primitives", "secp256k1", "demo-stf/experimental", "sov-ethereum/local"] native = ["anyhow", "jsonrpsee", "serde", "serde_json", "tracing", "tokio", "tracing-subscriber", "demo-stf/native", "sov-modules-stf-template/native", "sov-risc0-adapter/native", "sov-modules-api/native", "sov-state/native", "sov-cli", "clap", "sov-celestia-adapter/native", "sov-db", "sov-sequencer", "sov-stf-runner/native", - "sov-modules-api/native"] + "sov-modules-api/native", "sov-rollup-interface/native"] bench = ["native", "async-trait", "borsh", "hex"] -local = ["sov-ethereum/local"] offchain = ["demo-stf/offchain"] [[bench]] diff --git a/examples/demo-rollup/src/lib.rs b/examples/demo-rollup/src/lib.rs index 68d2030bc..e25d878f5 100644 --- a/examples/demo-rollup/src/lib.rs +++ b/examples/demo-rollup/src/lib.rs @@ -6,7 +6,6 @@ pub mod register_rpc; #[cfg(feature = "native")] mod rollup; -mod verifier; use const_rollup_config::ROLLUP_NAMESPACE_RAW; #[cfg(feature = "native")] pub use rollup::{ @@ -16,7 +15,6 @@ pub use rollup::{ use sov_celestia_adapter::types::NamespaceId; #[cfg(feature = "native")] use sov_db::ledger_db::LedgerDB; -pub use verifier::AppVerifier; /// The rollup stores its data in the namespace b"sov-test" on Celestia /// You can change this constant to point your rollup at a different namespace diff --git a/examples/demo-rollup/src/rollup.rs b/examples/demo-rollup/src/rollup.rs index 48032cbd1..cdaec16b6 100644 --- a/examples/demo-rollup/src/rollup.rs +++ b/examples/demo-rollup/src/rollup.rs @@ -8,6 +8,7 @@ use demo_stf::app::DefaultPrivateKey; use demo_stf::app::{create_zk_app_template, App, DefaultContext}; use demo_stf::genesis_config::get_genesis_config; use demo_stf::runtime::{get_rpc_methods, GenesisConfig, Runtime}; +use demo_stf::AppVerifier; #[cfg(feature = "experimental")] use secp256k1::SecretKey; use sov_celestia_adapter::verifier::address::CelestiaAddress; @@ -32,7 +33,7 @@ use tracing::debug; #[cfg(feature = "experimental")] use crate::register_rpc::register_ethereum; use crate::register_rpc::{register_ledger, register_sequencer}; -use crate::{initialize_ledger, AppVerifier, ROLLUP_NAMESPACE}; +use crate::{initialize_ledger, ROLLUP_NAMESPACE}; #[cfg(feature = "experimental")] const TX_SIGNER_PRIV_KEY_PATH: &str = "../test-data/keys/tx_signer_private_key.json"; @@ -136,7 +137,6 @@ pub async fn new_rollup_with_celestia_da( eth_rpc_config: EthRpcConfig { min_blob_size: Some(1), sov_tx_signer_priv_key: read_sov_tx_signer_priv_key()?, - #[cfg(feature = "local")] eth_signer, }, prover, @@ -185,7 +185,6 @@ pub fn new_rollup_with_mock_da_from_config( eth_rpc_config: EthRpcConfig { min_blob_size: Some(1), sov_tx_signer_priv_key: read_sov_tx_signer_priv_key()?, - #[cfg(feature = "local")] eth_signer, }, prover, diff --git a/examples/demo-rollup/src/verifier.rs b/examples/demo-rollup/src/verifier.rs deleted file mode 100644 index bdd769e5b..000000000 --- a/examples/demo-rollup/src/verifier.rs +++ /dev/null @@ -1,17 +0,0 @@ -use demo_stf::runtime::Runtime; -use sov_modules_api::default_context::ZkDefaultContext; -use sov_modules_stf_template::AppTemplate; -use sov_rollup_interface::da::DaVerifier; -use sov_stf_runner::verifier::StateTransitionVerifier; - -/// A verifier for the demo rollup -pub type AppVerifier = StateTransitionVerifier< - AppTemplate< - ZkDefaultContext, - ::Spec, - Zk, - Runtime::Spec>, - >, - DA, - Zk, ->; diff --git a/examples/demo-rollup/tests/bank/mod.rs b/examples/demo-rollup/tests/bank/mod.rs index 4c381f2ae..8db0f08e5 100644 --- a/examples/demo-rollup/tests/bank/mod.rs +++ b/examples/demo-rollup/tests/bank/mod.rs @@ -5,6 +5,8 @@ use demo_stf::app::DefaultPrivateKey; use demo_stf::runtime::RuntimeCall; use jsonrpsee::core::client::{Subscription, SubscriptionClientT}; use jsonrpsee::rpc_params; +use methods::MOCK_DA_ELF; +use sov_demo_rollup::DemoProverConfig; use sov_modules_api::default_context::DefaultContext; use sov_modules_api::transaction::Transaction; use sov_modules_api::{PrivateKey, Spec}; @@ -68,11 +70,11 @@ async fn send_test_create_token_tx(rpc_address: SocketAddr) -> Result<(), anyhow async fn bank_tx_tests() -> Result<(), anyhow::Error> { let (port_tx, port_rx) = tokio::sync::oneshot::channel(); - // Use a dummy `elf` file, since the prover doesn't currently use it in native execution - let prover = Risc0Host::new(&[]); + let prover = Risc0Host::new(MOCK_DA_ELF); + let config = DemoProverConfig::Execute; let rollup_task = tokio::spawn(async { - start_rollup(port_tx, Some(prover)).await; + start_rollup(port_tx, Some((prover, config))).await; }); // Wait for rollup task to start: diff --git a/examples/demo-rollup/tests/evm/mod.rs b/examples/demo-rollup/tests/evm/mod.rs index 932a5f24a..cc867c47a 100644 --- a/examples/demo-rollup/tests/evm/mod.rs +++ b/examples/demo-rollup/tests/evm/mod.rs @@ -205,7 +205,6 @@ impl TestClient { Ok(ethereum_types::U256::from(resp_array)) } - #[cfg(feature = "local")] async fn eth_accounts(&self) -> Vec

{ self.http_client .request("eth_accounts", rpc_params![]) @@ -213,7 +212,6 @@ impl TestClient { .unwrap() } - #[cfg(feature = "local")] async fn eth_send_transaction(&self, tx: TypedTransaction) -> PendingTransaction<'_, Http> { self.client .provider() @@ -303,10 +301,8 @@ impl TestClient { set_value_req.await.unwrap().unwrap().transaction_hash }; - { - let get_arg = self.query_contract(contract_address).await?; - assert_eq!(set_arg, get_arg.as_u32()); - } + let get_arg = self.query_contract(contract_address).await?; + assert_eq!(set_arg, get_arg.as_u32()); // Check that the second block has published // None should return the latest block @@ -343,7 +339,6 @@ impl TestClient { assert_eq!(102, get_arg.as_u32()); } - #[cfg(feature = "local")] { let value = 103; @@ -378,11 +373,8 @@ async fn send_tx_test_to_eth(rpc_address: SocketAddr) -> Result<(), Box( rpc_reporting_channel: oneshot::Sender, - prover: Option, + prover: Option<(Vm, DemoProverConfig)>, ) { let temp_dir = tempfile::tempdir().unwrap(); let temp_path = temp_dir.path(); @@ -26,7 +26,7 @@ pub async fn start_rollup( }, da: MockDaConfig {}, }; - let prover = prover.map(|vm| (vm, DemoProverConfig::Simulate)); + let rollup = new_rollup_with_mock_da_from_config(rollup_config, prover).expect("Rollup config is valid"); rollup diff --git a/examples/demo-stf/Cargo.toml b/examples/demo-stf/Cargo.toml index 2d888f222..28a93152e 100644 --- a/examples/demo-stf/Cargo.toml +++ b/examples/demo-stf/Cargo.toml @@ -22,6 +22,8 @@ tokio = { workspace = true, optional = true } hex = { workspace = true } tracing = { workspace = true } + +sov-stf-runner = { path = "../../full-node/sov-stf-runner" } sov-rollup-interface = { path = "../../rollup-interface" } sov-cli = { path = "../../module-system/sov-cli", optional = true } sov-sequencer-registry = { path = "../../module-system/module-implementations/sov-sequencer-registry" } @@ -36,7 +38,6 @@ sov-accounts = { path = "../../module-system/module-implementations/sov-accounts sov-state = { path = "../../module-system/sov-state" } sov-modules-api = { path = "../../module-system/sov-modules-api" } sov-sequencer = { path = "../../full-node/sov-sequencer", optional = true } -sov-stf-runner = { path = "../../full-node/sov-stf-runner", optional = true } # Only enable the evm on "experimental" feature sov-evm = { path = "../../module-system/module-implementations/sov-evm", optional = true } reth-primitives = { workspace = true, optional = true } @@ -53,6 +54,7 @@ default = [] offchain = ["sov-nft-module/offchain"] experimental = ["sov-evm/experimental", "reth-primitives"] native = [ + "sov-stf-runner/native", "sov-bank/native", "sov-nft-module/native", "sov-cli", @@ -63,9 +65,9 @@ native = [ "sov-value-setter/native", "sov-modules-api/native", "sov-rollup-interface/mocks", + "sov-rollup-interface/native", "sov-modules-stf-template/native", "sov-sequencer", - "sov-stf-runner/native", "clap", "serde_json", "jsonrpsee", diff --git a/examples/demo-stf/src/lib.rs b/examples/demo-stf/src/lib.rs index cc9131986..c5a640213 100644 --- a/examples/demo-stf/src/lib.rs +++ b/examples/demo-stf/src/lib.rs @@ -10,4 +10,21 @@ pub mod tests; #[cfg(feature = "native")] pub mod cli; +use runtime::Runtime; +use sov_modules_api::default_context::ZkDefaultContext; +use sov_modules_stf_template::AppTemplate; +use sov_rollup_interface::da::DaVerifier; pub use sov_state::ArrayWitness; +use sov_stf_runner::verifier::StateTransitionVerifier; + +/// A verifier for the demo rollup +pub type AppVerifier = StateTransitionVerifier< + AppTemplate< + ZkDefaultContext, + ::Spec, + Zk, + Runtime::Spec>, + >, + DA, + Zk, +>; diff --git a/full-node/sov-stf-runner/Cargo.toml b/full-node/sov-stf-runner/Cargo.toml index ef0d24cd4..dc4be096b 100644 --- a/full-node/sov-stf-runner/Cargo.toml +++ b/full-node/sov-stf-runner/Cargo.toml @@ -26,9 +26,9 @@ sov-db = { path = "../db/sov-db", version = "0.2", optional = true } sov-rollup-interface = { path = "../../rollup-interface", version = "0.2" } sov-state = { path = "../../module-system/sov-state", version = "0.2" } sov-modules-api = { path = "../../module-system/sov-modules-api", version = "0.2" } -sov-celestia-adapter = { path = "../../adapters/celestia", version = "0.2" } [dev-dependencies] +sov-celestia-adapter = { path = "../../adapters/celestia", version = "0.2" } tempfile = { workspace = true } rand = { workspace = true } diff --git a/rollup-interface/Cargo.toml b/rollup-interface/Cargo.toml index 9dfc68798..56161665e 100644 --- a/rollup-interface/Cargo.toml +++ b/rollup-interface/Cargo.toml @@ -49,4 +49,4 @@ proptest-derive = { workspace = true } default = [] native = ["tokio"] fuzzing = ["proptest", "proptest-derive", "sha2"] -mocks = ["sha2", "bytes/serde", "tokio"] +mocks = ["sha2", "bytes/serde"] diff --git a/rollup-interface/src/state_machine/mocks/da.rs b/rollup-interface/src/state_machine/mocks/da.rs index 2ad670eae..20c38d2eb 100644 --- a/rollup-interface/src/state_machine/mocks/da.rs +++ b/rollup-interface/src/state_machine/mocks/da.rs @@ -1,6 +1,8 @@ use std::fmt::Display; +#[cfg(feature = "native")] use std::sync::Arc; +#[cfg(feature = "native")] use async_trait::async_trait; use borsh::{BorshDeserialize, BorshSerialize}; use bytes::Bytes; @@ -10,7 +12,9 @@ use crate::da::{ BlobReaderTrait, BlockHashTrait, BlockHeaderTrait, CountedBufReader, DaSpec, DaVerifier, Time, }; use crate::mocks::MockValidityCond; -use crate::services::da::{DaService, SlotData}; +#[cfg(feature = "native")] +use crate::services::da::DaService; +use crate::services::da::SlotData; use crate::{BasicAddress, RollupAddress}; const JAN_1_2023: i64 = 1672531200; @@ -269,9 +273,12 @@ impl DaSpec for MockDaSpec { type ChainParams = (); } +#[cfg(feature = "native")] use tokio::sync::mpsc::{self, Receiver, Sender}; +#[cfg(feature = "native")] use tokio::sync::Mutex; +#[cfg(feature = "native")] #[derive(Clone)] /// DaService used in tests. pub struct MockDaService { @@ -280,6 +287,7 @@ pub struct MockDaService { sequencer_da_address: MockAddress, } +#[cfg(feature = "native")] impl MockDaService { /// Creates a new MockDaService. pub fn new(sequencer_da_address: MockAddress) -> Self { @@ -292,6 +300,7 @@ impl MockDaService { } } +#[cfg(feature = "native")] #[async_trait] impl DaService for MockDaService { type Spec = MockDaSpec; diff --git a/rollup-interface/src/state_machine/mocks/mod.rs b/rollup-interface/src/state_machine/mocks/mod.rs index 232a5626f..a489035ea 100644 --- a/rollup-interface/src/state_machine/mocks/mod.rs +++ b/rollup-interface/src/state_machine/mocks/mod.rs @@ -4,9 +4,11 @@ mod da; mod validity_condition; mod zk_vm; +#[cfg(feature = "native")] +pub use da::MockDaService; pub use da::{ - MockAddress, MockBlob, MockBlock, MockBlockHeader, MockDaConfig, MockDaService, MockDaSpec, - MockDaVerifier, MockHash, + MockAddress, MockBlob, MockBlock, MockBlockHeader, MockDaConfig, MockDaSpec, MockDaVerifier, + MockHash, }; pub use validity_condition::{MockValidityCond, MockValidityCondChecker}; pub use zk_vm::{MockCodeCommitment, MockProof, MockZkvm}; From fa589917301e9dfce1b72f942c51d2ba829fda5e Mon Sep 17 00:00:00 2001 From: Victor Lopes Date: Wed, 27 Sep 2023 22:29:04 +0200 Subject: [PATCH 22/34] fix: require `native` when applicable for all targets (#937) * fix: require `native` when applicable for all targets Prior to this commit, some checks of all-targets without the feature `native` would break. This commit introduces a fix for every workspace member to be consistent with the feature set. * change feature requirement to self-dev-dep * update deps * fix dupl celestia dependencies --- Cargo.lock | 3 +++ adapters/celestia/Cargo.toml | 1 + .../demo-prover/methods/guest-celestia/Cargo.lock | 13 +++++++++++-- examples/demo-prover/methods/guest-mock/Cargo.lock | 13 +++++++++++-- full-node/sov-sequencer/Cargo.toml | 2 +- full-node/sov-stf-runner/Cargo.toml | 2 +- .../module-implementations/sov-accounts/Cargo.toml | 1 + .../module-implementations/sov-bank/Cargo.toml | 1 + rollup-interface/src/state_machine/stf.rs | 2 +- rollup-interface/src/state_machine/stf/fuzzing.rs | 2 +- 10 files changed, 32 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d13c986c..f48f533dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7079,6 +7079,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "sov-accounts", "sov-modules-api", "sov-state", "tempfile", @@ -7116,6 +7117,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "sov-bank", "sov-modules-api", "sov-rollup-interface", "sov-state", @@ -7170,6 +7172,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.7", + "sov-celestia-adapter", "sov-rollup-interface", "sov-zk-cycle-macros", "tendermint", diff --git a/adapters/celestia/Cargo.toml b/adapters/celestia/Cargo.toml index ae2fb4e32..455311280 100644 --- a/adapters/celestia/Cargo.toml +++ b/adapters/celestia/Cargo.toml @@ -44,6 +44,7 @@ nmt-rs = { git = "https://github.com/Sovereign-Labs/nmt-rs.git", rev = "dd375884 [dev-dependencies] postcard = { version = "1", features = ["use-std"] } proptest = { version = "1.2" } +sov-celestia-adapter = { path = ".", features = ["native"] } wiremock = "0.5" diff --git a/examples/demo-prover/methods/guest-celestia/Cargo.lock b/examples/demo-prover/methods/guest-celestia/Cargo.lock index 414c154a8..e377f11e3 100644 --- a/examples/demo-prover/methods/guest-celestia/Cargo.lock +++ b/examples/demo-prover/methods/guest-celestia/Cargo.lock @@ -210,8 +210,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" -source = "git+https://github.com/rust-lang/cc-rs?rev=e5bbdfa#e5bbdfa1fa468c028cb38fee6c35a3cf2e5a2736" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -1842,3 +1846,8 @@ dependencies = [ "quote", "syn 2.0.37", ] + +[[patch.unused]] +name = "cc" +version = "1.0.79" +source = "git+https://github.com/rust-lang/cc-rs?rev=e5bbdfa#e5bbdfa1fa468c028cb38fee6c35a3cf2e5a2736" diff --git a/examples/demo-prover/methods/guest-mock/Cargo.lock b/examples/demo-prover/methods/guest-mock/Cargo.lock index 5e881876d..7ad1f92f1 100644 --- a/examples/demo-prover/methods/guest-mock/Cargo.lock +++ b/examples/demo-prover/methods/guest-mock/Cargo.lock @@ -174,8 +174,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" -source = "git+https://github.com/rust-lang/cc-rs?rev=e5bbdfa#e5bbdfa1fa468c028cb38fee6c35a3cf2e5a2736" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -1260,3 +1264,8 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[patch.unused]] +name = "cc" +version = "1.0.79" +source = "git+https://github.com/rust-lang/cc-rs?rev=e5bbdfa#e5bbdfa1fa468c028cb38fee6c35a3cf2e5a2736" diff --git a/full-node/sov-sequencer/Cargo.toml b/full-node/sov-sequencer/Cargo.toml index e810ba416..30ac53a30 100644 --- a/full-node/sov-sequencer/Cargo.toml +++ b/full-node/sov-sequencer/Cargo.toml @@ -24,5 +24,5 @@ sov-rollup-interface = { path = "../../rollup-interface", version = "0.2" } [dev-dependencies] async-trait = { workspace = true } -sov-rollup-interface = { path = "../../rollup-interface", features = ["mocks"] } +sov-rollup-interface = { path = "../../rollup-interface", features = ["mocks", "native"] } tokio = { workspace = true } diff --git a/full-node/sov-stf-runner/Cargo.toml b/full-node/sov-stf-runner/Cargo.toml index dc4be096b..375d52cd1 100644 --- a/full-node/sov-stf-runner/Cargo.toml +++ b/full-node/sov-stf-runner/Cargo.toml @@ -28,7 +28,6 @@ sov-state = { path = "../../module-system/sov-state", version = "0.2" } sov-modules-api = { path = "../../module-system/sov-modules-api", version = "0.2" } [dev-dependencies] -sov-celestia-adapter = { path = "../../adapters/celestia", version = "0.2" } tempfile = { workspace = true } rand = { workspace = true } @@ -37,6 +36,7 @@ sov-bank = { path = "../../module-system/module-implementations/sov-bank", featu sov-modules-stf-template = { path = "../../module-system/sov-modules-stf-template", features = ["native"] } sov-value-setter = { path = "../../module-system/module-implementations/examples/sov-value-setter", features = ["native"] } sov-accounts = { path = "../../module-system/module-implementations/sov-accounts", features = ["native"] } +sov-celestia-adapter = { path = "../../adapters/celestia", features = ["native"] } [features] default = [] diff --git a/module-system/module-implementations/sov-accounts/Cargo.toml b/module-system/module-implementations/sov-accounts/Cargo.toml index 1953e02a0..35ab123d6 100644 --- a/module-system/module-implementations/sov-accounts/Cargo.toml +++ b/module-system/module-implementations/sov-accounts/Cargo.toml @@ -27,6 +27,7 @@ sov-state = { path = "../../sov-state", version = "0.2" } [dev-dependencies] +sov-accounts = { path = ".", features = ["native"] } tempfile = { workspace = true } [features] diff --git a/module-system/module-implementations/sov-bank/Cargo.toml b/module-system/module-implementations/sov-bank/Cargo.toml index 3e8c272c4..03906f897 100644 --- a/module-system/module-implementations/sov-bank/Cargo.toml +++ b/module-system/module-implementations/sov-bank/Cargo.toml @@ -27,6 +27,7 @@ sov-rollup-interface = { path = "../../../rollup-interface", version = "0.2" } [dev-dependencies] +sov-bank = { path = ".", features = ["native"] } tempfile = { workspace = true } [features] diff --git a/rollup-interface/src/state_machine/stf.rs b/rollup-interface/src/state_machine/stf.rs index 1162cdf15..3ab0b5508 100644 --- a/rollup-interface/src/state_machine/stf.rs +++ b/rollup-interface/src/state_machine/stf.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use crate::da::DaSpec; use crate::zk::{ValidityCondition, Zkvm}; -#[cfg(any(test, feature = "fuzzing"))] +#[cfg(any(all(test, feature = "sha2"), feature = "fuzzing"))] pub mod fuzzing; /// The configuration of a full node of the rollup which creates zk proofs. diff --git a/rollup-interface/src/state_machine/stf/fuzzing.rs b/rollup-interface/src/state_machine/stf/fuzzing.rs index c9b26f19f..96199f6b2 100644 --- a/rollup-interface/src/state_machine/stf/fuzzing.rs +++ b/rollup-interface/src/state_machine/stf/fuzzing.rs @@ -16,7 +16,7 @@ pub trait FuzzHasher { /// The default hasher to use for fuzzing fn default_fuzz_hasher() -> Box { - Box::new(::sha2::Sha256::new()) + Box::new(sha2::Sha256::new()) } impl + Clone> FuzzHasher for T { From ce4b10540bedb22ec21ff69b89ad3c71cc88b331 Mon Sep 17 00:00:00 2001 From: Blazej Kolad Date: Thu, 28 Sep 2023 09:43:34 +0200 Subject: [PATCH 23/34] Remove default trait bound from Module (#941) --- module-system/sov-modules-api/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module-system/sov-modules-api/src/lib.rs b/module-system/sov-modules-api/src/lib.rs index d0dcc6f1f..b5e73d056 100644 --- a/module-system/sov-modules-api/src/lib.rs +++ b/module-system/sov-modules-api/src/lib.rs @@ -276,7 +276,7 @@ where /// All the methods have a default implementation that can't be invoked (because they take `NonInstantiable` parameter). /// This allows developers to override only some of the methods in their implementation and safely ignore the others. -pub trait Module: Default { +pub trait Module { /// Execution context. type Context: Context; From 1e0c87c54e2e459a784bcdabf966bf3dd9d41366 Mon Sep 17 00:00:00 2001 From: Blazej Kolad Date: Thu, 28 Sep 2023 11:14:33 +0200 Subject: [PATCH 24/34] Remove demo-rollup-local job from CI (#943) --- .github/workflows/rust.yml | 62 -------------------------------------- 1 file changed, 62 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1b90e7b04..832e55097 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -137,68 +137,6 @@ jobs: # separately. # TODO: https://github.com/nextest-rs/nextest/issues/16 - run: cargo test --workspace --doc --all-features - demo-rollup-local: - # `test` has already built dependencies, so we can share - # caches (the profile is `dev` in both cases). - needs: test - runs-on: buildjet-8vcpu-ubuntu-2204 - steps: - - uses: actions/checkout@v3 - - uses: rui314/setup-mold@v1 - - name: Install Rust - run: rustup show - - name: Install cargo-risc0 # Risc0 v0.17 and higher require a cargo extension to build the guest code - run: cargo install cargo-risczero - - name: Install risc0-zkvm toolchain # Use the risc0 cargo extension to install the risc0 std library for the current toolchain - run: cargo risczero install - - uses: Swatinem/rust-cache@v2 - with: - cache-provider: "buildjet" - shared-key: cargo-build - save-if: ${{ github.ref == 'refs/heads/nightly' }} - - name: start celestia local - working-directory: ./examples/demo-rollup - run: make start - - name: start sovereign demo-rollup - working-directory: ./examples/demo-rollup - run: | - cargo build - ../../target/debug/sov-demo-rollup & - - name: wait for service to be up - run: | - SECONDS=0 - while ((SECONDS <= 1200)) - 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; then - echo "demo-rollup is up" - exit 0 - fi - echo "Not up yet, sleeping for 3 seconds..." - sleep 3 - SECONDS=$((SECONDS+3)) - done - echo "demo-rollup took too long to start; exiting" - exit 1 - - name: submit rollup transaction - working-directory: ./examples/demo-rollup - run: make test-create-token - - name: check token supply - # simple grep check on RPC to verify if the curl output contains "1000" which is the supply of token - could use jq and parse, but considering this won't change, it seems like a simple check to get it out quick - # if we want more complex parsing in the future and validation, we can switch to jq or other tools - run: | - 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":["sov1zdwj8thgev2u3yyrrlekmvtsz4av4tp3m7dm5mx5peejnesga27svq9m72"],"id":1}' http://127.0.0.1:12345 | grep -q 1000; then - echo "demo-rollup test succeeded" - exit 0 - fi - echo "Not up yet, sleeping for 3 seconds..." - sleep 3 - SECONDS=$((SECONDS+3)) - done - echo "demo-rollup took too long to process transaction; exiting" - exit 1 coverage: name: coverage runs-on: buildjet-8vcpu-ubuntu-2204 From 1d8ac46e06779926d490772d9e4adbb235fe2c75 Mon Sep 17 00:00:00 2001 From: Nikolai Golub Date: Thu, 28 Sep 2023 18:29:53 +0200 Subject: [PATCH 25/34] Delta's go first, remove `get` from StateCheckpoint (#953) --- .../sov-modules-api/src/state/scratchpad.rs | 221 +++++++++--------- 1 file changed, 108 insertions(+), 113 deletions(-) diff --git a/module-system/sov-modules-api/src/state/scratchpad.rs b/module-system/sov-modules-api/src/state/scratchpad.rs index 0f24ee058..11c32f281 100644 --- a/module-system/sov-modules-api/src/state/scratchpad.rs +++ b/module-system/sov-modules-api/src/state/scratchpad.rs @@ -60,6 +60,53 @@ impl StateReaderAndWriter for Delta { type RevertableWrites = HashMap>; +struct AccessoryDelta { + // This inner storage is never accessed inside the zkVM because reads are + // not allowed, so it can result as dead code. + #[allow(dead_code)] + storage: S, + writes: RevertableWrites, +} + +impl AccessoryDelta { + fn new(storage: S) -> Self { + Self { + storage, + writes: Default::default(), + } + } + + fn freeze(&mut self) -> OrderedReadsAndWrites { + let mut reads_and_writes = OrderedReadsAndWrites::default(); + let writes = std::mem::take(&mut self.writes); + + for write in writes { + reads_and_writes.ordered_writes.push((write.0, write.1)); + } + + reads_and_writes + } +} + +impl StateReaderAndWriter for AccessoryDelta { + fn get(&mut self, key: &StorageKey) -> Option { + let cache_key = key.to_cache_key(); + if let Some(value) = self.writes.get(&cache_key) { + return value.clone().map(Into::into); + } + self.storage.get_accessory(key) + } + + fn set(&mut self, key: &StorageKey, value: StorageValue) { + self.writes + .insert(key.to_cache_key(), Some(value.into_cache_value())); + } + + fn delete(&mut self, key: &StorageKey) { + self.writes.insert(key.to_cache_key(), None); + } +} + /// This structure is responsible for storing the `read-write` set. /// /// A [`StateCheckpoint`] can be obtained from a [`WorkingSet`] in two ways: @@ -80,11 +127,6 @@ impl StateCheckpoint { } } - /// Fetches a value from the underlying storage. - pub fn get(&mut self, key: &StorageKey) -> Option { - self.delta.get(key) - } - /// Creates a new [`StateCheckpoint`] instance without any changes, backed /// by the given [`Storage`] and witness. pub fn with_witness( @@ -131,61 +173,82 @@ impl StateCheckpoint { } } -struct AccessoryDelta { - // This inner storage is never accessed inside the zkVM because reads are - // not allowed, so it can result as dead code. - #[allow(dead_code)] - storage: S, - writes: RevertableWrites, +/// This structure contains the read-write set and the events collected during the execution of a transaction. +/// There are two ways to convert it into a StateCheckpoint: +/// 1. By using the checkpoint() method, where all the changes are added to the underlying StateCheckpoint. +/// 2. By using the revert method, where the most recent changes are reverted and the previous `StateCheckpoint` is returned. +pub struct WorkingSet { + delta: RevertableWriter>, + accessory_delta: RevertableWriter>, + events: Vec, } -impl AccessoryDelta { - fn new(storage: S) -> Self { - Self { - storage, - writes: Default::default(), - } +impl WorkingSet { + /// Creates a new [`WorkingSet`] instance backed by the given [`Storage`]. + /// + /// The witness value is set to [`Default::default`]. Use + /// [`WorkingSet::with_witness`] to set a custom witness value. + pub fn new(inner: ::Storage) -> Self { + StateCheckpoint::new(inner).to_revertable() } - fn freeze(&mut self) -> OrderedReadsAndWrites { - let mut reads_and_writes = OrderedReadsAndWrites::default(); - let writes = std::mem::take(&mut self.writes); + /// Returns a handler for the accessory state (non-JMT state). + /// + /// You can use this method when calling getters and setters on accessory + /// state containers, like [`AccessoryStateMap`](crate::AccessoryStateMap). + pub fn accessory_state(&mut self) -> AccessoryWorkingSet { + AccessoryWorkingSet { ws: self } + } - for write in writes { - reads_and_writes.ordered_writes.push((write.0, write.1)); - } + /// Creates a new [`WorkingSet`] instance backed by the given [`Storage`] + /// and a custom witness value. + pub fn with_witness( + inner: ::Storage, + witness: <::Storage as Storage>::Witness, + ) -> Self { + StateCheckpoint::with_witness(inner, witness).to_revertable() + } - reads_and_writes + /// Turns this [`WorkingSet`] into a [`StateCheckpoint`], in preparation for + /// committing the changes to the underlying [`Storage`] via + /// [`StateCheckpoint::freeze`]. + pub fn checkpoint(self) -> StateCheckpoint { + StateCheckpoint { + delta: self.delta.commit(), + accessory_delta: self.accessory_delta.commit(), + } } -} -impl StateReaderAndWriter for AccessoryDelta { - fn get(&mut self, key: &StorageKey) -> Option { - let cache_key = key.to_cache_key(); - if let Some(value) = self.writes.get(&cache_key) { - return value.clone().map(Into::into); + /// Reverts the most recent changes to this [`WorkingSet`], returning a pristine + /// [`StateCheckpoint`] instance. + pub fn revert(self) -> StateCheckpoint { + StateCheckpoint { + delta: self.delta.revert(), + accessory_delta: self.accessory_delta.revert(), } - self.storage.get_accessory(key) } - fn set(&mut self, key: &StorageKey, value: StorageValue) { - self.writes - .insert(key.to_cache_key(), Some(value.into_cache_value())); + /// Adds an event to the working set. + pub fn add_event(&mut self, key: &str, value: &str) { + self.events.push(Event::new(key, value)); } - fn delete(&mut self, key: &StorageKey) { - self.writes.insert(key.to_cache_key(), None); + /// Extracts all events from this working set. + pub fn take_events(&mut self) -> Vec { + std::mem::take(&mut self.events) } -} -/// This structure contains the read-write set and the events collected during the execution of a transaction. -/// There are two ways to convert it into a StateCheckpoint: -/// 1. By using the checkpoint() method, where all the changes are added to the underlying StateCheckpoint. -/// 2. By using the revert method, where the most recent changes are reverted and the previous `StateCheckpoint` is returned. -pub struct WorkingSet { - delta: RevertableWriter>, - accessory_delta: RevertableWriter>, - events: Vec, + /// Returns an immutable slice of all events that have been previously + /// written to this working set. + pub fn events(&self) -> &[Event] { + &self.events + } + + /// Returns an immutable reference to the [`Storage`] instance backing this + /// working set. + pub fn backing(&self) -> &::Storage { + &self.delta.inner.inner + } } impl StateReaderAndWriter for WorkingSet { @@ -286,74 +349,6 @@ impl StateReaderAndWriter for RevertableWriter { } } -impl WorkingSet { - /// Creates a new [`WorkingSet`] instance backed by the given [`Storage`]. - /// - /// The witness value is set to [`Default::default`]. Use - /// [`WorkingSet::with_witness`] to set a custom witness value. - pub fn new(inner: ::Storage) -> Self { - StateCheckpoint::new(inner).to_revertable() - } - - /// Returns a handler for the accessory state (non-JMT state). - /// - /// You can use this method when calling getters and setters on accessory - /// state containers, like [`AccessoryStateMap`](crate::AccessoryStateMap). - pub fn accessory_state(&mut self) -> AccessoryWorkingSet { - AccessoryWorkingSet { ws: self } - } - - /// Creates a new [`WorkingSet`] instance backed by the given [`Storage`] - /// and a custom witness value. - pub fn with_witness( - inner: ::Storage, - witness: <::Storage as Storage>::Witness, - ) -> Self { - StateCheckpoint::with_witness(inner, witness).to_revertable() - } - - /// Turns this [`WorkingSet`] into a [`StateCheckpoint`], in preparation for - /// committing the changes to the underlying [`Storage`] via - /// [`StateCheckpoint::freeze`]. - pub fn checkpoint(self) -> StateCheckpoint { - StateCheckpoint { - delta: self.delta.commit(), - accessory_delta: self.accessory_delta.commit(), - } - } - - /// Reverts the most recent changes to this [`WorkingSet`], returning a pristine - /// [`StateCheckpoint`] instance. - pub fn revert(self) -> StateCheckpoint { - StateCheckpoint { - delta: self.delta.revert(), - accessory_delta: self.accessory_delta.revert(), - } - } - - /// Adds an event to the working set. - pub fn add_event(&mut self, key: &str, value: &str) { - self.events.push(Event::new(key, value)); - } - - /// Extracts all events from this working set. - pub fn take_events(&mut self) -> Vec { - std::mem::take(&mut self.events) - } - - /// Returns an immutable slice of all events that have been previously - /// written to this working set. - pub fn events(&self) -> &[Event] { - &self.events - } - - /// Returns an immutable reference to the [`Storage`] instance backing this - /// working set. - pub fn backing(&self) -> &::Storage { - &self.delta.inner.inner - } -} - pub(crate) trait StateReaderAndWriter { fn get(&mut self, key: &StorageKey) -> Option; From 13b822e37811311bbe9a386f48b0232f7c3379df Mon Sep 17 00:00:00 2001 From: Preston Evans <32944016+preston-evans98@users.noreply.github.com> Date: Thu, 28 Sep 2023 23:57:04 -0700 Subject: [PATCH 26/34] Make genesis config serializable (#956) * Make genesis config serializable * Evm config serde * Fix chain state integ test * fix vec-setter --- Cargo.lock | 1 + .../methods/guest-celestia/Cargo.lock | 7 +++++++ .../demo-prover/methods/guest-mock/Cargo.lock | 7 +++++++ examples/simple-nft-module/Cargo.toml | 5 ++--- examples/simple-nft-module/src/lib.rs | 2 ++ .../examples/sov-value-setter/Cargo.toml | 4 ++-- .../examples/sov-value-setter/src/lib.rs | 5 +---- .../examples/sov-vec-setter/Cargo.toml | 4 ++-- .../examples/sov-vec-setter/src/lib.rs | 2 ++ .../integration-tests/Cargo.toml | 1 + .../module-template/Cargo.toml | 4 ++-- .../module-template/src/lib.rs | 2 ++ .../sov-accounts/Cargo.toml | 4 ++-- .../sov-accounts/src/lib.rs | 3 ++- .../module-implementations/sov-bank/Cargo.toml | 4 ++-- .../module-implementations/sov-bank/src/lib.rs | 3 +++ .../sov-bank/src/token.rs | 7 ++++--- .../sov-chain-state/src/lib.rs | 1 + .../module-implementations/sov-evm/src/lib.rs | 4 ++-- .../sov-nft-module/Cargo.toml | 5 ++--- .../sov-nft-module/src/lib.rs | 2 ++ .../sov-prover-incentives/Cargo.toml | 4 ++-- .../sov-prover-incentives/src/lib.rs | 2 ++ .../sov-sequencer-registry/Cargo.toml | 3 +-- .../sov-sequencer-registry/src/lib.rs | 2 ++ module-system/sov-modules-api/Cargo.toml | 3 +-- .../sov-modules-api/src/default_signature.rs | 7 ++----- module-system/sov-modules-api/src/lib.rs | 17 +++++++++++------ .../sov-modules-macros/src/dispatch/genesis.rs | 1 + 29 files changed, 73 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f48f533dd..8d7dd54a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3548,6 +3548,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", + "serde", "sov-chain-state", "sov-data-generators", "sov-modules-api", diff --git a/examples/demo-prover/methods/guest-celestia/Cargo.lock b/examples/demo-prover/methods/guest-celestia/Cargo.lock index e377f11e3..611c388d7 100644 --- a/examples/demo-prover/methods/guest-celestia/Cargo.lock +++ b/examples/demo-prover/methods/guest-celestia/Cargo.lock @@ -384,6 +384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" dependencies = [ "pkcs8", + "serde", "signature", ] @@ -408,6 +409,7 @@ checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" dependencies = [ "curve25519-dalek", "ed25519", + "serde", "sha2 0.10.6", ] @@ -1274,6 +1276,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", + "serde", "sov-modules-api", "sov-state", "thiserror", @@ -1285,6 +1288,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", + "serde", "sov-modules-api", "sov-rollup-interface", "sov-state", @@ -1419,6 +1423,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", + "serde", "sov-modules-api", "sov-modules-macros", "sov-state", @@ -1459,6 +1464,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", + "serde", "sov-bank", "sov-modules-api", "sov-state", @@ -1500,6 +1506,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", + "serde", "sov-modules-api", "sov-state", "thiserror", diff --git a/examples/demo-prover/methods/guest-mock/Cargo.lock b/examples/demo-prover/methods/guest-mock/Cargo.lock index 7ad1f92f1..df398c1a3 100644 --- a/examples/demo-prover/methods/guest-mock/Cargo.lock +++ b/examples/demo-prover/methods/guest-mock/Cargo.lock @@ -309,6 +309,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" dependencies = [ + "serde", "signature", ] @@ -320,6 +321,7 @@ checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" dependencies = [ "curve25519-dalek", "ed25519", + "serde", "sha2", ] @@ -860,6 +862,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", + "serde", "sov-modules-api", "sov-state", "thiserror", @@ -871,6 +874,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", + "serde", "sov-modules-api", "sov-rollup-interface", "sov-state", @@ -978,6 +982,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", + "serde", "sov-modules-api", "sov-modules-macros", "sov-state", @@ -1019,6 +1024,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", + "serde", "sov-bank", "sov-modules-api", "sov-state", @@ -1060,6 +1066,7 @@ version = "0.2.0" dependencies = [ "anyhow", "borsh", + "serde", "sov-modules-api", "sov-state", "thiserror", diff --git a/examples/simple-nft-module/Cargo.toml b/examples/simple-nft-module/Cargo.toml index a4d6446c8..651b9b034 100644 --- a/examples/simple-nft-module/Cargo.toml +++ b/examples/simple-nft-module/Cargo.toml @@ -12,7 +12,7 @@ publish = false [dependencies] anyhow = { workspace = true } borsh = { workspace = true, features = ["rc"] } -serde = { workspace = true, optional = true } +serde = { workspace = true } sov-modules-api = { path = "../../module-system/sov-modules-api" } sov-state = { path = "../../module-system/sov-state" } @@ -27,6 +27,5 @@ simple-nft-module = { version = "*", features = ["native"], path = "." } [features] default = [] -serde = ["dep:serde"] -native = ["serde", "sov-state/native", "sov-modules-api/native", "jsonrpsee"] +native = ["sov-state/native", "sov-modules-api/native", "jsonrpsee"] test = ["native"] diff --git a/examples/simple-nft-module/src/lib.rs b/examples/simple-nft-module/src/lib.rs index 70ea4b156..16bce9c15 100644 --- a/examples/simple-nft-module/src/lib.rs +++ b/examples/simple-nft-module/src/lib.rs @@ -8,6 +8,7 @@ mod genesis; mod query; #[cfg(feature = "native")] pub use query::*; +use serde::{Deserialize, Serialize}; use sov_modules_api::{CallResponse, Context, Error, Module, ModuleInfo, WorkingSet}; #[derive(ModuleInfo, Clone)] @@ -29,6 +30,7 @@ pub struct NonFungibleToken { /// Config for the NonFungibleToken module. /// Sets admin and existing owners. +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct NonFungibleTokenConfig { /// Admin of the NonFungibleToken module. pub admin: C::Address, diff --git a/module-system/module-implementations/examples/sov-value-setter/Cargo.toml b/module-system/module-implementations/examples/sov-value-setter/Cargo.toml index 392fbcf4a..0b11abf11 100644 --- a/module-system/module-implementations/examples/sov-value-setter/Cargo.toml +++ b/module-system/module-implementations/examples/sov-value-setter/Cargo.toml @@ -22,7 +22,7 @@ anyhow = { workspace = true } sov-modules-api = { path = "../../../sov-modules-api" } sov-state = { path = "../../../sov-state" } schemars = { workspace = true, optional = true } -serde = { workspace = true, optional = true } +serde = { workspace = true } serde_json = { workspace = true, optional = true } thiserror = { workspace = true } borsh = { workspace = true, features = ["rc"] } @@ -31,4 +31,4 @@ clap = { workspace = true, optional = true } [features] default = [] -native = ["serde", "serde_json", "jsonrpsee", "schemars", "clap", "sov-modules-api/native", "sov-state/native"] +native = ["serde_json", "jsonrpsee", "schemars", "clap", "sov-modules-api/native", "sov-state/native"] diff --git a/module-system/module-implementations/examples/sov-value-setter/src/lib.rs b/module-system/module-implementations/examples/sov-value-setter/src/lib.rs index a49519240..a66addb13 100644 --- a/module-system/module-implementations/examples/sov-value-setter/src/lib.rs +++ b/module-system/module-implementations/examples/sov-value-setter/src/lib.rs @@ -15,10 +15,7 @@ pub use query::*; use sov_modules_api::{Error, ModuleInfo, WorkingSet}; /// Initial configuration for sov-value-setter module. -#[cfg_attr( - feature = "native", - derive(serde::Serialize, serde::Deserialize, Debug, PartialEq) -)] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)] pub struct ValueSetterConfig { /// Admin of the module. pub admin: C::Address, diff --git a/module-system/module-implementations/examples/sov-vec-setter/Cargo.toml b/module-system/module-implementations/examples/sov-vec-setter/Cargo.toml index 0f3d77719..5adfa4f79 100644 --- a/module-system/module-implementations/examples/sov-vec-setter/Cargo.toml +++ b/module-system/module-implementations/examples/sov-vec-setter/Cargo.toml @@ -22,7 +22,7 @@ sov-modules-api = { path = "../../../sov-modules-api", default-features = false, sov-state = { path = "../../../sov-state", default-features = false } sov-rollup-interface = { path = "../../../../rollup-interface" } schemars = { workspace = true, optional = true } -serde = { workspace = true, optional = true } +serde = { workspace = true } serde_json = { workspace = true, optional = true } thiserror = { workspace = true } borsh = { workspace = true, features = ["rc"] } @@ -31,4 +31,4 @@ clap = { workspace = true, optional = true } [features] default = ["native"] -native = ["serde", "serde_json", "jsonrpsee", "schemars", "clap", "sov-modules-api/native"] +native = ["serde_json", "jsonrpsee", "schemars", "clap", "sov-modules-api/native"] diff --git a/module-system/module-implementations/examples/sov-vec-setter/src/lib.rs b/module-system/module-implementations/examples/sov-vec-setter/src/lib.rs index 7a74628eb..a444dffa4 100644 --- a/module-system/module-implementations/examples/sov-vec-setter/src/lib.rs +++ b/module-system/module-implementations/examples/sov-vec-setter/src/lib.rs @@ -9,9 +9,11 @@ mod query; pub use call::CallMessage; #[cfg(feature = "native")] pub use query::*; +use serde::{Deserialize, Serialize}; use sov_modules_api::{Error, ModuleInfo, WorkingSet}; /// Initial configuration for sov-vec-setter module. +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct VecSetterConfig { /// Admin of the module. pub admin: C::Address, diff --git a/module-system/module-implementations/integration-tests/Cargo.toml b/module-system/module-implementations/integration-tests/Cargo.toml index 269e24e59..27fc047d3 100644 --- a/module-system/module-implementations/integration-tests/Cargo.toml +++ b/module-system/module-implementations/integration-tests/Cargo.toml @@ -15,6 +15,7 @@ resolver = "2" anyhow = { workspace = true } borsh = { workspace = true, features = ["rc"] } tempfile = { workspace = true } +serde = { workspace = true } sov-modules-api = { path = "../../sov-modules-api", features = ["native"] } sov-state = { path = "../../sov-state", features = ["native"] } diff --git a/module-system/module-implementations/module-template/Cargo.toml b/module-system/module-implementations/module-template/Cargo.toml index c6ca48549..38974ba2d 100644 --- a/module-system/module-implementations/module-template/Cargo.toml +++ b/module-system/module-implementations/module-template/Cargo.toml @@ -16,7 +16,7 @@ anyhow = { workspace = true } borsh = { workspace = true, features = ["rc"] } thiserror = { workspace = true } schemars = { workspace = true, optional = true } -serde = { workspace = true, optional = true } +serde = { workspace = true } serde_json = { workspace = true, optional = true } sov-bank = { path = "../sov-bank" } @@ -31,4 +31,4 @@ module-template = { path = ".", version = "*", features = ["native"] } [features] default = [] -native = ["serde", "serde_json", "schemars", "sov-modules-api/native"] +native = ["serde_json", "schemars", "sov-modules-api/native"] diff --git a/module-system/module-implementations/module-template/src/lib.rs b/module-system/module-implementations/module-template/src/lib.rs index 1daf9e8cf..00c63a402 100644 --- a/module-system/module-implementations/module-template/src/lib.rs +++ b/module-system/module-implementations/module-template/src/lib.rs @@ -5,8 +5,10 @@ mod query; pub use call::CallMessage; #[cfg(feature = "native")] pub use query::*; +use serde::{Deserialize, Serialize}; use sov_modules_api::{Error, ModuleInfo, WorkingSet}; +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ExampleModuleConfig {} /// A new module: diff --git a/module-system/module-implementations/sov-accounts/Cargo.toml b/module-system/module-implementations/sov-accounts/Cargo.toml index 35ab123d6..fb0aac286 100644 --- a/module-system/module-implementations/sov-accounts/Cargo.toml +++ b/module-system/module-implementations/sov-accounts/Cargo.toml @@ -16,7 +16,7 @@ anyhow = { workspace = true } arbitrary = { workspace = true, optional = true } borsh = { workspace = true, features = ["rc"] } schemars = { workspace = true, optional = true } -serde = { workspace = true, optional = true } +serde = { workspace = true } serde_json = { workspace = true, optional = true } thiserror = { workspace = true } clap = { workspace = true, optional = true } @@ -33,4 +33,4 @@ tempfile = { workspace = true } [features] default = [] arbitrary = ["dep:arbitrary", "sov-state/arbitrary", "sov-modules-api/arbitrary"] -native = ["serde", "serde_json", "jsonrpsee", "schemars", "clap", "sov-state/native", "sov-modules-api/native"] +native = ["serde_json", "jsonrpsee", "schemars", "clap", "sov-state/native", "sov-modules-api/native"] diff --git a/module-system/module-implementations/sov-accounts/src/lib.rs b/module-system/module-implementations/sov-accounts/src/lib.rs index 9f9c987b3..922fce9f4 100644 --- a/module-system/module-implementations/sov-accounts/src/lib.rs +++ b/module-system/module-implementations/sov-accounts/src/lib.rs @@ -15,7 +15,8 @@ pub use call::{CallMessage, UPDATE_ACCOUNT_MSG}; use sov_modules_api::{Context, Error, ModuleInfo, WorkingSet}; /// Initial configuration for sov-accounts module. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[serde(bound = "C::PublicKey: serde::Serialize + serde::de::DeserializeOwned")] pub struct AccountConfig { /// Public keys to initialize the rollup. pub pub_keys: Vec, diff --git a/module-system/module-implementations/sov-bank/Cargo.toml b/module-system/module-implementations/sov-bank/Cargo.toml index 03906f897..18a95f343 100644 --- a/module-system/module-implementations/sov-bank/Cargo.toml +++ b/module-system/module-implementations/sov-bank/Cargo.toml @@ -17,7 +17,7 @@ borsh = { workspace = true, features = ["rc"] } clap = { workspace = true, optional = true, features = ["derive"] } jsonrpsee = { workspace = true, features = ["macros", "client-core", "server"], optional = true } schemars = { workspace = true, optional = true } -serde = { workspace = true, optional = true } +serde = { workspace = true } serde_json = { workspace = true, optional = true } thiserror = { workspace = true } @@ -32,5 +32,5 @@ tempfile = { workspace = true } [features] default = [] -native = ["serde", "serde_json", "jsonrpsee", "clap", "schemars", "sov-state/native", "sov-modules-api/native", ] +native = ["serde_json", "jsonrpsee", "clap", "schemars", "sov-state/native", "sov-modules-api/native", ] cli = ["native"] diff --git a/module-system/module-implementations/sov-bank/src/lib.rs b/module-system/module-implementations/sov-bank/src/lib.rs index cd664fd3c..e8d3425e4 100644 --- a/module-system/module-implementations/sov-bank/src/lib.rs +++ b/module-system/module-implementations/sov-bank/src/lib.rs @@ -12,6 +12,7 @@ pub mod utils; /// Specifies the call methods using in that module. pub use call::CallMessage; +use serde::{Deserialize, Serialize}; use sov_modules_api::{CallResponse, Error, ModuleInfo, WorkingSet}; use token::Token; /// Specifies an interface to interact with tokens. @@ -21,6 +22,7 @@ pub use utils::{get_genesis_token_address, get_token_address}; /// [`TokenConfig`] specifies a configuration used when generating a token for the bank /// module. +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct TokenConfig { /// The name of the token. pub token_name: String, @@ -33,6 +35,7 @@ pub struct TokenConfig { } /// Initial configuration for sov-bank module. +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct BankConfig { /// A list of configurations for the initial tokens. pub tokens: Vec>, diff --git a/module-system/module-implementations/sov-bank/src/token.rs b/module-system/module-implementations/sov-bank/src/token.rs index c552d1b4f..8ad62943a 100644 --- a/module-system/module-implementations/sov-bank/src/token.rs +++ b/module-system/module-implementations/sov-bank/src/token.rs @@ -6,6 +6,7 @@ use std::fmt::Formatter; use std::num::ParseIntError; use anyhow::{bail, Context, Result}; +use serde::{Deserialize, Serialize}; use sov_modules_api::WorkingSet; use sov_state::Prefix; #[cfg(feature = "native")] @@ -21,13 +22,13 @@ pub type Amount = u64; /// (type [`sov_modules_api::Spec::Address`]). #[cfg_attr( feature = "native", - derive(serde::Serialize), - derive(serde::Deserialize), derive(clap::Parser), derive(schemars::JsonSchema), schemars(bound = "C::Address: ::schemars::JsonSchema", rename = "Coins") )] -#[derive(borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq, Clone)] +#[derive( + borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq, Clone, Serialize, Deserialize, +)] pub struct Coins { /// An `amount` of coins stored. pub amount: Amount, diff --git a/module-system/module-implementations/sov-chain-state/src/lib.rs b/module-system/module-implementations/sov-chain-state/src/lib.rs index f7bcce225..4de0fbe14 100644 --- a/module-system/module-implementations/sov-chain-state/src/lib.rs +++ b/module-system/module-implementations/sov-chain-state/src/lib.rs @@ -147,6 +147,7 @@ pub struct ChainState } /// Initial configuration of the chain state +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ChainStateConfig { /// Initial slot height pub initial_slot_height: TransitionHeight, diff --git a/module-system/module-implementations/sov-evm/src/lib.rs b/module-system/module-implementations/sov-evm/src/lib.rs index ea65e39be..b256b2ae5 100644 --- a/module-system/module-implementations/sov-evm/src/lib.rs +++ b/module-system/module-implementations/sov-evm/src/lib.rs @@ -48,7 +48,7 @@ mod experimental { }; /// Evm account. - #[derive(Clone, Debug)] + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct AccountData { /// Account address. pub address: Address, @@ -75,7 +75,7 @@ mod experimental { } /// Genesis configuration. - #[derive(Clone, Debug)] + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct EvmConfig { /// Genesis accounts. pub data: Vec, diff --git a/module-system/module-implementations/sov-nft-module/Cargo.toml b/module-system/module-implementations/sov-nft-module/Cargo.toml index 5732923fb..38b0d28b9 100644 --- a/module-system/module-implementations/sov-nft-module/Cargo.toml +++ b/module-system/module-implementations/sov-nft-module/Cargo.toml @@ -12,7 +12,7 @@ publish = false [dependencies] anyhow = { workspace = true } borsh = { workspace = true, features = ["rc"] } -serde = { workspace = true, optional = true } +serde = { workspace = true } schemars = { workspace = true, optional = true } serde_json = { workspace = true, optional = true } jsonrpsee = { workspace = true, features = ["macros", "client-core", "server"], optional = true } @@ -34,6 +34,5 @@ sov-nft-module = { version = "*", features = ["native"], path = "." } [features] default = [] offchain = ["postgres","tokio","tracing"] -serde = ["dep:serde"] -native = ["serde", "serde_json", "jsonrpsee", "schemars", "sov-state/native", "sov-modules-api/native", ] +native = ["serde_json", "jsonrpsee", "schemars", "sov-state/native", "sov-modules-api/native", ] test = ["native"] diff --git a/module-system/module-implementations/sov-nft-module/src/lib.rs b/module-system/module-implementations/sov-nft-module/src/lib.rs index 7096c2ffe..3ca20dc3f 100644 --- a/module-system/module-implementations/sov-nft-module/src/lib.rs +++ b/module-system/module-implementations/sov-nft-module/src/lib.rs @@ -14,6 +14,7 @@ use nft::*; mod query; #[cfg(feature = "native")] pub use query::*; +use serde::{Deserialize, Serialize}; use sov_modules_api::{CallResponse, Context, Error, Module, ModuleInfo, StateMap, WorkingSet}; mod offchain; /// Utility functions. @@ -39,6 +40,7 @@ pub struct NonFungibleToken { /// Config for the NonFungibleToken module. /// Sets admin and existing owners. +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct NonFungibleTokenConfig {} impl Module for NonFungibleToken { diff --git a/module-system/module-implementations/sov-prover-incentives/Cargo.toml b/module-system/module-implementations/sov-prover-incentives/Cargo.toml index 11255e5fe..381d4aa60 100644 --- a/module-system/module-implementations/sov-prover-incentives/Cargo.toml +++ b/module-system/module-implementations/sov-prover-incentives/Cargo.toml @@ -22,7 +22,7 @@ anyhow = { workspace = true } borsh = { workspace = true, features = ["rc"] } bincode = { workspace = true } schemars = { workspace = true, optional = true } -serde = { workspace = true, optional = true } +serde = { workspace = true } serde_json = { workspace = true, optional = true } sov-bank = { path = "../sov-bank", version = "0.2" } @@ -32,4 +32,4 @@ sov-state = { path = "../../sov-state", version = "0.2" } [features] default = [] -native = ["serde", "serde_json", "schemars", "sov-state/native", "sov-modules-api/native"] +native = ["serde_json", "schemars", "sov-state/native", "sov-modules-api/native"] diff --git a/module-system/module-implementations/sov-prover-incentives/src/lib.rs b/module-system/module-implementations/sov-prover-incentives/src/lib.rs index 2fa93e06e..44cd7414b 100644 --- a/module-system/module-implementations/sov-prover-incentives/src/lib.rs +++ b/module-system/module-implementations/sov-prover-incentives/src/lib.rs @@ -14,6 +14,7 @@ pub use call::CallMessage; /// The response type used by RPC queries. #[cfg(feature = "native")] pub use query::*; +use serde::{Deserialize, Serialize}; use sov_modules_api::{Context, Error, ModuleInfo, WorkingSet, Zkvm}; use sov_state::codec::BcsCodec; @@ -21,6 +22,7 @@ use sov_state::codec::BcsCodec; /// address of the bonding token, the minimum bond, the commitment to /// the allowed verifier method and a set of initial provers with their /// bonding amount. +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProverIncentivesConfig { /// The address of the token to be used for bonding. bonding_token_address: C::Address, diff --git a/module-system/module-implementations/sov-sequencer-registry/Cargo.toml b/module-system/module-implementations/sov-sequencer-registry/Cargo.toml index ff677b951..c073ec2fa 100644 --- a/module-system/module-implementations/sov-sequencer-registry/Cargo.toml +++ b/module-system/module-implementations/sov-sequencer-registry/Cargo.toml @@ -24,7 +24,7 @@ sov-bank = { path = "../sov-bank", version = "0.2" } sov-modules-api = { path = "../../sov-modules-api", version = "0.2" } sov-state = { path = "../../sov-state", version = "0.2" } schemars = { workspace = true, optional = true } -serde = { workspace = true, optional = true } +serde = { workspace = true } serde_json = { workspace = true, optional = true } borsh = { workspace = true, features = ["rc"] } jsonrpsee = { workspace = true, features = ["macros", "client-core", "server"], optional = true } @@ -37,7 +37,6 @@ sov-zk-cycle-utils = { path = "../../../utils/zk-cycle-utils", version = "0.2", bench = ["sov-zk-cycle-macros/bench", "risc0-zkvm", "risc0-zkvm-platform", "sov-zk-cycle-utils"] default = [] native = [ - "serde", "serde_json", "jsonrpsee", "schemars", diff --git a/module-system/module-implementations/sov-sequencer-registry/src/lib.rs b/module-system/module-implementations/sov-sequencer-registry/src/lib.rs index 7df00cb9e..a74447e31 100644 --- a/module-system/module-implementations/sov-sequencer-registry/src/lib.rs +++ b/module-system/module-implementations/sov-sequencer-registry/src/lib.rs @@ -16,6 +16,7 @@ mod query; pub use call::CallMessage; #[cfg(feature = "native")] pub use query::*; +use serde::{Deserialize, Serialize}; use sov_modules_api::{CallResponse, Error, ModuleInfo, StateMap, StateValue, WorkingSet}; use sov_state::codec::BcsCodec; @@ -25,6 +26,7 @@ use sov_state::codec::BcsCodec; /// [`Module::genesis`](sov_modules_api::Module::genesis). /// // TODO: Should we allow multiple sequencers in genesis? +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SequencerConfig { /// The rollup address of the sequencer. pub seq_rollup_address: C::Address, diff --git a/module-system/sov-modules-api/Cargo.toml b/module-system/sov-modules-api/Cargo.toml index 682dd08d4..ab42a5c5f 100644 --- a/module-system/sov-modules-api/Cargo.toml +++ b/module-system/sov-modules-api/Cargo.toml @@ -32,7 +32,7 @@ hex = { workspace = true, optional = true } clap = { workspace = true, optional = true } schemars = { workspace = true, optional = true, features = [] } -ed25519-dalek = { version = "2.0.0", default-features = false } +ed25519-dalek = { version = "2.0.0", default-features = false, features = ["serde"] } rand = { version = "0.8", optional = true } sov-zk-cycle-macros = { path = "../../utils/zk-cycle-macros", version = "0.2", optional = true } @@ -55,7 +55,6 @@ native = [ "hex", "schemars", "ed25519-dalek/default", - "ed25519-dalek/serde", "ed25519-dalek/rand_core", "clap", "jsonrpsee", diff --git a/module-system/sov-modules-api/src/default_signature.rs b/module-system/sov-modules-api/src/default_signature.rs index ab7be5a2b..7745b7451 100644 --- a/module-system/sov-modules-api/src/default_signature.rs +++ b/module-system/sov-modules-api/src/default_signature.rs @@ -166,11 +166,8 @@ pub mod private_key { } } -#[cfg_attr( - feature = "native", - derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema) -)] -#[derive(PartialEq, Eq, Clone, Debug)] +#[cfg_attr(feature = "native", derive(schemars::JsonSchema))] +#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct DefaultPublicKey { #[cfg_attr( feature = "native", diff --git a/module-system/sov-modules-api/src/lib.rs b/module-system/sov-modules-api/src/lib.rs index b5e73d056..7ef5293b8 100644 --- a/module-system/sov-modules-api/src/lib.rs +++ b/module-system/sov-modules-api/src/lib.rs @@ -153,7 +153,16 @@ pub enum NonInstantiable {} /// PublicKey used in the Module System. pub trait PublicKey: - borsh::BorshDeserialize + borsh::BorshSerialize + Eq + Hash + Clone + Debug + Send + Sync + borsh::BorshDeserialize + + borsh::BorshSerialize + + Eq + + Hash + + Clone + + Debug + + Send + + Sync + + Serialize + + for<'a> Deserialize<'a> { fn to_address(&self) -> A; } @@ -210,11 +219,7 @@ pub trait Spec { /// The public key used for digital signatures #[cfg(feature = "native")] - type PublicKey: PublicKey - + Serialize - + for<'a> Deserialize<'a> - + ::schemars::JsonSchema - + FromStr; + type PublicKey: PublicKey + ::schemars::JsonSchema + FromStr; #[cfg(not(feature = "native"))] type PublicKey: PublicKey; diff --git a/module-system/sov-modules-macros/src/dispatch/genesis.rs b/module-system/sov-modules-macros/src/dispatch/genesis.rs index 2530ee86d..b41306336 100644 --- a/module-system/sov-modules-macros/src/dispatch/genesis.rs +++ b/module-system/sov-modules-macros/src/dispatch/genesis.rs @@ -101,6 +101,7 @@ impl GenesisMacro { quote::quote! { #[doc = "Initial configuration for the rollup."] + #[derive(::serde::Deserialize, ::serde::Serialize)] pub struct GenesisConfig #impl_generics #where_clause{ #(#[doc = "Module configuration"] pub #fields)* } From 9ae8c3cb7a9ef8efa8d91e96d84dc259c2bd7c81 Mon Sep 17 00:00:00 2001 From: Blazej Kolad Date: Fri, 29 Sep 2023 09:28:30 +0200 Subject: [PATCH 27/34] Introduce: `PublicKeyHex` in `sov-modules-api` (#954) * Add PublicKeyHex * PubKeyHex impl * Update DefaultPublicKey::from_str * Add tests * Remove println * Add doc --- .../methods/guest-celestia/Cargo.lock | 1 + .../demo-prover/methods/guest-mock/Cargo.lock | 1 + module-system/sov-modules-api/Cargo.toml | 3 +- .../sov-modules-api/src/default_signature.rs | 11 +- module-system/sov-modules-api/src/lib.rs | 1 + .../sov-modules-api/src/pub_key_hex.rs | 124 ++++++++++++++++++ 6 files changed, 130 insertions(+), 11 deletions(-) create mode 100644 module-system/sov-modules-api/src/pub_key_hex.rs diff --git a/examples/demo-prover/methods/guest-celestia/Cargo.lock b/examples/demo-prover/methods/guest-celestia/Cargo.lock index 611c388d7..da5807d78 100644 --- a/examples/demo-prover/methods/guest-celestia/Cargo.lock +++ b/examples/demo-prover/methods/guest-celestia/Cargo.lock @@ -1378,6 +1378,7 @@ dependencies = [ "borsh", "derive_more", "ed25519-dalek", + "hex", "jmt", "serde", "sha2 0.10.6", diff --git a/examples/demo-prover/methods/guest-mock/Cargo.lock b/examples/demo-prover/methods/guest-mock/Cargo.lock index df398c1a3..db5de049c 100644 --- a/examples/demo-prover/methods/guest-mock/Cargo.lock +++ b/examples/demo-prover/methods/guest-mock/Cargo.lock @@ -937,6 +937,7 @@ dependencies = [ "borsh", "derive_more", "ed25519-dalek", + "hex", "jmt", "serde", "sha2", diff --git a/module-system/sov-modules-api/Cargo.toml b/module-system/sov-modules-api/Cargo.toml index ab42a5c5f..6e2e256e9 100644 --- a/module-system/sov-modules-api/Cargo.toml +++ b/module-system/sov-modules-api/Cargo.toml @@ -28,7 +28,7 @@ bech32 = { workspace = true } derive_more = { workspace = true } jmt = { workspace = true } serde_json = { workspace = true, optional = true } -hex = { workspace = true, optional = true } +hex = { workspace = true } clap = { workspace = true, optional = true } schemars = { workspace = true, optional = true, features = [] } @@ -52,7 +52,6 @@ default = ["macros"] native = [ "serde_json", "rand", - "hex", "schemars", "ed25519-dalek/default", "ed25519-dalek/rand_core", diff --git a/module-system/sov-modules-api/src/default_signature.rs b/module-system/sov-modules-api/src/default_signature.rs index 7745b7451..48c886f49 100644 --- a/module-system/sov-modules-api/src/default_signature.rs +++ b/module-system/sov-modules-api/src/default_signature.rs @@ -254,15 +254,8 @@ impl FromStr for DefaultPublicKey { type Err = anyhow::Error; fn from_str(s: &str) -> Result { - let bytes = hex::decode(s)?; - - let bytes: [u8; PUBLIC_KEY_LENGTH] = bytes - .try_into() - .map_err(|_| anyhow::anyhow!("Invalid public key size"))?; - - let pub_key = DalekPublicKey::from_bytes(&bytes) - .map_err(|_| anyhow::anyhow!("Invalid public key"))?; - Ok(DefaultPublicKey { pub_key }) + let pk_hex = crate::pub_key_hex::PublicKeyHex::try_from(s)?; + pk_hex.try_into() } } diff --git a/module-system/sov-modules-api/src/lib.rs b/module-system/sov-modules-api/src/lib.rs index 7ef5293b8..053fdbd47 100644 --- a/module-system/sov-modules-api/src/lib.rs +++ b/module-system/sov-modules-api/src/lib.rs @@ -10,6 +10,7 @@ mod dispatch; mod encode; mod error; pub mod hooks; +mod pub_key_hex; #[cfg(feature = "macros")] mod reexport_macros; diff --git a/module-system/sov-modules-api/src/pub_key_hex.rs b/module-system/sov-modules-api/src/pub_key_hex.rs new file mode 100644 index 000000000..3c5d390c5 --- /dev/null +++ b/module-system/sov-modules-api/src/pub_key_hex.rs @@ -0,0 +1,124 @@ +use derive_more::Display; +use ed25519_dalek::{VerifyingKey as DalekPublicKey, PUBLIC_KEY_LENGTH}; + +/// A hexadecimal representation of a PublicKey. +use crate::default_signature::DefaultPublicKey; +#[derive( + serde::Serialize, + serde::Deserialize, + borsh::BorshDeserialize, + borsh::BorshSerialize, + Debug, + PartialEq, + Clone, + Eq, + Display, +)] +#[serde(try_from = "String", into = "String")] +#[display(fmt = "{}", "hex")] +pub struct PublicKeyHex { + hex: String, +} + +impl TryFrom<&str> for PublicKeyHex { + type Error = anyhow::Error; + + fn try_from(hex: &str) -> Result { + Self::try_from(hex.to_owned()) + } +} + +impl TryFrom for PublicKeyHex { + type Error = anyhow::Error; + + fn try_from(hex: String) -> Result { + if hex.len() & 1 != 0 { + anyhow::bail!("Bad hex conversion: odd input length") + } + + if let Some((index, c)) = hex.chars().enumerate().find(|(_, c)| { + !(matches!(c, '0'..='9' | 'a'..='f') || matches!(c, '0'..='9' | 'A'..='F')) + }) { + anyhow::bail!( + "Bad hex conversion: wrong character `{}` at index {}", + c, + index + ) + } + + Ok(Self { hex }) + } +} + +impl From for String { + fn from(pub_key: PublicKeyHex) -> Self { + pub_key.hex + } +} + +impl From for PublicKeyHex { + fn from(pub_key: DefaultPublicKey) -> Self { + let hex = hex::encode(pub_key.pub_key.as_bytes()); + Self { hex } + } +} + +impl TryFrom for DefaultPublicKey { + type Error = anyhow::Error; + + fn try_from(pub_key: PublicKeyHex) -> Result { + let bytes = hex::decode(pub_key.hex)?; + + let bytes: [u8; PUBLIC_KEY_LENGTH] = bytes + .try_into() + .map_err(|_| anyhow::anyhow!("Invalid public key size"))?; + + let pub_key = DalekPublicKey::from_bytes(&bytes) + .map_err(|_| anyhow::anyhow!("Invalid public key"))?; + + Ok(DefaultPublicKey { pub_key }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::default_signature::private_key::DefaultPrivateKey; + use crate::PrivateKey; + + #[test] + fn test_pub_key_hex() { + let pub_key = DefaultPrivateKey::generate().pub_key(); + let pub_key_hex = PublicKeyHex::try_from(pub_key.clone()).unwrap(); + let converted_pub_key = DefaultPublicKey::try_from(pub_key_hex).unwrap(); + assert_eq!(pub_key, converted_pub_key); + } + + #[test] + fn test_pub_key_hex_str() { + let key = "022e229198d957bf0c0a504e7d7bcec99a1d62cccc7861ed2452676ad0323ad8"; + let pub_key_hex_lower: PublicKeyHex = key.try_into().unwrap(); + let pub_key_hex_upper: PublicKeyHex = key.to_uppercase().try_into().unwrap(); + + let pub_key_lower = DefaultPublicKey::try_from(pub_key_hex_lower).unwrap(); + let pub_key_upper = DefaultPublicKey::try_from(pub_key_hex_upper).unwrap(); + + assert_eq!(pub_key_lower, pub_key_upper) + } + + #[test] + fn test_bad_pub_key_hex_str() { + let key = "022e229198d957Zf0c0a504e7d7bcec99a1d62cccc7861ed2452676ad0323ad8"; + let err = PublicKeyHex::try_from(key).unwrap_err(); + + assert_eq!( + err.to_string(), + "Bad hex conversion: wrong character `Z` at index 14" + ); + + let key = "022"; + let err = PublicKeyHex::try_from(key).unwrap_err(); + + assert_eq!(err.to_string(), "Bad hex conversion: odd input length") + } +} From a514a5b003f56bcf68de153b8cddc20d488e74f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orkun=20Mahir=20K=C4=B1l=C4=B1=C3=A7?= Date: Fri, 29 Sep 2023 12:46:50 +0300 Subject: [PATCH 28/34] estimate-gas signed (#947) --- examples/demo-rollup/tests/evm/mod.rs | 30 ++- full-node/sov-ethereum/src/lib.rs | 80 ++++++- .../module-implementations/sov-evm/src/lib.rs | 4 + .../sov-evm/src/query.rs | 213 +++++++++++++++++- 4 files changed, 308 insertions(+), 19 deletions(-) diff --git a/examples/demo-rollup/tests/evm/mod.rs b/examples/demo-rollup/tests/evm/mod.rs index cc867c47a..cebe97a4d 100644 --- a/examples/demo-rollup/tests/evm/mod.rs +++ b/examples/demo-rollup/tests/evm/mod.rs @@ -92,17 +92,15 @@ impl TestClient { contract_address: H160, set_arg: u32, ) -> PendingTransaction<'_, Http> { - let nonce = self.eth_get_transaction_count(self.from_addr).await; - + // Tx without gas_limit should estimate and include it in send_transaction endpoint + // Tx without nonce should fetch and include it in send_transaction endpoint let req = Eip1559TransactionRequest::new() .from(self.from_addr) .to(contract_address) .chain_id(self.chain_id) - .nonce(nonce) .data(self.contract.set_call_data(set_arg)) .max_priority_fee_per_gas(10u64) - .max_fee_per_gas(MAX_FEE_PER_GAS) - .gas(900000u64); + .max_fee_per_gas(MAX_FEE_PER_GAS); let typed_transaction = TypedTransaction::Eip1559(req); @@ -148,9 +146,17 @@ impl TestClient { .chain_id(self.chain_id) .nonce(nonce) .data(self.contract.set_call_data(set_arg)) - .gas_price(10u64) - .gas(900000u64); + .gas_price(10u64); + + let typed_transaction = TypedTransaction::Legacy(req.clone()); + // Estimate gas on rpc + let gas = self + .eth_estimate_gas(typed_transaction, Some("latest".to_owned())) + .await; + + // Call with the estimated gas + let req = req.gas(gas); let typed_transaction = TypedTransaction::Legacy(req); let response = self @@ -268,6 +274,16 @@ impl TestClient { .map_err(|e| e.into()) } + async fn eth_estimate_gas(&self, tx: TypedTransaction, block_number: Option) -> u64 { + let gas: ethereum_types::U64 = self + .http_client + .request("eth_estimateGas", rpc_params![tx, block_number]) + .await + .unwrap(); + + gas.as_u64() + } + async fn execute(self) -> Result<(), Box> { // Nonce should be 0 in genesis let nonce = self.eth_get_transaction_count(self.from_addr).await; diff --git a/full-node/sov-ethereum/src/lib.rs b/full-node/sov-ethereum/src/lib.rs index b34e57cb3..264e5420a 100644 --- a/full-node/sov-ethereum/src/lib.rs +++ b/full-node/sov-ethereum/src/lib.rs @@ -18,9 +18,9 @@ pub mod experimental { use jsonrpsee::types::ErrorObjectOwned; use jsonrpsee::RpcModule; use reth_primitives::{ - Address as RethAddress, TransactionSignedNoHash as RethTransactionSignedNoHash, + Address as RethAddress, TransactionSignedNoHash as RethTransactionSignedNoHash, U128, U256, }; - use reth_rpc_types::{TransactionRequest, TypedTransactionRequest}; + use reth_rpc_types::{CallRequest, TransactionRequest, TypedTransactionRequest}; use sov_evm::{CallMessage, Evm, RlpEvmTransaction}; use sov_modules_api::transaction::Transaction; use sov_modules_api::utils::to_jsonrpsee_error_object; @@ -211,6 +211,8 @@ pub mod experimental { let raw_evm_tx = { let mut working_set = WorkingSet::::new(ethereum.storage.clone()); + + // set nonce if none if transaction_request.nonce.is_none() { let nonce = evm .get_transaction_count(from, None, &mut working_set) @@ -219,27 +221,43 @@ pub mod experimental { transaction_request.nonce = Some(nonce); } + // get current chain id let chain_id = evm .chain_id(&mut working_set) .expect("Failed to get chain id") .map(|id| id.as_u64()) .unwrap_or(1); - // TODO: implement gas logic after gas estimation (#906) is implemented - // https://github.com/Sovereign-Labs/sovereign-sdk/issues/906 + // get call request to estimate gas and gas prices + let (call_request, gas_price, max_fee_per_gas) = + get_call_request_and_params(from, chain_id, &transaction_request); + + // estimate gas limit + let gas_limit = U256::from( + evm.eth_estimate_gas(call_request, None, &mut working_set)? + .as_u64(), + ); + + // get typed transaction request let transaction_request = match transaction_request.into_typed_request() { Some(TypedTransactionRequest::Legacy(mut m)) => { m.chain_id = Some(chain_id); + m.gas_limit = gas_limit; + m.gas_price = gas_price; TypedTransactionRequest::Legacy(m) } Some(TypedTransactionRequest::EIP2930(mut m)) => { m.chain_id = chain_id; + m.gas_limit = gas_limit; + m.gas_price = gas_price; TypedTransactionRequest::EIP2930(m) } Some(TypedTransactionRequest::EIP1559(mut m)) => { m.chain_id = chain_id; + m.gas_limit = gas_limit; + m.max_fee_per_gas = max_fee_per_gas; TypedTransactionRequest::EIP1559(m) } @@ -251,10 +269,12 @@ pub mod experimental { } }; + // get raw transaction let transaction = into_transaction(transaction_request).map_err(|_| { to_jsonrpsee_error_object("Invalid types in transaction request", ETH_RPC_ERROR) })?; + // sign transaction let signed_tx = ethereum .eth_rpc_config .eth_signer @@ -338,4 +358,56 @@ pub mod experimental { let bytes: [u8; 16] = bytes[16..].try_into()?; Ok(u128::from_be_bytes(bytes)) } + + fn get_call_request_and_params( + from: reth_primitives::H160, + chain_id: u64, + transaction_request: &TransactionRequest, + ) -> (CallRequest, U128, U128) { + // TODO: we need an oracle to fetch the gas price of the current chain + // https://github.com/Sovereign-Labs/sovereign-sdk/issues/883 + let gas_price = transaction_request.gas_price.unwrap_or_default(); + let max_fee_per_gas = transaction_request.max_fee_per_gas.unwrap_or_default(); + + // TODO: Generate call request better according to the transaction type + // https://github.com/Sovereign-Labs/sovereign-sdk/issues/946 + let call_request = CallRequest { + from: Some(from), + to: transaction_request.to, + gas: transaction_request.gas, + gas_price: { + if transaction_request.max_priority_fee_per_gas.is_some() { + // eip 1559 + None + } else { + // legacy + Some(U256::from(gas_price)) + } + }, + max_fee_per_gas: Some(U256::from(max_fee_per_gas)), + value: transaction_request.value, + input: transaction_request.data.clone().into(), + nonce: transaction_request.nonce, + chain_id: Some(chain_id.into()), + access_list: transaction_request.access_list.clone(), + max_priority_fee_per_gas: { + if transaction_request.max_priority_fee_per_gas.is_some() { + // eip 1559 + Some(U256::from( + transaction_request + .max_priority_fee_per_gas + .unwrap_or(max_fee_per_gas), + )) + } else { + // legacy + None + } + }, + transaction_type: None, + blob_versioned_hashes: vec![], + max_fee_per_blob_gas: None, + }; + + (call_request, gas_price, max_fee_per_gas) + } } diff --git a/module-system/module-implementations/sov-evm/src/lib.rs b/module-system/module-implementations/sov-evm/src/lib.rs index b256b2ae5..a53d197aa 100644 --- a/module-system/module-implementations/sov-evm/src/lib.rs +++ b/module-system/module-implementations/sov-evm/src/lib.rs @@ -47,6 +47,10 @@ mod experimental { Block, BlockEnv, Receipt, SealedBlock, TransactionSignedAndRecovered, }; + // Gas per transaction not creating a contract. + pub(crate) const MIN_TRANSACTION_GAS: u64 = 21_000u64; + pub(crate) const MIN_CREATE_GAS: u64 = 53_000u64; + /// Evm account. #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct AccountData { diff --git a/module-system/module-implementations/sov-evm/src/query.rs b/module-system/module-implementations/sov-evm/src/query.rs index e2314465a..183cb87f5 100644 --- a/module-system/module-implementations/sov-evm/src/query.rs +++ b/module-system/module-implementations/sov-evm/src/query.rs @@ -1,18 +1,24 @@ +use std::array::TryFromSliceError; + use ethereum_types::U64; use jsonrpsee::core::RpcResult; use reth_primitives::contract::create_address; use reth_primitives::TransactionKind::{Call, Create}; use reth_primitives::{TransactionSignedEcRecovered, U128, U256}; +use revm::primitives::{ + EVMError, ExecutionResult, Halt, InvalidTransaction, TransactTo, KECCAK_EMPTY, +}; use sov_modules_api::macros::rpc_gen; use sov_modules_api::WorkingSet; use tracing::info; use crate::call::get_cfg_env; -use crate::error::rpc::ensure_success; +use crate::error::rpc::{ensure_success, RevertError, RpcInvalidTransactionError}; use crate::evm::db::EvmDb; use crate::evm::primitive_types::{BlockEnv, Receipt, SealedBlock, TransactionSignedAndRecovered}; use crate::evm::{executor, prepare_call_env}; -use crate::Evm; +use crate::experimental::{MIN_CREATE_GAS, MIN_TRANSACTION_GAS}; +use crate::{EthApiError, Evm}; #[rpc_gen(client, server, namespace = "eth")] impl Evm { @@ -260,15 +266,177 @@ impl Evm { } /// Handler for: `eth_estimateGas` - // TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/502 + // https://github.com/paradigmxyz/reth/blob/main/crates/rpc/rpc/src/eth/api/call.rs#L172 #[rpc_method(name = "estimateGas")] pub fn eth_estimate_gas( &self, - _data: reth_rpc_types::CallRequest, - _block_number: Option, - _working_set: &mut WorkingSet, - ) -> RpcResult { - unimplemented!("eth_estimateGas not implemented") + request: reth_rpc_types::CallRequest, + block_number: Option, + working_set: &mut WorkingSet, + ) -> RpcResult { + info!("evm module: eth_estimateGas"); + let mut block_env = match block_number { + Some(ref block_number) if block_number == "pending" => { + self.block_env.get(working_set).unwrap_or_default().clone() + } + _ => { + let block = self.get_sealed_block_by_number(block_number, working_set); + BlockEnv::from(&block) + } + }; + + let tx_env = prepare_call_env(&block_env, request.clone()).unwrap(); + + let cfg = self.cfg.get(working_set).unwrap_or_default(); + let cfg_env = get_cfg_env(&block_env, cfg, Some(get_cfg_env_template())); + + let request_gas = request.gas; + let request_gas_price = request.gas_price; + let env_gas_limit = block_env.gas_limit; + + // get the highest possible gas limit, either the request's set value or the currently + // configured gas limit + let mut highest_gas_limit = request.gas.unwrap_or(U256::from(env_gas_limit)); + + let account = self.accounts.get(&tx_env.caller, working_set).unwrap(); + + // if the request is a simple transfer we can optimize + if tx_env.data.is_empty() { + if let TransactTo::Call(to) = tx_env.transact_to { + let to_account = self.accounts.get(&to, working_set).unwrap(); + if KECCAK_EMPTY == to_account.info.code_hash { + // simple transfer, check if caller has sufficient funds + let available_funds = account.info.balance; + + if tx_env.value > available_funds { + return Err(RpcInvalidTransactionError::InsufficientFundsForTransfer.into()); + } + return Ok(U64::from(MIN_TRANSACTION_GAS)); + } + } + } + + // check funds of the sender + if tx_env.gas_price > U256::ZERO { + // allowance is (balance - tx.value) / tx.gas_price + let allowance = (account.info.balance - tx_env.value) / tx_env.gas_price; + + if highest_gas_limit > allowance { + // cap the highest gas limit by max gas caller can afford with given gas price + highest_gas_limit = allowance; + } + } + + // if the provided gas limit is less than computed cap, use that + let gas_limit = std::cmp::min(U256::from(tx_env.gas_limit), highest_gas_limit); + block_env.gas_limit = convert_u256_to_u64(gas_limit).unwrap(); + + let evm_db = self.get_db(working_set); + + // execute the call without writing to db + let result = executor::inspect(evm_db, &block_env, tx_env.clone(), cfg_env.clone()); + + // Exceptional case: init used too much gas, we need to increase the gas limit and try + // again + if let Err(EVMError::Transaction(InvalidTransaction::CallerGasLimitMoreThanBlock)) = result + { + // if price or limit was included in the request then we can execute the request + // again with the block's gas limit to check if revert is gas related or not + if request_gas.is_some() || request_gas_price.is_some() { + let evm_db = self.get_db(working_set); + return Err(map_out_of_gas_err(block_env, tx_env, cfg_env, evm_db).into()); + } + } + + let result = result.unwrap(); + + match result.result { + ExecutionResult::Success { .. } => { + // succeeded + } + ExecutionResult::Halt { reason, gas_used } => { + return Err(RpcInvalidTransactionError::halt(reason, gas_used).into()) + } + ExecutionResult::Revert { output, .. } => { + // if price or limit was included in the request then we can execute the request + // again with the block's gas limit to check if revert is gas related or not + return if request_gas.is_some() || request_gas_price.is_some() { + let evm_db = self.get_db(working_set); + Err(map_out_of_gas_err(block_env, tx_env, cfg_env, evm_db).into()) + } else { + // the transaction did revert + Err(RpcInvalidTransactionError::Revert(RevertError::new(output)).into()) + }; + } + } + + // at this point we know the call succeeded but want to find the _best_ (lowest) gas the + // transaction succeeds with. we find this by doing a binary search over the + // possible range NOTE: this is the gas the transaction used, which is less than the + // transaction requires to succeed + let gas_used = result.result.gas_used(); + // the lowest value is capped by the gas it takes for a transfer + let mut lowest_gas_limit = if tx_env.transact_to.is_create() { + MIN_CREATE_GAS + } else { + MIN_TRANSACTION_GAS + }; + let mut highest_gas_limit: u64 = highest_gas_limit.try_into().unwrap_or(u64::MAX); + // pick a point that's close to the estimated gas + let mut mid_gas_limit = std::cmp::min( + gas_used * 3, + ((highest_gas_limit as u128 + lowest_gas_limit as u128) / 2) as u64, + ); + // binary search + while (highest_gas_limit - lowest_gas_limit) > 1 { + let mut tx_env = tx_env.clone(); + tx_env.gas_limit = mid_gas_limit; + + let evm_db = self.get_db(working_set); + let result = executor::inspect(evm_db, &block_env, tx_env.clone(), cfg_env.clone()); + + // Exceptional case: init used too much gas, we need to increase the gas limit and try + // again + if let Err(EVMError::Transaction(InvalidTransaction::CallerGasLimitMoreThanBlock)) = + result + { + // increase the lowest gas limit + lowest_gas_limit = mid_gas_limit; + + // new midpoint + mid_gas_limit = ((highest_gas_limit as u128 + lowest_gas_limit as u128) / 2) as u64; + continue; + } + + let result = result.unwrap(); + match result.result { + ExecutionResult::Success { .. } => { + // cap the highest gas limit with succeeding gas limit + highest_gas_limit = mid_gas_limit; + } + ExecutionResult::Revert { .. } => { + // increase the lowest gas limit + lowest_gas_limit = mid_gas_limit; + } + ExecutionResult::Halt { reason, .. } => { + match reason { + Halt::OutOfGas(_) => { + // increase the lowest gas limit + lowest_gas_limit = mid_gas_limit; + } + err => { + // these should be unreachable because we know the transaction succeeds, + // but we consider these cases an error + return Err(RpcInvalidTransactionError::EvmHalt(err).into()); + } + } + } + } + // new midpoint + mid_gas_limit = ((highest_gas_limit as u128 + lowest_gas_limit as u128) / 2) as u64; + } + + Ok(U64::from(highest_gas_limit)) } /// Handler for: `eth_gasPrice` @@ -388,3 +556,32 @@ pub(crate) fn build_rpc_receipt( .collect(), } } + +fn map_out_of_gas_err( + block_env: BlockEnv, + mut tx_env: revm::primitives::TxEnv, + cfg_env: revm::primitives::CfgEnv, + db: EvmDb<'_, C>, +) -> EthApiError { + let req_gas_limit = tx_env.gas_limit; + tx_env.gas_limit = block_env.gas_limit; + let res = executor::inspect(db, &block_env, tx_env, cfg_env).unwrap(); + match res.result { + ExecutionResult::Success { .. } => { + // transaction succeeded by manually increasing the gas limit to + // highest, which means the caller lacks funds to pay for the tx + RpcInvalidTransactionError::BasicOutOfGas(U256::from(req_gas_limit)).into() + } + ExecutionResult::Revert { output, .. } => { + // reverted again after bumping the limit + RpcInvalidTransactionError::Revert(RevertError::new(output)).into() + } + ExecutionResult::Halt { reason, .. } => RpcInvalidTransactionError::EvmHalt(reason).into(), + } +} + +fn convert_u256_to_u64(u256: reth_primitives::U256) -> Result { + let bytes: [u8; 32] = u256.to_be_bytes(); + let bytes: [u8; 8] = bytes[24..].try_into()?; + Ok(u64::from_be_bytes(bytes)) +} From 19b4ea1a62e60e5508457599bb18b2b32c1c072c Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Fri, 29 Sep 2023 16:49:58 +0530 Subject: [PATCH 29/34] remove include and make it a module --- .../module-implementations/sov-nft-module/src/lib.rs | 2 ++ .../sov-nft-module/src/offchain.rs | 5 ++--- .../module-implementations/sov-nft-module/src/sql.rs | 9 +++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/module-system/module-implementations/sov-nft-module/src/lib.rs b/module-system/module-implementations/sov-nft-module/src/lib.rs index 3ca20dc3f..b1d26129b 100644 --- a/module-system/module-implementations/sov-nft-module/src/lib.rs +++ b/module-system/module-implementations/sov-nft-module/src/lib.rs @@ -17,6 +17,8 @@ pub use query::*; use serde::{Deserialize, Serialize}; use sov_modules_api::{CallResponse, Context, Error, Module, ModuleInfo, StateMap, WorkingSet}; mod offchain; +#[cfg(feature = "offchain")] +mod sql; /// Utility functions. pub mod utils; diff --git a/module-system/module-implementations/sov-nft-module/src/offchain.rs b/module-system/module-implementations/sov-nft-module/src/offchain.rs index a6460d775..a2d6a192c 100644 --- a/module-system/module-implementations/sov-nft-module/src/offchain.rs +++ b/module-system/module-implementations/sov-nft-module/src/offchain.rs @@ -2,15 +2,14 @@ use postgres::NoTls; use sov_modules_macros::offchain; +#[cfg(feature = "offchain")] +use crate::sql::*; #[cfg(feature = "offchain")] use crate::utils::get_collection_address; #[cfg(feature = "offchain")] use crate::CollectionAddress; use crate::{Collection, Nft, OwnerAddress}; -#[cfg(feature = "offchain")] -include!("sql.rs"); - /// Syncs a collection to the corresponding table "collections" in postgres #[offchain] pub fn update_collection(collection: &Collection) { diff --git a/module-system/module-implementations/sov-nft-module/src/sql.rs b/module-system/module-implementations/sov-nft-module/src/sql.rs index 0a29a90ec..6b521a877 100644 --- a/module-system/module-implementations/sov-nft-module/src/sql.rs +++ b/module-system/module-implementations/sov-nft-module/src/sql.rs @@ -1,5 +1,4 @@ -pub const INSERT_OR_UPDATE_COLLECTION: &str = - "INSERT INTO collections (\ +pub const INSERT_OR_UPDATE_COLLECTION: &str = "INSERT INTO collections (\ collection_address, collection_name, creator_address,\ frozen, metadata_url, supply)\ VALUES ($1, $2, $3, $4, $5, $6)\ @@ -13,8 +12,7 @@ pub const INSERT_OR_UPDATE_COLLECTION: &str = pub const QUERY_OWNER_FROM_NFTS: &str = "SELECT owner FROM nfts WHERE collection_address = $1 AND nft_id = $2"; -pub const DECREMENT_COUNT_FOR_OLD_OWNER: &str = - "UPDATE top_owners SET count = count - 1 \ +pub const DECREMENT_COUNT_FOR_OLD_OWNER: &str = "UPDATE top_owners SET count = count - 1 \ WHERE owner = $1 AND collection_address = $2 AND count > 0"; pub const INCREMENT_OR_UPDATE_COUNT_FOR_NEW_OWNER: &str = @@ -22,8 +20,7 @@ pub const INCREMENT_OR_UPDATE_COUNT_FOR_NEW_OWNER: &str = ON CONFLICT (owner, collection_address) \ DO UPDATE SET count = top_owners.count + 1"; -pub const INSERT_OR_UPDATE_NFT: &str = - "INSERT INTO nfts (\ +pub const INSERT_OR_UPDATE_NFT: &str = "INSERT INTO nfts (\ collection_address, nft_id, metadata_url,\ owner, frozen)\ VALUES ($1, $2, $3, $4, $5)\ From 45cc7482953a5e5c4e0b001f75d84bc15789b7d8 Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Fri, 29 Sep 2023 17:03:42 +0530 Subject: [PATCH 30/34] fix packages.yml --- module-system/module-implementations/sov-nft-module/Cargo.toml | 1 + packages_to_publish.yml | 1 + utils/nft-utils/Cargo.toml | 1 + 3 files changed, 3 insertions(+) diff --git a/module-system/module-implementations/sov-nft-module/Cargo.toml b/module-system/module-implementations/sov-nft-module/Cargo.toml index 38b0d28b9..d1f4e0d9f 100644 --- a/module-system/module-implementations/sov-nft-module/Cargo.toml +++ b/module-system/module-implementations/sov-nft-module/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "sov-nft-module" +description = "A Sovereign SDK module for managing non-fungible tokens" version = { workspace = true } edition = { workspace = true } authors = { workspace = true } diff --git a/packages_to_publish.yml b/packages_to_publish.yml index cf287870f..2143d509a 100644 --- a/packages_to_publish.yml +++ b/packages_to_publish.yml @@ -17,6 +17,7 @@ - sov-prover-incentives - sov-chain-state - sov-blob-storage +- sov-nft-module # Adapters - sov-risc0-adapter diff --git a/utils/nft-utils/Cargo.toml b/utils/nft-utils/Cargo.toml index 2aaa4bc2e..6375f1ee6 100644 --- a/utils/nft-utils/Cargo.toml +++ b/utils/nft-utils/Cargo.toml @@ -11,6 +11,7 @@ version = { workspace = true } readme = "README.md" resolver = "2" autotests = false +publish = false [dependencies] borsh = { workspace = true } From a45fab2c2d924a25c04e1366ff5f2aef16299c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orkun=20Mahir=20K=C4=B1l=C4=B1=C3=A7?= Date: Fri, 29 Sep 2023 13:51:57 +0300 Subject: [PATCH 31/34] EVM: Implement account related RPC (#958) * implement account endpoints * test account endpoints * remove unnecessary conversions --- examples/demo-rollup/tests/evm/mod.rs | 68 ++++++++++++++++++- .../sov-evm/src/query.rs | 67 ++++++++++++++++++ 2 files changed, 132 insertions(+), 3 deletions(-) diff --git a/examples/demo-rollup/tests/evm/mod.rs b/examples/demo-rollup/tests/evm/mod.rs index cebe97a4d..b9942c14b 100644 --- a/examples/demo-rollup/tests/evm/mod.rs +++ b/examples/demo-rollup/tests/evm/mod.rs @@ -87,6 +87,23 @@ impl TestClient { Ok(receipt_req) } + async fn deploy_contract_call(&self) -> Result> { + let req = Eip1559TransactionRequest::new() + .from(self.from_addr) + .chain_id(self.chain_id) + .nonce(0u64) + .max_priority_fee_per_gas(10u64) + .max_fee_per_gas(MAX_FEE_PER_GAS) + .gas(900000u64) + .data(self.contract.byte_code()); + + let typed_transaction = TypedTransaction::Eip1559(req); + + let receipt_req = self.eth_call(typed_transaction, None).await?; + + Ok(receipt_req) + } + async fn set_value_unsigned( &self, contract_address: H160, @@ -236,6 +253,31 @@ impl TestClient { chain_id.as_u64() } + async fn eth_get_balance(&self, address: Address) -> ethereum_types::U256 { + self.http_client + .request("eth_getBalance", rpc_params![address, "latest"]) + .await + .unwrap() + } + + async fn eth_get_storage_at( + &self, + address: Address, + index: ethereum_types::U256, + ) -> ethereum_types::U256 { + self.http_client + .request("eth_getStorageAt", rpc_params![address, index, "latest"]) + .await + .unwrap() + } + + async fn eth_get_code(&self, address: Address) -> Bytes { + self.http_client + .request("eth_getCode", rpc_params![address, "latest"]) + .await + .unwrap() + } + async fn eth_get_transaction_count(&self, address: Address) -> u64 { let count: ethereum_types::U64 = self .http_client @@ -289,17 +331,30 @@ impl TestClient { let nonce = self.eth_get_transaction_count(self.from_addr).await; assert_eq!(0, nonce); - let contract_address = { + // Balance should be > 0 in genesis + let balance = self.eth_get_balance(self.from_addr).await; + assert!(balance > ethereum_types::U256::zero()); + + let (contract_address, runtime_code) = { + let runtime_code = self.deploy_contract_call().await?; + let deploy_contract_req = self.deploy_contract().await?; self.send_publish_batch_request().await; - deploy_contract_req + let contract_address = deploy_contract_req .await? .unwrap() .contract_address - .unwrap() + .unwrap(); + + (contract_address, runtime_code) }; + // Assert contract deployed correctly + let code = self.eth_get_code(contract_address).await; + // code has natural following 0x00 bytes, so we need to trim it + assert_eq!(code.to_vec()[..runtime_code.len()], runtime_code.to_vec()); + // Nonce should be 1 after the deploy let nonce = self.eth_get_transaction_count(self.from_addr).await; assert_eq!(1, nonce); @@ -320,6 +375,13 @@ impl TestClient { let get_arg = self.query_contract(contract_address).await?; assert_eq!(set_arg, get_arg.as_u32()); + // Assert storage slot is set + let storage_slot = 0x0; + let storage_value = self + .eth_get_storage_at(contract_address, storage_slot.into()) + .await; + assert_eq!(storage_value, ethereum_types::U256::from(set_arg)); + // Check that the second block has published // None should return the latest block // It should have a single transaction, setting the value diff --git a/module-system/module-implementations/sov-evm/src/query.rs b/module-system/module-implementations/sov-evm/src/query.rs index 183cb87f5..a6ddd1350 100644 --- a/module-system/module-implementations/sov-evm/src/query.rs +++ b/module-system/module-implementations/sov-evm/src/query.rs @@ -100,6 +100,51 @@ impl Evm { Ok(Some(block.into())) } + /// Handler for: `eth_getBalance` + #[rpc_method(name = "getBalance")] + pub fn get_balance( + &self, + address: reth_primitives::Address, + _block_number: Option, + working_set: &mut WorkingSet, + ) -> RpcResult { + info!("evm module: eth_getBalance"); + + // TODO: Implement block_number once we have archival state #951 + // https://github.com/Sovereign-Labs/sovereign-sdk/issues/951 + + let balance = self + .accounts + .get(&address, working_set) + .map(|account| account.info.balance) + .unwrap_or_default(); + + Ok(balance) + } + + /// Handler for: `eth_getStorageAt` + #[rpc_method(name = "getStorageAt")] + pub fn get_storage_at( + &self, + address: reth_primitives::Address, + index: reth_primitives::U256, + _block_number: Option, + working_set: &mut WorkingSet, + ) -> RpcResult { + info!("evm module: eth_getStorageAt"); + + // TODO: Implement block_number once we have archival state #951 + // https://github.com/Sovereign-Labs/sovereign-sdk/issues/951 + + let storage_slot = self + .accounts + .get(&address, working_set) + .and_then(|account| account.storage.get(&index, working_set)) + .unwrap_or_default(); + + Ok(storage_slot) + } + /// Handler for: `eth_getTransactionCount` #[rpc_method(name = "getTransactionCount")] pub fn get_transaction_count( @@ -122,6 +167,28 @@ impl Evm { Ok(nonce.into()) } + /// Handler for: `eth_getCode` + #[rpc_method(name = "getCode")] + pub fn get_code( + &self, + address: reth_primitives::Address, + _block_number: Option, + working_set: &mut WorkingSet, + ) -> RpcResult { + info!("evm module: eth_getCode"); + + // TODO: Implement block_number once we have archival state #951 + // https://github.com/Sovereign-Labs/sovereign-sdk/issues/951 + + let code = self + .accounts + .get(&address, working_set) + .and_then(|account| self.code.get(&account.info.code_hash, working_set)) + .unwrap_or_default(); + + Ok(code) + } + /// Handler for: `eth_feeHistory` // TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/502 #[rpc_method(name = "feeHistory")] From 261e6cb4beeaf259d20325f43b3c14e7bac1297e Mon Sep 17 00:00:00 2001 From: Victor Lopes Date: Fri, 29 Sep 2023 13:17:48 +0200 Subject: [PATCH 32/34] feature: gas meter (#795) * feat: add gas meter to working set This commit introduces `GasMeter`, encapsulated by `WorkingSet`. It will allow the user to consume scalar gas from the working set, and define arbitrary price parsed from a constants.json manifest file at compilation. At each compilation, the `ModuleInfo` derive macro will parse such file, and set the gas price configuration. * fix lint fmt * fix ci test expected error string * update default context to 2 dimensions --- constants.json | 11 +- .../methods/guest-celestia/Cargo.lock | 5 - .../demo-prover/methods/guest-mock/Cargo.lock | 5 - module-system/README.md | 105 ++++++++++ .../sov-bank/src/lib.rs | 30 ++- module-system/sov-modules-api/README.md | 6 + .../sov-modules-api/src/default_context.rs | 6 +- module-system/sov-modules-api/src/gas.rs | 92 ++++++++ module-system/sov-modules-api/src/lib.rs | 17 ++ .../sov-modules-api/src/state/scratchpad.rs | 19 ++ module-system/sov-modules-macros/src/lib.rs | 2 +- .../sov-modules-macros/src/manifest.rs | 196 ++++++++++-------- .../sov-modules-macros/src/module_info.rs | 52 ++++- .../field_missing_attribute.stderr | 2 +- 14 files changed, 442 insertions(+), 106 deletions(-) create mode 100644 module-system/sov-modules-api/src/gas.rs diff --git a/constants.json b/constants.json index 96620c6e7..9beb309f4 100644 --- a/constants.json +++ b/constants.json @@ -1,3 +1,12 @@ { - "comment": "Sovereign SDK constants" + "comment": "Sovereign SDK constants", + "gas": { + "Bank": { + "create_token": [4, 4], + "transfer": [5, 5], + "burn": [2, 2], + "mint": [2, 2], + "freeze": [1, 1] + } + } } diff --git a/examples/demo-prover/methods/guest-celestia/Cargo.lock b/examples/demo-prover/methods/guest-celestia/Cargo.lock index da5807d78..b4574b135 100644 --- a/examples/demo-prover/methods/guest-celestia/Cargo.lock +++ b/examples/demo-prover/methods/guest-celestia/Cargo.lock @@ -1854,8 +1854,3 @@ dependencies = [ "quote", "syn 2.0.37", ] - -[[patch.unused]] -name = "cc" -version = "1.0.79" -source = "git+https://github.com/rust-lang/cc-rs?rev=e5bbdfa#e5bbdfa1fa468c028cb38fee6c35a3cf2e5a2736" diff --git a/examples/demo-prover/methods/guest-mock/Cargo.lock b/examples/demo-prover/methods/guest-mock/Cargo.lock index db5de049c..d4302d1f0 100644 --- a/examples/demo-prover/methods/guest-mock/Cargo.lock +++ b/examples/demo-prover/methods/guest-mock/Cargo.lock @@ -1272,8 +1272,3 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[patch.unused]] -name = "cc" -version = "1.0.79" -source = "git+https://github.com/rust-lang/cc-rs?rev=e5bbdfa#e5bbdfa1fa468c028cb38fee6c35a3cf2e5a2736" diff --git a/module-system/README.md b/module-system/README.md index 4d8bf8d43..9812c4b1c 100644 --- a/module-system/README.md +++ b/module-system/README.md @@ -44,6 +44,111 @@ This has several consequences. First, it means that modules are always cheap to always yields the same result as calling `MyModule::new()`. Finally, it means that every method of the module which reads or modifies state needs to take a `WorkingSet` as an argument. +### Gas configuration + +The module might contain a field for the gas configuration. If annotated with `#[gas]` under a struct that derives `ModuleInfo`, it will attempt to read a `constants.json` file from the root of the project, and inject it into the `Default::default()` implementation of the module. + +Here is an example `constants.json` file: + +```json +{ + "gas": { + "create_token": 4, + "transfer": 5, + "burn": 2, + "mint": 2, + "freeze": 1 + } +} +``` + +The `ModuleInfo` macro will look for a `gas` field inside the JSON, that must be an object, and will look for the name of the module inside of the `gas` object. If present, it will parse that object as gas configuration; otherwise, it will parse the `gas` object directly. On the example above, it will attempt to parse a structure that looks like this: + +```rust +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BankGasConfig { + pub create_token: GU, + pub transfer: GU, + pub burn: GU, + pub mint: GU, + pub freeze: GU, +} +``` + +The `GasUnit` generic type will be defined by the runtime `Context`. For `DefaultContext`, we use `TupleGasUnit<2>` - that is, a gas unit with a two dimensions. The same setup is defined for `ZkDefaultContext`. Here is an example of a `constants.json` file, specific to the `Bank` module: + +```json +{ + "gas": { + "comment": "this field will be ignored, as there is a matching module field", + "Bank": { + "create_token": [4, 19], + "transfer": [5, 25], + "burn": [2, 7], + "mint": [2, 6], + "freeze": [1, 4] + } + } +} +``` + +As you can see above, the fields can be either array, numeric, or boolean. If boolean, it will be converted to either `0` or `1`. If array, each element is expected to be either a numeric or boolean. The example above will create a gas unit of two dimensions. If the `Context` requires less dimensions than available, it will pick the first ones of relevance, and ignore the rest. That is: with a `Context` of one dimension, , the effective config will be expanded to: + +```rust +BankGasConfig { + create_token: [4], + transfer: [5], + burn: [2], + mint: [2], + freeze: [1], +} +``` + +In order to charge gas from the working set, the function `charge_gas` can be used. + +```rust +fn call( + &self, + msg: Self::CallMessage, + context: &Self::Context, + working_set: &mut WorkingSet, +) -> Result { + match msg { + call::CallMessage::CreateToken { + salt, + token_name, + initial_balance, + minter_address, + authorized_minters, + } => { + self.charge_gas(working_set, &self.gas.create_token)?; + // Implementation elided... +} +``` + +On the example above, we charge the configured unit from the working set. Concretely, we will charge a unit of `[4, 19]` from both `DefaultContext` and `ZkDefaultContext`. The working set will be the responsible to perform a scalar conversion from the dimensions to a single funds value. It will perform an inner product of the loaded price, with the provided unit. + +Let's assume we have a working set with the loaded price `[3, 2]`. The charged gas of the operation above will be `[3] · [4] = 3 × 4 = 12` for a single dimension context, and `[3, 2] · [4, 19] = 3 × 4 + 2 × 19 = 50` for both `DefaultContext` and `ZkDefaultContext`. This approach is intended to unlock [Dynamic Pricing](https://arxiv.org/abs/2208.07919). + +The aforementioned `Bank` struct, with the gas configuration, will look like this: + +```rust +#[derive(ModuleInfo)] +pub struct Bank { + /// The address of the bank module. + #[address] + pub(crate) address: C::Address, + + /// The gas configuration of the sov-bank module. + #[gas] + pub(crate) gas: BankGasConfig, + + /// A mapping of addresses to tokens in the bank. + #[state] + pub(crate) tokens: sov_state::StateMap>, +} +``` + ### Public Functions: The Module-to-Module Interface The first interface that modules expose is defined by the public methods from the rollup's `impl`. These methods are diff --git a/module-system/module-implementations/sov-bank/src/lib.rs b/module-system/module-implementations/sov-bank/src/lib.rs index e8d3425e4..7f3cece42 100644 --- a/module-system/module-implementations/sov-bank/src/lib.rs +++ b/module-system/module-implementations/sov-bank/src/lib.rs @@ -13,7 +13,7 @@ pub mod utils; /// Specifies the call methods using in that module. pub use call::CallMessage; use serde::{Deserialize, Serialize}; -use sov_modules_api::{CallResponse, Error, ModuleInfo, WorkingSet}; +use sov_modules_api::{CallResponse, Error, GasUnit, ModuleInfo, WorkingSet}; use token::Token; /// Specifies an interface to interact with tokens. pub use token::{Amount, Coins}; @@ -41,6 +41,25 @@ pub struct BankConfig { pub tokens: Vec>, } +/// Gas configuration for the bank module +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BankGasConfig { + /// Gas price multiplier for the create token operation + pub create_token: GU, + + /// Gas price multiplier for the transfer operation + pub transfer: GU, + + /// Gas price multiplier for the burn operation + pub burn: GU, + + /// Gas price multiplier for the mint operation + pub mint: GU, + + /// Gas price multiplier for the freeze operation + pub freeze: GU, +} + /// The sov-bank module manages user balances. It provides functionality for: /// - Token creation. /// - Token transfers. @@ -52,6 +71,10 @@ pub struct Bank { #[address] pub(crate) address: C::Address, + /// The gas configuration of the sov-bank module. + #[gas] + pub(crate) gas: BankGasConfig, + /// A mapping of addresses to tokens in the sov-bank. #[state] pub(crate) tokens: sov_modules_api::StateMap>, @@ -82,6 +105,7 @@ impl sov_modules_api::Module for Bank { minter_address, authorized_minters, } => { + self.charge_gas(working_set, &self.gas.create_token)?; self.create_token( token_name, salt, @@ -95,10 +119,12 @@ impl sov_modules_api::Module for Bank { } call::CallMessage::Transfer { to, coins } => { + self.charge_gas(working_set, &self.gas.create_token)?; Ok(self.transfer(to, coins, context, working_set)?) } call::CallMessage::Burn { coins } => { + self.charge_gas(working_set, &self.gas.burn)?; Ok(self.burn_from_eoa(coins, context, working_set)?) } @@ -106,11 +132,13 @@ impl sov_modules_api::Module for Bank { coins, minter_address, } => { + self.charge_gas(working_set, &self.gas.mint)?; self.mint_from_eoa(&coins, &minter_address, context, working_set)?; Ok(CallResponse::default()) } call::CallMessage::Freeze { token_address } => { + self.charge_gas(working_set, &self.gas.freeze)?; Ok(self.freeze(token_address, context, working_set)?) } } diff --git a/module-system/sov-modules-api/README.md b/module-system/sov-modules-api/README.md index 5753e7eb5..9db055ed7 100644 --- a/module-system/sov-modules-api/README.md +++ b/module-system/sov-modules-api/README.md @@ -14,6 +14,9 @@ crate: - Interaction with user messages: The module must define the `call` method and the `CallMessage` type, which handle user messages. These messages typically result in changes to the module's state. + - Gas configuration: The module may use a `GasConfig` type, annotated by `#[gas]`, that will be loaded from the + constants manifest configuration. + 1. The `ModuleInfo` trait: Provides additional information related to a module. This trait is automatically derived. 1. The `Spec` trait: It defines all the types that modules are generic over. This separation allows the module logic to @@ -29,3 +32,6 @@ crate: 1. The `DispatchCall` trait: Defines how messages are forwarded to the appropriate module and how the call message is executed. The implementation of this trait can be generated automatically using a macro. + +1. The `GasUnit` trait: Defines how the scalar gas value is deducted from the working set. This is implemented for + `[u64; N]`, and can be customized by the user. diff --git a/module-system/sov-modules-api/src/default_context.rs b/module-system/sov-modules-api/src/default_context.rs index a07f6b735..8fccbcc41 100644 --- a/module-system/sov-modules-api/src/default_context.rs +++ b/module-system/sov-modules-api/src/default_context.rs @@ -9,7 +9,7 @@ use sov_state::{ArrayWitness, DefaultStorageSpec, ZkStorage}; #[cfg(feature = "native")] use crate::default_signature::private_key::DefaultPrivateKey; use crate::default_signature::{DefaultPublicKey, DefaultSignature}; -use crate::{Address, Context, PublicKey, Spec}; +use crate::{Address, Context, PublicKey, Spec, TupleGasUnit}; #[cfg(feature = "native")] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] @@ -31,6 +31,8 @@ impl Spec for DefaultContext { #[cfg(feature = "native")] impl Context for DefaultContext { + type GasUnit = TupleGasUnit<2>; + fn sender(&self) -> &Self::Address { &self.sender } @@ -58,6 +60,8 @@ impl Spec for ZkDefaultContext { } impl Context for ZkDefaultContext { + type GasUnit = TupleGasUnit<2>; + fn sender(&self) -> &Self::Address { &self.sender } diff --git a/module-system/sov-modules-api/src/gas.rs b/module-system/sov-modules-api/src/gas.rs new file mode 100644 index 000000000..071af99fe --- /dev/null +++ b/module-system/sov-modules-api/src/gas.rs @@ -0,0 +1,92 @@ +use core::fmt; + +use anyhow::Result; + +/// A gas unit that provides scalar conversion from complex, multi-dimensional types. +pub trait GasUnit: fmt::Debug + Clone { + /// A zeroed instance of the unit. + const ZEROED: Self; + + /// Creates a unit from a multi-dimensional unit with arbitrary dimension. + fn from_arbitrary_dimensions(dimensions: &[u64]) -> Self; + + /// Converts the unit into a scalar value, given a price. + fn value(&self, price: &Self) -> u64; +} + +/// A multi-dimensional gas unit. +pub type TupleGasUnit = [u64; N]; + +impl GasUnit for TupleGasUnit { + const ZEROED: Self = [0; N]; + + fn from_arbitrary_dimensions(dimensions: &[u64]) -> Self { + // as demonstrated on the link below, the compiler can easily optimize the conversion as if + // it is a transparent type. + // + // https://rust.godbolt.org/z/rPhaxnPEY + let mut unit = Self::ZEROED; + unit.iter_mut() + .zip(dimensions.iter().copied()) + .for_each(|(a, b)| *a = b); + unit + } + + fn value(&self, price: &Self) -> u64 { + self.iter() + .zip(price.iter().copied()) + .map(|(a, b)| a.saturating_mul(b)) + .fold(0, |a, b| a.saturating_add(b)) + } +} + +/// A gas meter. +pub struct GasMeter +where + GU: GasUnit, +{ + remaining_funds: u64, + gas_price: GU, +} + +impl Default for GasMeter +where + GU: GasUnit, +{ + fn default() -> Self { + Self { + remaining_funds: 0, + gas_price: GU::ZEROED, + } + } +} + +impl GasMeter +where + GU: GasUnit, +{ + /// Creates a new instance of the gas meter with the provided price. + pub fn new(remaining_funds: u64, gas_price: GU) -> Self { + Self { + remaining_funds, + gas_price, + } + } + + /// Returns the remaining gas funds. + pub const fn remaining_funds(&self) -> u64 { + self.remaining_funds + } + + /// Deducts the provided gas unit from the remaining funds, computing the scalar value of the + /// funds from the price of the instance. + pub fn charge_gas(&mut self, gas: &GU) -> Result<()> { + let gas = gas.value(&self.gas_price); + self.remaining_funds = self + .remaining_funds + .checked_sub(gas) + .ok_or_else(|| anyhow::anyhow!("Not enough gas"))?; + + Ok(()) + } +} diff --git a/module-system/sov-modules-api/src/lib.rs b/module-system/sov-modules-api/src/lib.rs index 053fdbd47..38ec31cfd 100644 --- a/module-system/sov-modules-api/src/lib.rs +++ b/module-system/sov-modules-api/src/lib.rs @@ -9,6 +9,7 @@ pub mod default_signature; mod dispatch; mod encode; mod error; +mod gas; pub mod hooks; mod pub_key_hex; @@ -46,6 +47,7 @@ use digest::Digest; pub use dispatch::CliWallet; pub use dispatch::{DispatchCall, EncodeCall, Genesis}; pub use error::Error; +pub use gas::{GasUnit, TupleGasUnit}; pub use prefix::Prefix; pub use response::CallResponse; #[cfg(feature = "native")] @@ -256,6 +258,9 @@ pub trait Spec { /// instance of the state transition function. By making modules generic over a `Context`, developers /// can easily update their cryptography to conform to the needs of different zk-proof systems. pub trait Context: Spec + Clone + Debug + PartialEq + 'static { + /// Gas unit for the gas price computation. + type GasUnit: GasUnit; + /// Sender of the transaction. fn sender(&self) -> &Self::Address; @@ -311,6 +316,17 @@ pub trait Module { ) -> Result { unreachable!() } + + /// Attempts to charge the provided amount of gas from the working set. + /// + /// The scalar gas value will be computed from the price defined on the working set. + fn charge_gas( + &self, + working_set: &mut WorkingSet, + gas: &::GasUnit, + ) -> anyhow::Result<()> { + working_set.charge_gas(gas) + } } /// A [`Module`] that has a well-defined and known [JSON @@ -327,6 +343,7 @@ pub trait ModuleCallJsonSchema: Module { /// Every module has to implement this trait. pub trait ModuleInfo { + /// Execution context. type Context: Context; /// Returns address of the module. diff --git a/module-system/sov-modules-api/src/state/scratchpad.rs b/module-system/sov-modules-api/src/state/scratchpad.rs index 11c32f281..62070c9d0 100644 --- a/module-system/sov-modules-api/src/state/scratchpad.rs +++ b/module-system/sov-modules-api/src/state/scratchpad.rs @@ -7,6 +7,7 @@ use sov_state::codec::{EncodeKeyLike, StateCodec, StateValueCodec}; use sov_state::storage::{Storage, StorageKey, StorageValue}; use sov_state::{OrderedReadsAndWrites, Prefix, StorageInternalCache}; +use crate::gas::GasMeter; use crate::{Context, Spec}; /// A working set accumulates reads and writes on top of the underlying DB, @@ -145,6 +146,7 @@ impl StateCheckpoint { delta: RevertableWriter::new(self.delta), accessory_delta: RevertableWriter::new(self.accessory_delta), events: Default::default(), + gas_meter: GasMeter::default(), } } @@ -181,6 +183,7 @@ pub struct WorkingSet { delta: RevertableWriter>, accessory_delta: RevertableWriter>, events: Vec, + gas_meter: GasMeter, } impl WorkingSet { @@ -249,6 +252,22 @@ impl WorkingSet { pub fn backing(&self) -> &::Storage { &self.delta.inner.inner } + + /// Returns the remaining gas funds. + pub const fn gas_remaining_funds(&self) -> u64 { + self.gas_meter.remaining_funds() + } + + /// Overrides the current gas settings with the provided values. + pub fn set_gas(&mut self, funds: u64, gas_price: C::GasUnit) { + self.gas_meter = GasMeter::new(funds, gas_price); + } + + /// Attempts to charge the provided gas unit from the gas meter, using the internal price to + /// compute the scalar value. + pub fn charge_gas(&mut self, gas: &C::GasUnit) -> anyhow::Result<()> { + self.gas_meter.charge_gas(gas) + } } impl StateReaderAndWriter for WorkingSet { diff --git a/module-system/sov-modules-macros/src/lib.rs b/module-system/sov-modules-macros/src/lib.rs index 4a52beeb5..c5bd5d1ca 100644 --- a/module-system/sov-modules-macros/src/lib.rs +++ b/module-system/sov-modules-macros/src/lib.rs @@ -36,7 +36,7 @@ use proc_macro::TokenStream; use rpc::ExposeRpcMacro; use syn::{parse_macro_input, DeriveInput, ItemFn}; -#[proc_macro_derive(ModuleInfo, attributes(state, module, address))] +#[proc_macro_derive(ModuleInfo, attributes(state, module, address, gas))] pub fn module_info(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input); diff --git a/module-system/sov-modules-macros/src/manifest.rs b/module-system/sov-modules-macros/src/manifest.rs index 601010fce..5f778620f 100644 --- a/module-system/sov-modules-macros/src/manifest.rs +++ b/module-system/sov-modules-macros/src/manifest.rs @@ -1,19 +1,18 @@ -// TODO remove once consumed -#![allow(dead_code)] - use std::path::{Path, PathBuf}; use std::{env, fmt, fs, ops}; use proc_macro2::{Ident, TokenStream}; use serde_json::Value; +use syn::{PathArguments, Type, TypePath}; #[derive(Debug, Clone)] -pub struct Manifest { +pub struct Manifest<'a> { + parent: &'a Ident, path: PathBuf, value: Value, } -impl ops::Deref for Manifest { +impl<'a> ops::Deref for Manifest<'a> { type Target = Value; fn deref(&self) -> &Self::Target { @@ -21,20 +20,24 @@ impl ops::Deref for Manifest { } } -impl Manifest { +impl<'a> Manifest<'a> { /// Parse a manifest file from a string. /// /// The provided path will be used to feedback error to the user, if any. /// /// The `parent` is used to report the errors to the correct span location. - pub fn read_str(manifest: S, path: PathBuf, parent: &Ident) -> Result + pub fn read_str(manifest: S, path: PathBuf, parent: &'a Ident) -> Result where S: AsRef, { let value = serde_json::from_str(manifest.as_ref()) .map_err(|e| Self::err(&path, parent, format!("failed to parse manifest: {e}")))?; - Ok(Self { path, value }) + Ok(Self { + parent, + path, + value, + }) } /// Reads a `constants.json` manifest file, recursing from the target directory that builds the @@ -43,7 +46,7 @@ impl Manifest { /// If the environment variable `CONSTANTS_MANIFEST` is set, it will use that instead. /// /// The `parent` is used to report the errors to the correct span location. - pub fn read_constants(parent: &Ident) -> Result { + pub fn read_constants(parent: &'a Ident) -> Result { let manifest = "constants.json"; let initial_path = match env::var("CONSTANTS_MANIFEST") { Ok(p) => PathBuf::from(&p).canonicalize().map_err(|e| { @@ -123,19 +126,18 @@ impl Manifest { /// /// The `gas` field resolution will first attempt to query `gas.parent`, and then fallback to /// `gas`. They must be objects with arrays of integers as fields. - pub fn parse_gas_config( - &self, - parent: &Ident, - ) -> Result<(Ident, TokenStream, TokenStream), syn::Error> { - let root = self + pub fn parse_gas_config(&self, ty: &Type, field: &Ident) -> Result { + let map = self .value .as_object() - .ok_or_else(|| Self::err(&self.path, parent, "manifest is not an object"))? + .ok_or_else(|| Self::err(&self.path, field, "manifest is not an object"))?; + + let root = map .get("gas") .ok_or_else(|| { Self::err( &self.path, - parent, + field, "manifest does not contain a `gas` attribute", ) })? @@ -143,87 +145,95 @@ impl Manifest { .ok_or_else(|| { Self::err( &self.path, - parent, - format!("`gas` attribute of `{}` is not an object", parent), + field, + format!("`gas` attribute of `{}` is not an object", field), ) })?; - let root = match root.get(&parent.to_string()) { + let root = match root.get(&self.parent.to_string()) { Some(Value::Object(m)) => m, Some(_) => { return Err(Self::err( &self.path, - parent, - format!( - "matching constants entry `{}` is not an object", - &parent.to_string() - ), + field, + format!("matching constants entry `{}` is not an object", field), )) } None => root, }; let mut field_values = vec![]; - let mut fields = vec![]; for (k, v) in root { let k: Ident = syn::parse_str(k).map_err(|e| { Self::err( &self.path, - parent, + field, format!("failed to parse key attribyte `{}`: {}", k, e), ) })?; - let v = v - .as_array() - .ok_or_else(|| { - Self::err( - &self.path, - parent, - format!("`{}` attribute is not an array", k), - ) - })? - .iter() - .map(|v| { - v.as_u64().ok_or_else(|| { + let v = match v { + Value::Array(a) => a + .iter() + .map(|v| match v { + Value::Bool(b) => Ok(*b as u64), + Value::Number(n) => n.as_u64().ok_or_else(|| { + Self::err( + &self.path, + field, + format!( + "the value of the field `{k}` must be an array of valid `u64`" + ), + ) + }), + _ => Err(Self::err( + &self.path, + field, + format!( + "the value of the field `{k}` must be an array of numbers, or booleans" + ), + )), + }) + .collect::>()?, + Value::Number(n) => n + .as_u64() + .ok_or_else(|| { Self::err( &self.path, - parent, - format!("`{}` attribute is not an array of integers", k), + field, + format!("the value of the field `{k}` must be a `u64`"), ) }) - }) - .collect::, _>>()?; + .map(|n| vec![n])?, + Value::Bool(b) => vec![*b as u64], - let n = v.len(); - fields.push(quote::quote!(pub #k: [u64; #n])); - field_values.push(quote::quote!(#k: [#(#v,)*])); + _ => { + return Err(Self::err( + &self.path, + field, + format!( + "the value of the field `{k}` must be an array, number, or boolean" + ), + )) + } + }; + + field_values.push(quote::quote!(#k: <<::Context as ::sov_modules_api::Context>::GasUnit as ::sov_modules_api::GasUnit>::from_arbitrary_dimensions(&[#(#v,)*]))); } - let ty = format!("{parent}GasConfig"); - let ty = syn::parse_str(&ty).map_err(|e| { - Self::err( - &self.path, - parent, - format!("failed to parse type name `{}`: {}", ty, e), - ) - })?; - - let def = quote::quote! { - #[allow(missing_docs)] - #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct #ty { - #(#fields,)* + // remove generics, if any + let mut ty = ty.clone(); + if let Type::Path(TypePath { path, .. }) = &mut ty { + if let Some(p) = path.segments.last_mut() { + p.arguments = PathArguments::None; } - }; + } - let decl = quote::quote! { - #ty { + Ok(quote::quote! { + let #field = #ty { #(#field_values,)* - } - }; - - Ok((ty, def, decl)) + }; + }) } fn err(path: P, ident: &syn::Ident, msg: T) -> syn::Error @@ -274,42 +284,54 @@ fn parse_gas_config_works() { }"#; let parent = Ident::new("Foo", proc_macro2::Span::call_site()); - let (ty, def, decl) = Manifest::read_str(input, PathBuf::from("foo.json"), &parent) + let gas_config: Type = syn::parse_str("FooGasConfig").unwrap(); + let field: Ident = syn::parse_str("foo_gas_config").unwrap(); + + let decl = Manifest::read_str(input, PathBuf::from("foo.json"), &parent) .unwrap() - .parse_gas_config(&parent) + .parse_gas_config(&gas_config, &field) .unwrap(); #[rustfmt::skip] assert_eq!( - ty.to_string(), + decl.to_string(), quote::quote!( - FooGasConfig + let foo_gas_config = FooGasConfig { + complex_math_operation: <<::Context as ::sov_modules_api::Context>::GasUnit as ::sov_modules_api::GasUnit>::from_arbitrary_dimensions(&[1u64, 2u64, 3u64, ]), + some_other_operation: <<::Context as ::sov_modules_api::Context>::GasUnit as ::sov_modules_api::GasUnit>::from_arbitrary_dimensions(&[4u64, 5u64, 6u64, ]), + }; ) .to_string() ); +} - #[rustfmt::skip] - assert_eq!( - def.to_string(), - quote::quote!( - #[allow(missing_docs)] - #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct FooGasConfig { - pub complex_math_operation: [u64; 3usize], - pub some_other_operation: [u64; 3usize], - } - ) - .to_string() - ); +#[test] +fn parse_gas_config_single_dimension_works() { + let input = r#"{ + "comment": "Sovereign SDK constants", + "gas": { + "complex_math_operation": 1, + "some_other_operation": 2 + } + }"#; + + let parent = Ident::new("Foo", proc_macro2::Span::call_site()); + let gas_config: Type = syn::parse_str("FooGasConfig").unwrap(); + let field: Ident = syn::parse_str("foo_gas_config").unwrap(); + + let decl = Manifest::read_str(input, PathBuf::from("foo.json"), &parent) + .unwrap() + .parse_gas_config(&gas_config, &field) + .unwrap(); #[rustfmt::skip] assert_eq!( decl.to_string(), quote::quote!( - FooGasConfig { - complex_math_operation: [1u64, 2u64, 3u64, ], - some_other_operation: [4u64, 5u64, 6u64, ], - } + let foo_gas_config = FooGasConfig { + complex_math_operation: <<::Context as ::sov_modules_api::Context>::GasUnit as ::sov_modules_api::GasUnit>::from_arbitrary_dimensions(&[1u64, ]), + some_other_operation: <<::Context as ::sov_modules_api::Context>::GasUnit as ::sov_modules_api::GasUnit>::from_arbitrary_dimensions(&[2u64, ]), + }; ) .to_string() ); diff --git a/module-system/sov-modules-macros/src/module_info.rs b/module-system/sov-modules-macros/src/module_info.rs index 833947824..c199fbc00 100644 --- a/module-system/sov-modules-macros/src/module_info.rs +++ b/module-system/sov-modules-macros/src/module_info.rs @@ -5,6 +5,7 @@ use syn::{ use self::parsing::{ModuleField, ModuleFieldAttribute, StructDef}; use crate::common::get_generics_type_param; +use crate::manifest::Manifest; pub(crate) fn derive_module_info( input: DeriveInput, @@ -84,6 +85,10 @@ fn impl_module_info(struct_def: &StructDef) -> Result { + impl_self_init.push(make_init_gas_config(ident, field)?); + impl_self_body.push(&field.ident); + } }; } @@ -93,7 +98,6 @@ fn impl_module_info(struct_def: &StructDef) -> Result Self { #(#impl_self_init)* @@ -213,6 +217,16 @@ fn make_init_module(field: &ModuleField) -> Result Result { + let field_ident = &field.ident; + let ty = &field.ty; + + Manifest::read_constants(parent)?.parse_gas_config(ty, field_ident) +} + fn make_module_prefix_fn(struct_ident: &Ident) -> proc_macro2::TokenStream { let body = make_module_prefix_fn_body(struct_ident); quote::quote! { @@ -270,6 +284,7 @@ pub mod parsing { let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); let fields = parse_module_fields(&input.data)?; check_exactly_one_address(&fields)?; + check_zero_or_one_gas(&fields)?; Ok(StructDef { ident, @@ -301,6 +316,7 @@ pub mod parsing { Module, State { codec_builder: Option }, Address, + Gas, } impl ModuleFieldAttribute { @@ -327,6 +343,16 @@ pub mod parsing { } } "state" => parse_state_attr(attr), + "gas" => { + if attr.tokens.is_empty() { + Ok(Self::Gas) + } else { + Err(syn::Error::new_spanned( + attr, + "The `#[gas]` attribute does not accept any arguments.", + )) + } + } _ => unreachable!("attribute names were validated already; this is a bug"), } } @@ -407,6 +433,24 @@ pub mod parsing { } } + fn check_zero_or_one_gas(fields: &[ModuleField]) -> syn::Result<()> { + let gas_fields = fields + .iter() + .filter(|field| matches!(field.attr, ModuleFieldAttribute::Gas)) + .collect::>(); + + match gas_fields.len() { + 0 | 1 => Ok(()), + _ => Err(syn::Error::new_spanned( + gas_fields[1].ident.clone(), + format!( + "The `gas` attribute is defined more than once, revisit field: {}", + gas_fields[1].ident, + ), + )), + } + } + fn data_to_struct(data: &syn::Data) -> syn::Result<&DataStruct> { match data { syn::Data::Struct(data_struct) => Ok(data_struct), @@ -433,9 +477,9 @@ pub mod parsing { let mut attr = None; for a in field.attrs.iter() { match a.path.segments[0].ident.to_string().as_str() { - "state" | "module" | "address" => { + "state" | "module" | "address" | "gas" => { if attr.is_some() { - return Err(syn::Error::new_spanned(ident, "Only one attribute out of `#[module]`, `#[state]` and `#[address]` is allowed per field.")); + return Err(syn::Error::new_spanned(ident, "Only one attribute out of `#[module]`, `#[state]`, `#[address]`, and #[gas] is allowed per field.")); } else { attr = Some(a); } @@ -449,7 +493,7 @@ pub mod parsing { } else { Err(syn::Error::new_spanned( ident, - format!("The field `{}` is missing an attribute: add `#[module]`, `#[state]` or `#[address]`.", ident), + format!("The field `{}` is missing an attribute: add `#[module]`, `#[state]`, `#[address]`, or #[gas].", ident), )) } } diff --git a/module-system/sov-modules-macros/tests/module_info/field_missing_attribute.stderr b/module-system/sov-modules-macros/tests/module_info/field_missing_attribute.stderr index 4cdbb45b5..6045d7148 100644 --- a/module-system/sov-modules-macros/tests/module_info/field_missing_attribute.stderr +++ b/module-system/sov-modules-macros/tests/module_info/field_missing_attribute.stderr @@ -1,4 +1,4 @@ -error: The field `test_state1` is missing an attribute: add `#[module]`, `#[state]` or `#[address]`. +error: The field `test_state1` is missing an attribute: add `#[module]`, `#[state]`, `#[address]`, or #[gas]. --> tests/module_info/field_missing_attribute.rs:8:5 | 8 | test_state1: StateMap, From e1bc28c9710559e135a7e4ca7f9437c08323bfd0 Mon Sep 17 00:00:00 2001 From: Blazej Kolad Date: Fri, 29 Sep 2023 15:08:03 +0200 Subject: [PATCH 33/34] Read `accounts` genesis from a file. (#959) * PrivateKeyHex in accounts * Add test_config_serialization * Add accounts.json * fix ci * fix CI * cleanup * Arbitrary PublicKeyHex * PublicKeyHex impl --- examples/demo-stf/src/genesis_config.rs | 14 +++++++--- examples/test-data/genesis/accounts.json | 3 ++ fuzz/fuzz_targets/accounts_call.rs | 2 +- .../sov-accounts/src/genesis.rs | 7 +++-- .../sov-accounts/src/lib.rs | 21 ++++++-------- .../sov-accounts/src/tests.rs | 28 +++++++++++++++++-- .../sov-modules-api/src/default_signature.rs | 2 +- module-system/sov-modules-api/src/lib.rs | 3 +- .../sov-modules-api/src/pub_key_hex.rs | 20 +++++++++---- 9 files changed, 68 insertions(+), 32 deletions(-) create mode 100644 examples/test-data/genesis/accounts.json diff --git a/examples/demo-stf/src/genesis_config.rs b/examples/demo-stf/src/genesis_config.rs index f0fe2b110..244321602 100644 --- a/examples/demo-stf/src/genesis_config.rs +++ b/examples/demo-stf/src/genesis_config.rs @@ -1,6 +1,7 @@ use anyhow::Context as AnyhowContext; #[cfg(feature = "experimental")] use reth_primitives::Bytes; +use sov_accounts::AccountConfig; use sov_chain_state::ChainStateConfig; use sov_cli::wallet_state::PrivateKeyAndAddress; #[cfg(feature = "experimental")] @@ -85,12 +86,17 @@ fn create_genesis_config( let value_setter_genesis_path = "../test-data/genesis/value_setter.json"; let value_setter_data = std::fs::read_to_string(value_setter_genesis_path) .with_context(|| format!("Failed to read genesis from {}", value_setter_genesis_path))?; - - let nft_config = sov_nft_module::NonFungibleTokenConfig {}; - let value_setter_config: ValueSetterConfig = serde_json::from_str(&value_setter_data) .with_context(|| format!("Failed to parse genesis from {}", value_setter_genesis_path))?; + let accounts_genesis_path = "../test-data/genesis/accounts.json"; + let accounts_data = std::fs::read_to_string(accounts_genesis_path) + .with_context(|| format!("Failed to read genesis from {}", accounts_genesis_path))?; + + let accounts_config: AccountConfig = serde_json::from_str(&accounts_data) + .with_context(|| format!("Failed to parse genesis from {}", accounts_genesis_path))?; + + let nft_config = sov_nft_module::NonFungibleTokenConfig {}; // This will be read from a file: #872 let chain_state_config = ChainStateConfig { // TODO: Put actual value @@ -104,7 +110,7 @@ fn create_genesis_config( (), chain_state_config, value_setter_config, - sov_accounts::AccountConfig { pub_keys: vec![] }, + accounts_config, #[cfg(feature = "experimental")] get_evm_config(evm_genesis_addresses), nft_config, diff --git a/examples/test-data/genesis/accounts.json b/examples/test-data/genesis/accounts.json new file mode 100644 index 000000000..3a134d04d --- /dev/null +++ b/examples/test-data/genesis/accounts.json @@ -0,0 +1,3 @@ +{ + "pub_keys":[] +} \ No newline at end of file diff --git a/fuzz/fuzz_targets/accounts_call.rs b/fuzz/fuzz_targets/accounts_call.rs index df64afe76..ceec6c61e 100644 --- a/fuzz/fuzz_targets/accounts_call.rs +++ b/fuzz/fuzz_targets/accounts_call.rs @@ -42,7 +42,7 @@ fuzz_target!(|input: (u16, [u8; 32], Vec)| -> Corpus { let storage = ::Storage::with_path(tmpdir.path()).unwrap(); let working_set = &mut WorkingSet::new(storage); - let config: AccountConfig = keys.iter().map(|k| k.pub_key()).collect(); + let config: AccountConfig = keys.iter().map(|k| k.pub_key()).collect(); let accounts: Accounts = Accounts::default(); accounts.genesis(&config, working_set).unwrap(); diff --git a/module-system/module-implementations/sov-accounts/src/genesis.rs b/module-system/module-implementations/sov-accounts/src/genesis.rs index 9d9bb8f5d..5b80b9857 100644 --- a/module-system/module-implementations/sov-accounts/src/genesis.rs +++ b/module-system/module-implementations/sov-accounts/src/genesis.rs @@ -9,12 +9,13 @@ impl Accounts { config: &::Config, working_set: &mut WorkingSet, ) -> Result<()> { - for pub_key in config.pub_keys.iter() { - if self.accounts.get(pub_key, working_set).is_some() { + for pub_key_hex in config.pub_keys.iter() { + let pub_key = pub_key_hex.try_into()?; + if self.accounts.get(&pub_key, working_set).is_some() { bail!("Account already exists") } - self.create_default_account(pub_key.clone(), working_set)?; + self.create_default_account(pub_key, working_set)?; } Ok(()) diff --git a/module-system/module-implementations/sov-accounts/src/lib.rs b/module-system/module-implementations/sov-accounts/src/lib.rs index 922fce9f4..7fa0a1e66 100644 --- a/module-system/module-implementations/sov-accounts/src/lib.rs +++ b/module-system/module-implementations/sov-accounts/src/lib.rs @@ -12,18 +12,17 @@ pub use query::*; mod tests; pub use call::{CallMessage, UPDATE_ACCOUNT_MSG}; -use sov_modules_api::{Context, Error, ModuleInfo, WorkingSet}; +use sov_modules_api::{Context, Error, ModuleInfo, PublicKeyHex, WorkingSet}; /// Initial configuration for sov-accounts module. #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C::PublicKey: serde::Serialize + serde::de::DeserializeOwned")] -pub struct AccountConfig { +pub struct AccountConfig { /// Public keys to initialize the rollup. - pub pub_keys: Vec, + pub pub_keys: Vec, } -impl FromIterator for AccountConfig { - fn from_iter>(iter: T) -> Self { +impl FromIterator for AccountConfig { + fn from_iter>(iter: T) -> Self { Self { pub_keys: iter.into_iter().collect(), } @@ -59,7 +58,7 @@ pub struct Accounts { impl sov_modules_api::Module for Accounts { type Context = C; - type Config = AccountConfig; + type Config = AccountConfig; type CallMessage = call::CallMessage; @@ -95,11 +94,7 @@ where } #[cfg(feature = "arbitrary")] -impl<'a, C> arbitrary::Arbitrary<'a> for AccountConfig -where - C: Context, - C::PublicKey: arbitrary::Arbitrary<'a>, -{ +impl<'a> arbitrary::Arbitrary<'a> for AccountConfig { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { // TODO we might want a dedicated struct that will generate the private key counterpart so // payloads can be signed and verified @@ -123,7 +118,7 @@ where ) -> arbitrary::Result { use sov_modules_api::Module; - let config: AccountConfig = u.arbitrary()?; + let config: AccountConfig = u.arbitrary()?; let accounts = Accounts::default(); accounts diff --git a/module-system/module-implementations/sov-accounts/src/tests.rs b/module-system/module-implementations/sov-accounts/src/tests.rs index c57be2c65..9b6804cba 100644 --- a/module-system/module-implementations/sov-accounts/src/tests.rs +++ b/module-system/module-implementations/sov-accounts/src/tests.rs @@ -1,5 +1,8 @@ +use std::str::FromStr; + use sov_modules_api::default_context::DefaultContext; use sov_modules_api::default_signature::private_key::DefaultPrivateKey; +use sov_modules_api::default_signature::DefaultPublicKey; use sov_modules_api::{AddressBech32, Context, Module, PrivateKey, PublicKey, Spec, WorkingSet}; use sov_state::ProverStorage; @@ -7,15 +10,34 @@ use crate::query::{self, Response}; use crate::{call, AccountConfig, Accounts}; type C = DefaultContext; +#[test] +fn test_config_serialization() { + let pub_key = &DefaultPublicKey::from_str( + "1cd4e2d9d5943e6f3d12589d31feee6bb6c11e7b8cd996a393623e207da72cbf", + ) + .unwrap(); + + let config = AccountConfig { + pub_keys: vec![pub_key.clone().try_into().unwrap()], + }; + + let data = r#" + { + "pub_keys":["1cd4e2d9d5943e6f3d12589d31feee6bb6c11e7b8cd996a393623e207da72cbf"] + }"#; + + let parsed_config: AccountConfig = serde_json::from_str(data).unwrap(); + assert_eq!(parsed_config, config); +} + #[test] fn test_config_account() { let priv_key = DefaultPrivateKey::generate(); - let init_pub_key = priv_key.pub_key(); let init_pub_key_addr = init_pub_key.to_address::<::Address>(); - let account_config = AccountConfig:: { - pub_keys: vec![init_pub_key.clone()], + let account_config = AccountConfig { + pub_keys: vec![init_pub_key.clone().try_into().unwrap()], }; let accounts = &mut Accounts::::default(); diff --git a/module-system/sov-modules-api/src/default_signature.rs b/module-system/sov-modules-api/src/default_signature.rs index 48c886f49..75b11777d 100644 --- a/module-system/sov-modules-api/src/default_signature.rs +++ b/module-system/sov-modules-api/src/default_signature.rs @@ -254,7 +254,7 @@ impl FromStr for DefaultPublicKey { type Err = anyhow::Error; fn from_str(s: &str) -> Result { - let pk_hex = crate::pub_key_hex::PublicKeyHex::try_from(s)?; + let pk_hex = &crate::pub_key_hex::PublicKeyHex::try_from(s)?; pk_hex.try_into() } } diff --git a/module-system/sov-modules-api/src/lib.rs b/module-system/sov-modules-api/src/lib.rs index 38ec31cfd..22d430ab9 100644 --- a/module-system/sov-modules-api/src/lib.rs +++ b/module-system/sov-modules-api/src/lib.rs @@ -28,8 +28,8 @@ pub mod transaction; #[cfg(feature = "native")] pub mod utils; +pub use pub_key_hex::PublicKeyHex; pub use state::*; - #[cfg(feature = "macros")] extern crate sov_modules_macros; @@ -166,6 +166,7 @@ pub trait PublicKey: + Sync + Serialize + for<'a> Deserialize<'a> + + for<'a> TryFrom<&'a PublicKeyHex, Error = anyhow::Error> { fn to_address(&self) -> A; } diff --git a/module-system/sov-modules-api/src/pub_key_hex.rs b/module-system/sov-modules-api/src/pub_key_hex.rs index 3c5d390c5..07667ca3c 100644 --- a/module-system/sov-modules-api/src/pub_key_hex.rs +++ b/module-system/sov-modules-api/src/pub_key_hex.rs @@ -63,11 +63,11 @@ impl From for PublicKeyHex { } } -impl TryFrom for DefaultPublicKey { +impl TryFrom<&PublicKeyHex> for DefaultPublicKey { type Error = anyhow::Error; - fn try_from(pub_key: PublicKeyHex) -> Result { - let bytes = hex::decode(pub_key.hex)?; + fn try_from(pub_key: &PublicKeyHex) -> Result { + let bytes = hex::decode(&pub_key.hex)?; let bytes: [u8; PUBLIC_KEY_LENGTH] = bytes .try_into() @@ -90,7 +90,7 @@ mod tests { fn test_pub_key_hex() { let pub_key = DefaultPrivateKey::generate().pub_key(); let pub_key_hex = PublicKeyHex::try_from(pub_key.clone()).unwrap(); - let converted_pub_key = DefaultPublicKey::try_from(pub_key_hex).unwrap(); + let converted_pub_key = DefaultPublicKey::try_from(&pub_key_hex).unwrap(); assert_eq!(pub_key, converted_pub_key); } @@ -100,8 +100,8 @@ mod tests { let pub_key_hex_lower: PublicKeyHex = key.try_into().unwrap(); let pub_key_hex_upper: PublicKeyHex = key.to_uppercase().try_into().unwrap(); - let pub_key_lower = DefaultPublicKey::try_from(pub_key_hex_lower).unwrap(); - let pub_key_upper = DefaultPublicKey::try_from(pub_key_hex_upper).unwrap(); + let pub_key_lower = DefaultPublicKey::try_from(&pub_key_hex_lower).unwrap(); + let pub_key_upper = DefaultPublicKey::try_from(&pub_key_hex_upper).unwrap(); assert_eq!(pub_key_lower, pub_key_upper) } @@ -122,3 +122,11 @@ mod tests { assert_eq!(err.to_string(), "Bad hex conversion: odd input length") } } + +#[cfg(feature = "arbitrary")] +impl<'a> arbitrary::Arbitrary<'a> for PublicKeyHex { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + let hex: String = hex::encode(String::arbitrary(u)?); + Ok(PublicKeyHex::try_from(hex).unwrap()) + } +} From 564ee3134e2c2bd5cb54dd502d09ff80e0687baf Mon Sep 17 00:00:00 2001 From: dubbelosix Date: Fri, 29 Sep 2023 19:36:03 +0530 Subject: [PATCH 34/34] fix a wrong merge conflict --- examples/demo-prover/methods/guest-celestia/Cargo.lock | 5 +++++ examples/demo-prover/methods/guest-mock/Cargo.lock | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/examples/demo-prover/methods/guest-celestia/Cargo.lock b/examples/demo-prover/methods/guest-celestia/Cargo.lock index b4574b135..da5807d78 100644 --- a/examples/demo-prover/methods/guest-celestia/Cargo.lock +++ b/examples/demo-prover/methods/guest-celestia/Cargo.lock @@ -1854,3 +1854,8 @@ dependencies = [ "quote", "syn 2.0.37", ] + +[[patch.unused]] +name = "cc" +version = "1.0.79" +source = "git+https://github.com/rust-lang/cc-rs?rev=e5bbdfa#e5bbdfa1fa468c028cb38fee6c35a3cf2e5a2736" diff --git a/examples/demo-prover/methods/guest-mock/Cargo.lock b/examples/demo-prover/methods/guest-mock/Cargo.lock index d4302d1f0..db5de049c 100644 --- a/examples/demo-prover/methods/guest-mock/Cargo.lock +++ b/examples/demo-prover/methods/guest-mock/Cargo.lock @@ -1272,3 +1272,8 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[patch.unused]] +name = "cc" +version = "1.0.79" +source = "git+https://github.com/rust-lang/cc-rs?rev=e5bbdfa#e5bbdfa1fa468c028cb38fee6c35a3cf2e5a2736"