From 3c119640966f8668434543c6d4af3d2f14378d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Delabrouille?= <34384633+tdelabro@users.noreply.github.com> Date: Mon, 29 Jan 2024 12:00:35 +0100 Subject: [PATCH] feat: on store sierra contract class in madara backend db (#1409) Co-authored-by: Lucas @ StarkWare <70894690+LucasLvy@users.noreply.github.com> --- CHANGELOG.md | 3 ++ Cargo.lock | 4 +- Cargo.toml | 1 - crates/client/data-availability/src/lib.rs | 3 +- crates/client/db/Cargo.toml | 8 ++-- crates/client/db/src/da_db.rs | 40 ++++++++++--------- crates/client/db/src/error.rs | 6 ++- crates/client/db/src/lib.rs | 15 ++++++- crates/client/db/src/mapping_db.rs | 28 ++++++------- crates/client/db/src/messaging_db.rs | 2 +- crates/client/db/src/meta_db.rs | 12 +++--- crates/client/db/src/sierra_classes_db.rs | 38 ++++++++++++++++++ crates/client/mapping-sync/Cargo.toml | 1 + crates/client/mapping-sync/src/sync_blocks.rs | 35 ++++++++-------- crates/client/rpc-core/Cargo.toml | 1 + crates/client/rpc-core/src/utils.rs | 39 ++++++++++++++++++ crates/client/rpc/src/lib.rs | 24 +++++++++-- .../client/rpc/src/madara_backend_client.rs | 2 +- crates/client/storage/Cargo.toml | 2 +- crates/client/storage/src/lib.rs | 2 +- .../src/overrides/schema_v1_override.rs | 2 +- 21 files changed, 195 insertions(+), 73 deletions(-) create mode 100644 crates/client/db/src/sierra_classes_db.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index e443515aa7..5e60f64e38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Next release +- feat(client): on `add_declare_transaction` store sierra contract classes in + the madara backend +- chore: use struct error in client/db - fix: don't ignore Sierra to CASM mapping in genesis config - refacto: early exit txs fee estimation when one fails - dev: fix linter warning in README.md diff --git a/Cargo.lock b/Cargo.lock index 97b3e902b0..83935b63a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6386,6 +6386,7 @@ dependencies = [ name = "mc-mapping-sync" version = "0.6.0" dependencies = [ + "anyhow", "futures", "futures-timer", "log", @@ -6462,6 +6463,7 @@ dependencies = [ "flate2", "frame-support", "hex", + "indexmap 2.0.0-pre", "jsonrpsee 0.16.3", "mp-block", "mp-digest-log", @@ -12244,7 +12246,7 @@ dependencies = [ [[package]] name = "starknet_api" version = "0.4.1" -source = "git+https://github.com/keep-starknet-strange/starknet-api?branch=no_std-support-dc83f05#3415c832bb0cd72d1764dcd9ce803ddf932895ef" +source = "git+https://github.com/keep-starknet-strange/starknet-api?branch=no_std-support-dc83f05#c76154572057fb53b31f3a29a24e2469ca721d51" dependencies = [ "cairo-lang-casm-contract-class", "derive_more", diff --git a/Cargo.toml b/Cargo.toml index c8080ad70f..1b66c058d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -236,7 +236,6 @@ starknet-core-contract-client = { git = "https://github.com/keep-starknet-strang # Other third party dependencies anyhow = "1.0.75" flate2 = "1.0.28" -scale-codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } parity-scale-codec = { version = "3.2.2", default-features = false } scale-info = { version = "2.10.0", default-features = false } lazy_static = { version = "1.4.0", default-features = false } diff --git a/crates/client/data-availability/src/lib.rs b/crates/client/data-availability/src/lib.rs index f33071a3c8..9b0eb12570 100644 --- a/crates/client/data-availability/src/lib.rs +++ b/crates/client/data-availability/src/lib.rs @@ -236,9 +236,10 @@ pub async fn update_state( log::info!("validity da mode not implemented"); } DaMode::Sovereign => match madara_backend.da().state_diff(&block_hash) { - Ok(state_diff) => { + Ok(Some(state_diff)) => { da_client.publish_state_diff(state_diff).await.map_err(|e| anyhow!("DA PUBLISH ERROR: {e}"))?; } + Ok(None) => Err(anyhow!("there is no state diff stored for block {}", block_hash))?, Err(e) => Err(anyhow!("could not pull state diff for block {}: {}", block_hash, e))?, }, DaMode::Volition => log::info!("volition da mode not implemented"), diff --git a/crates/client/db/Cargo.toml b/crates/client/db/Cargo.toml index 6277bedea6..beb29f26ce 100644 --- a/crates/client/db/Cargo.toml +++ b/crates/client/db/Cargo.toml @@ -20,14 +20,16 @@ ethers = { workspace = true } kvdb-rocksdb = { version = "0.19.0", optional = true } log = { workspace = true, default-features = true } parity-db = { version = "0.4.12", optional = true } -sc-client-db = { workspace = true, default-features = true } -scale-codec = { workspace = true, default-features = true, features = [ +parity-scale-codec = { workspace = true, default-features = true, features = [ "derive", ] } +sc-client-db = { workspace = true, default-features = true } sp-core = { workspace = true, default-features = true } sp-database = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } -starknet_api = { workspace = true, default-features = true } +starknet_api = { workspace = true, default-features = true, features = [ + "parity-scale-codec", +] } thiserror = { workspace = true } uuid = "1.4.1" diff --git a/crates/client/db/src/da_db.rs b/crates/client/db/src/da_db.rs index 132fd098a1..2fe20018a5 100644 --- a/crates/client/db/src/da_db.rs +++ b/crates/client/db/src/da_db.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use ethers::types::U256; // Substrate -use scale_codec::{Decode, Encode}; +use parity_scale_codec::{Decode, Encode}; use sp_database::Database; use sp_runtime::traits::Block as BlockT; // Starknet @@ -11,7 +11,7 @@ use starknet_api::block::BlockHash; use starknet_api::hash::StarkFelt; use uuid::Uuid; -use crate::DbHash; +use crate::{DbError, DbHash}; // The fact db stores DA facts that need to be written to L1 pub struct DaDb { @@ -21,56 +21,60 @@ pub struct DaDb { // TODO: purge old cairo job keys impl DaDb { - pub fn state_diff(&self, block_hash: &BlockHash) -> Result, String> { + pub fn state_diff(&self, block_hash: &BlockHash) -> Result>, DbError> { match self.db.get(crate::columns::DA, block_hash.0.bytes()) { - Some(raw) => Ok(Vec::::decode(&mut &raw[..]).map_err(|e| format!("{:?}", e))?), - None => Err(String::from("can't write state diff")), + Some(raw) => Ok(Some(Vec::::decode(&mut &raw[..])?)), + None => Ok(None), } } - pub fn store_state_diff(&self, block_hash: &BlockHash, diff: Vec) -> Result<(), String> { + pub fn store_state_diff(&self, block_hash: &BlockHash, diff: Vec) -> Result<(), DbError> { let mut transaction = sp_database::Transaction::new(); transaction.set(crate::columns::DA, block_hash.0.bytes(), &diff.encode()); - self.db.commit(transaction).map_err(|e| format!("{:?}", e))?; + self.db.commit(transaction)?; Ok(()) } - pub fn cairo_job(&self, block_hash: &BlockHash) -> Result { + pub fn cairo_job(&self, block_hash: &BlockHash) -> Result, DbError> { match self.db.get(crate::columns::DA, block_hash.0.bytes()) { - Some(raw) => Ok(Uuid::from_slice(&raw[..]).map_err(|e| format!("{:?}", e))?), - None => Err(String::from("can't locate cairo job")), + Some(raw) => Ok(Some(Uuid::from_slice(&raw[..])?)), + None => Ok(None), } } - pub fn update_cairo_job(&self, block_hash: &BlockHash, job_id: Uuid) -> Result<(), String> { + pub fn update_cairo_job(&self, block_hash: &BlockHash, job_id: Uuid) -> Result<(), DbError> { let mut transaction = sp_database::Transaction::new(); transaction.set(crate::columns::DA, block_hash.0.bytes(), &job_id.into_bytes()); - self.db.commit(transaction).map_err(|e| format!("{:?}", e))?; + self.db.commit(transaction)?; Ok(()) } - pub fn last_proved_block(&self) -> Result { + pub fn last_proved_block(&self) -> Result { match self.db.get(crate::columns::DA, crate::static_keys::LAST_PROVED_BLOCK) { Some(raw) => { - let felt = StarkFelt::deserialize(&raw[..]).ok_or("Failed to deserialize block hash")?; + let felt = StarkFelt::decode(&mut &raw[..])?; Ok(BlockHash(felt)) } - None => Err(String::from("can't locate last proved block")), + None => Err(DbError::ValueNotInitialized( + crate::columns::DA, + // Safe coze `LAST_PROVED_BLOCK` is utf8 + unsafe { std::str::from_utf8_unchecked(crate::static_keys::LAST_PROVED_BLOCK) }.to_string(), + )), } } - pub fn update_last_proved_block(&self, block_hash: &BlockHash) -> Result<(), String> { + pub fn update_last_proved_block(&self, block_hash: &BlockHash) -> Result<(), DbError> { let mut transaction = sp_database::Transaction::new(); - transaction.set(crate::columns::DA, crate::static_keys::LAST_PROVED_BLOCK, block_hash.0.bytes()); + transaction.set(crate::columns::DA, crate::static_keys::LAST_PROVED_BLOCK, &block_hash.0.encode()); - self.db.commit(transaction).map_err(|e| format!("{:?}", e))?; + self.db.commit(transaction)?; Ok(()) } diff --git a/crates/client/db/src/error.rs b/crates/client/db/src/error.rs index 90753eb47f..85bb66de2f 100644 --- a/crates/client/db/src/error.rs +++ b/crates/client/db/src/error.rs @@ -3,5 +3,9 @@ pub enum DbError { #[error("Failed to commit DB Update: `{0}`")] CommitError(#[from] sp_database::error::DatabaseError), #[error("Failed to deserialize DB Data: `{0}`")] - DeserializeError(#[from] scale_codec::Error), + DeserializeError(#[from] parity_scale_codec::Error), + #[error("Failed to build Uuid: `{0}`")] + Uuid(#[from] uuid::Error), + #[error("A value was queryied that was not initialized at column: `{0}` key: `{1}`")] + ValueNotInitialized(u32, String), } diff --git a/crates/client/db/src/lib.rs b/crates/client/db/src/lib.rs index b6d09b6e12..cfc794e53c 100644 --- a/crates/client/db/src/lib.rs +++ b/crates/client/db/src/lib.rs @@ -16,10 +16,12 @@ pub use error::DbError; mod mapping_db; pub use mapping_db::MappingCommitment; +use sierra_classes_db::SierraClassesDb; use starknet_api::hash::StarkHash; mod da_db; mod db_opening_utils; mod messaging_db; +mod sierra_classes_db; pub use messaging_db::LastSyncedEventBlock; mod meta_db; @@ -49,13 +51,14 @@ pub(crate) mod columns { // ===== /!\ =================================================================================== // MUST BE INCREMENTED WHEN A NEW COLUMN IN ADDED // ===== /!\ =================================================================================== - pub const NUM_COLUMNS: u32 = 7; + pub const NUM_COLUMNS: u32 = 8; pub const META: u32 = 0; pub const BLOCK_MAPPING: u32 = 1; pub const TRANSACTION_MAPPING: u32 = 2; pub const SYNCED_MAPPING: u32 = 3; pub const DA: u32 = 4; + /// This column is used to map starknet block hashes to a list of transaction hashes that are /// contained in the block. /// @@ -64,6 +67,9 @@ pub(crate) mod columns { /// This column contains last synchronized L1 block. pub const MESSAGING: u32 = 6; + + /// This column contains the Sierra contract classes + pub const SIERRA_CONTRACT_CLASSES: u32 = 7; } pub mod static_keys { @@ -82,6 +88,7 @@ pub struct Backend { mapping: Arc>, da: Arc>, messaging: Arc>, + sierra_classes: Arc>, } /// Returns the Starknet database directory. @@ -123,6 +130,7 @@ impl Backend { meta: Arc::new(MetaDb { db: db.clone(), _marker: PhantomData }), da: Arc::new(DaDb { db: db.clone(), _marker: PhantomData }), messaging: Arc::new(MessagingDb { db: db.clone(), _marker: PhantomData }), + sierra_classes: Arc::new(SierraClassesDb { db: db.clone(), _marker: PhantomData }), }) } @@ -146,6 +154,11 @@ impl Backend { &self.messaging } + /// Return the sierra classes database manager + pub fn sierra_classes(&self) -> &Arc> { + &self.sierra_classes + } + /// In the future, we will compute the block global state root asynchronously in the client, /// using the Starknet-Bonzai-trie. /// That what replaces it for now :) diff --git a/crates/client/db/src/mapping_db.rs b/crates/client/db/src/mapping_db.rs index f8b33642f2..4b84e373c6 100644 --- a/crates/client/db/src/mapping_db.rs +++ b/crates/client/db/src/mapping_db.rs @@ -2,12 +2,12 @@ use std::marker::PhantomData; use std::sync::{Arc, Mutex}; // Substrate -use scale_codec::{Decode, Encode}; +use parity_scale_codec::{Decode, Encode}; use sp_core::H256; use sp_database::Database; use sp_runtime::traits::Block as BlockT; -use crate::DbHash; +use crate::{DbError, DbHash}; /// The mapping to write in db #[derive(Debug)] @@ -33,9 +33,9 @@ impl MappingDb { } /// Check if the given block hash has already been processed - pub fn is_synced(&self, block_hash: &B::Hash) -> Result { + pub fn is_synced(&self, block_hash: &B::Hash) -> Result { match self.db.get(crate::columns::SYNCED_MAPPING, &block_hash.encode()) { - Some(raw) => Ok(bool::decode(&mut &raw[..]).map_err(|e| format!("{:?}", e))?), + Some(raw) => Ok(bool::decode(&mut &raw[..])?), None => Ok(false), } } @@ -44,28 +44,28 @@ impl MappingDb { /// /// Under some circumstances it can return multiples blocks hashes, meaning that the result has /// to be checked against the actual blockchain state in order to find the good one. - pub fn block_hash(&self, starknet_block_hash: &H256) -> Result>, String> { + pub fn block_hash(&self, starknet_block_hash: &H256) -> Result>, DbError> { match self.db.get(crate::columns::BLOCK_MAPPING, &starknet_block_hash.encode()) { - Some(raw) => Ok(Some(Vec::::decode(&mut &raw[..]).map_err(|e| format!("{:?}", e))?)), + Some(raw) => Ok(Some(Vec::::decode(&mut &raw[..])?)), None => Ok(None), } } /// Register that a Substrate block has been seen, without it containing a Starknet one - pub fn write_none(&self, block_hash: B::Hash) -> Result<(), String> { + pub fn write_none(&self, block_hash: B::Hash) -> Result<(), DbError> { let _lock = self.write_lock.lock(); let mut transaction = sp_database::Transaction::new(); transaction.set(crate::columns::SYNCED_MAPPING, &block_hash.encode(), &true.encode()); - self.db.commit(transaction).map_err(|e| format!("{:?}", e))?; + self.db.commit(transaction)?; Ok(()) } /// Register that a Substate block has been seen and map it to the Statknet block it contains - pub fn write_hashes(&self, commitment: MappingCommitment) -> Result<(), String> { + pub fn write_hashes(&self, commitment: MappingCommitment) -> Result<(), DbError> { let _lock = self.write_lock.lock(); let mut transaction = sp_database::Transaction::new(); @@ -108,7 +108,7 @@ impl MappingDb { ); } - self.db.commit(transaction).map_err(|e| format!("{:?}", e))?; + self.db.commit(transaction)?; Ok(()) } @@ -121,9 +121,9 @@ impl MappingDb { /// * `transaction_hash` - the transaction hash to search for. H256 is used here because it's a /// native type of substrate, and we are sure it's SCALE encoding is optimized and will not /// change. - pub fn block_hash_from_transaction_hash(&self, transaction_hash: H256) -> Result, String> { + pub fn block_hash_from_transaction_hash(&self, transaction_hash: H256) -> Result, DbError> { match self.db.get(crate::columns::TRANSACTION_MAPPING, &transaction_hash.encode()) { - Some(raw) => Ok(Some(::decode(&mut &raw[..]).map_err(|e| format!("{:?}", e))?)), + Some(raw) => Ok(Some(::decode(&mut &raw[..])?)), None => Ok(None), } } @@ -142,14 +142,14 @@ impl MappingDb { /// /// - The cache is disabled. /// - The provided `starknet_hash` is not present in the cache. - pub fn cached_transaction_hashes_from_block_hash(&self, starknet_hash: H256) -> Result>, String> { + pub fn cached_transaction_hashes_from_block_hash(&self, starknet_hash: H256) -> Result>, DbError> { if !self.cache_more_things { // The cache is not enabled, no need to even touch the database. return Ok(None); } match self.db.get(crate::columns::STARKNET_TRANSACTION_HASHES_CACHE, &starknet_hash.encode()) { - Some(raw) => Ok(Some(Vec::::decode(&mut &raw[..]).map_err(|e| format!("{:?}", e))?)), + Some(raw) => Ok(Some(Vec::::decode(&mut &raw[..])?)), None => Ok(None), } } diff --git a/crates/client/db/src/messaging_db.rs b/crates/client/db/src/messaging_db.rs index 5c22b59d3c..c824b9506c 100644 --- a/crates/client/db/src/messaging_db.rs +++ b/crates/client/db/src/messaging_db.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use std::sync::Arc; // Substrate -use scale_codec::{Decode, Encode}; +use parity_scale_codec::{Decode, Encode}; use sp_database::Database; use sp_runtime::traits::Block as BlockT; diff --git a/crates/client/db/src/meta_db.rs b/crates/client/db/src/meta_db.rs index 24c222b7e0..4ae4f3f4c7 100644 --- a/crates/client/db/src/meta_db.rs +++ b/crates/client/db/src/meta_db.rs @@ -2,11 +2,11 @@ use std::marker::PhantomData; use std::sync::Arc; // Substrate -use scale_codec::{Decode, Encode}; +use parity_scale_codec::{Decode, Encode}; use sp_database::Database; use sp_runtime::traits::Block as BlockT; -use crate::DbHash; +use crate::{DbError, DbHash}; /// Allow interaction with the meta db /// @@ -19,20 +19,20 @@ pub struct MetaDb { impl MetaDb { /// Retrieve the current tips of the synced chain - pub fn current_syncing_tips(&self) -> Result, String> { + pub fn current_syncing_tips(&self) -> Result, DbError> { match self.db.get(crate::columns::META, crate::static_keys::CURRENT_SYNCING_TIPS) { - Some(raw) => Ok(Vec::::decode(&mut &raw[..]).map_err(|e| format!("{:?}", e))?), + Some(raw) => Ok(Vec::::decode(&mut &raw[..])?), None => Ok(Vec::new()), } } /// Store the current tips of the synced chain - pub fn write_current_syncing_tips(&self, tips: Vec) -> Result<(), String> { + pub fn write_current_syncing_tips(&self, tips: Vec) -> Result<(), DbError> { let mut transaction = sp_database::Transaction::new(); transaction.set(crate::columns::META, crate::static_keys::CURRENT_SYNCING_TIPS, &tips.encode()); - self.db.commit(transaction).map_err(|e| format!("{:?}", e))?; + self.db.commit(transaction)?; Ok(()) } diff --git a/crates/client/db/src/sierra_classes_db.rs b/crates/client/db/src/sierra_classes_db.rs new file mode 100644 index 0000000000..4ee7523b78 --- /dev/null +++ b/crates/client/db/src/sierra_classes_db.rs @@ -0,0 +1,38 @@ +use std::marker::PhantomData; +use std::sync::Arc; + +use parity_scale_codec::{Decode, Encode}; +use sp_database::Database; +use sp_runtime::traits::Block as BlockT; +use starknet_api::hash::StarkFelt; +use starknet_api::state::ContractClass; + +use crate::{DbError, DbHash}; + +/// Allow interaction with the sierra classes db +pub struct SierraClassesDb { + pub(crate) db: Arc>, + pub(crate) _marker: PhantomData, +} + +impl SierraClassesDb { + pub fn store_sierra_class(&self, class_hash: StarkFelt, class: ContractClass) -> Result<(), DbError> { + let mut transaction = sp_database::Transaction::new(); + + transaction.set(crate::columns::SIERRA_CONTRACT_CLASSES, &class_hash.encode(), &class.encode()); + + self.db.commit(transaction)?; + + Ok(()) + } + + pub fn get_sierra_class(&self, class_hash: StarkFelt) -> Result, DbError> { + let opt_contract_class = self + .db + .get(crate::columns::SIERRA_CONTRACT_CLASSES, &class_hash.encode()) + .map(|raw| ContractClass::decode(&mut &raw[..])) + .transpose()?; + + Ok(opt_contract_class) + } +} diff --git a/crates/client/mapping-sync/Cargo.toml b/crates/client/mapping-sync/Cargo.toml index e914efac76..374032ead4 100644 --- a/crates/client/mapping-sync/Cargo.toml +++ b/crates/client/mapping-sync/Cargo.toml @@ -13,6 +13,7 @@ publish = false repository = "https://github.com/keep-starknet-strange/madara" [dependencies] +anyhow = { workspace = true, default-features = true } futures = { workspace = true, default-features = true } futures-timer = "3.0.2" log = { workspace = true, default-features = true } diff --git a/crates/client/mapping-sync/src/sync_blocks.rs b/crates/client/mapping-sync/src/sync_blocks.rs index e15f372f7f..f73893db30 100644 --- a/crates/client/mapping-sync/src/sync_blocks.rs +++ b/crates/client/mapping-sync/src/sync_blocks.rs @@ -9,7 +9,7 @@ use sp_blockchain::{Backend as _, HeaderBackend}; use sp_core::H256; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Zero}; -fn sync_block(client: &C, backend: &mc_db::Backend, header: &B::Header) -> Result<(), String> +fn sync_block(client: &C, backend: &mc_db::Backend, header: &B::Header) -> anyhow::Result<()> where C: HeaderBackend + StorageProvider, C: ProvideRuntimeApi, @@ -32,15 +32,12 @@ where let storage_starknet_block_hash = storage_starknet_block.header().hash::(); // Ensure the two blocks sources (chain storage and block digest) agree on the block content if digest_starknet_block_hash != storage_starknet_block_hash { - Err(format!( + Err(anyhow::anyhow!( "Starknet block hash mismatch: madara consensus digest ({digest_starknet_block_hash:?}), \ db state ({storage_starknet_block_hash:?})" )) } else { - let chain_id = client - .runtime_api() - .chain_id(substrate_block_hash) - .map_err(|_| "Failed to fetch chain_id through the runtime api")?; + let chain_id = client.runtime_api().chain_id(substrate_block_hash)?; // Success, we write the Starknet to Substate hashes mapping to db let mapping_commitment = mc_db::MappingCommitment { @@ -53,16 +50,16 @@ where .collect(), }; - backend.mapping().write_hashes(mapping_commitment) + backend.mapping().write_hashes(mapping_commitment).map_err(|e| anyhow::anyhow!(e)) } } // If there is not Starknet block in this Substrate block, we write it in the db - None => backend.mapping().write_none(substrate_block_hash), + None => backend.mapping().write_none(substrate_block_hash).map_err(|e| anyhow::anyhow!(e)), } } // If there is not Starknet block in this Substrate block, we write it in the db - Err(FindLogError::NotLog) => backend.mapping().write_none(substrate_block_hash), - Err(FindLogError::MultipleLogs) => Err("Multiple logs found".to_string()), + Err(FindLogError::NotLog) => backend.mapping().write_none(substrate_block_hash).map_err(|e| anyhow::anyhow!(e)), + Err(FindLogError::MultipleLogs) => Err(anyhow::anyhow!("Multiple logs found")), } } @@ -70,7 +67,7 @@ fn sync_genesis_block( _client: &C, backend: &mc_db::Backend, header: &B::Header, -) -> Result<(), String> +) -> anyhow::Result<()> where C: HeaderBackend, B: BlockT, @@ -80,8 +77,10 @@ where let block = match find_starknet_block(header.digest()) { Ok(block) => block, - Err(FindLogError::NotLog) => return backend.mapping().write_none(substrate_block_hash), - Err(FindLogError::MultipleLogs) => return Err("Multiple logs found".to_string()), + Err(FindLogError::NotLog) => { + return backend.mapping().write_none(substrate_block_hash).map_err(|e| anyhow::anyhow!(e)); + } + Err(FindLogError::MultipleLogs) => return Err(anyhow::anyhow!("Multiple logs found")), }; let block_hash = block.header().hash::(); let mapping_commitment = mc_db::MappingCommitment:: { @@ -100,7 +99,7 @@ fn sync_one_block( substrate_backend: &BE, madara_backend: &mc_db::Backend, sync_from: ::Number, -) -> Result +) -> anyhow::Result where C: ProvideRuntimeApi, C::Api: StarknetRuntimeApi, @@ -111,7 +110,7 @@ where let mut current_syncing_tips = madara_backend.meta().current_syncing_tips()?; if current_syncing_tips.is_empty() { - let mut leaves = substrate_backend.blockchain().leaves().map_err(|e| format!("{:?}", e))?; + let mut leaves = substrate_backend.blockchain().leaves()?; if leaves.is_empty() { return Ok(false); } @@ -155,7 +154,7 @@ pub fn sync_blocks( madara_backend: &mc_db::Backend, limit: usize, sync_from: ::Number, -) -> Result +) -> anyhow::Result where C: ProvideRuntimeApi, C::Api: StarknetRuntimeApi, @@ -177,7 +176,7 @@ fn fetch_header( madara_backend: &mc_db::Backend, checking_tip: B::Hash, sync_from: ::Number, -) -> Result, String> +) -> anyhow::Result> where BE: HeaderBackend, { @@ -188,6 +187,6 @@ where match substrate_backend.header(checking_tip) { Ok(Some(checking_header)) if checking_header.number() >= &sync_from => Ok(Some(checking_header)), Ok(Some(_)) => Ok(None), - Ok(None) | Err(_) => Err("Header not found".to_string()), + Ok(None) | Err(_) => Err(anyhow::anyhow!("Header not found")), } } diff --git a/crates/client/rpc-core/Cargo.toml b/crates/client/rpc-core/Cargo.toml index 40a47396fc..c252599162 100644 --- a/crates/client/rpc-core/Cargo.toml +++ b/crates/client/rpc-core/Cargo.toml @@ -26,6 +26,7 @@ cairo-vm = { workspace = true, default-features = false } flate2 = { workspace = true } frame-support = { workspace = true } hex = { workspace = true, default-features = true } +indexmap = { workspace = true, default-features = true } jsonrpsee = { workspace = true, features = [ "server", "macros", diff --git a/crates/client/rpc-core/src/utils.rs b/crates/client/rpc-core/src/utils.rs index 9e27dea176..0231eb5105 100644 --- a/crates/client/rpc-core/src/utils.rs +++ b/crates/client/rpc-core/src/utils.rs @@ -10,8 +10,10 @@ use cairo_lang_starknet::contract_class::{ }; use cairo_lang_starknet::contract_class_into_casm_contract_class::StarknetSierraCompilationError; use cairo_lang_utils::bigint::BigUintAsHex; +use indexmap::IndexMap; use mp_block::Block as StarknetBlock; use mp_digest_log::find_starknet_block; +use mp_felt::Felt252Wrapper; use num_bigint::{BigInt, BigUint, Sign}; use sp_api::{BlockT, HeaderT}; use sp_blockchain::HeaderBackend; @@ -117,6 +119,36 @@ pub fn flattened_sierra_to_casm_contract_class( Ok(casm_contract_class) } +pub fn flattened_sierra_to_sierra_contract_class( + flattened_sierra: Arc, +) -> starknet_api::state::ContractClass { + let mut entry_point_by_type = + IndexMap::>::with_capacity(3); + for sierra_entrypoint in flattened_sierra.entry_points_by_type.constructor.iter() { + entry_point_by_type + .entry(starknet_api::state::EntryPointType::Constructor) + .or_default() + .push(rpc_entry_point_to_starknet_api_entry_point(sierra_entrypoint)); + } + for sierra_entrypoint in flattened_sierra.entry_points_by_type.external.iter() { + entry_point_by_type + .entry(starknet_api::state::EntryPointType::External) + .or_default() + .push(rpc_entry_point_to_starknet_api_entry_point(sierra_entrypoint)); + } + for sierra_entrypoint in flattened_sierra.entry_points_by_type.l1_handler.iter() { + entry_point_by_type + .entry(starknet_api::state::EntryPointType::L1Handler) + .or_default() + .push(rpc_entry_point_to_starknet_api_entry_point(sierra_entrypoint)); + } + starknet_api::state::ContractClass { + sierra_program: flattened_sierra.sierra_program.iter().map(|f| Felt252Wrapper(*f).into()).collect(), + entry_point_by_type, + abi: flattened_sierra.abi.clone(), + } +} + /// Converts a [FieldElement] to a [BigUint] fn field_element_to_big_uint(value: &FieldElement) -> BigUint { BigInt::from_bytes_be(Sign::Plus, &value.to_bytes_be()).to_biguint().unwrap() @@ -127,6 +159,13 @@ fn field_element_to_big_uint_as_hex(value: &FieldElement) -> BigUintAsHex { BigUintAsHex { value: field_element_to_big_uint(value) } } +fn rpc_entry_point_to_starknet_api_entry_point(value: &SierraEntryPoint) -> starknet_api::state::EntryPoint { + starknet_api::state::EntryPoint { + function_idx: starknet_api::state::FunctionIndex(value.function_idx), + selector: Felt252Wrapper(value.selector).into(), + } +} + /// Converts a [EntryPointsByType] to a [ContractEntryPoints] fn entry_points_by_type_to_contract_entry_points(value: EntryPointsByType) -> ContractEntryPoints { fn sierra_entry_point_to_contract_entry_point(value: SierraEntryPoint) -> ContractEntryPoint { diff --git a/crates/client/rpc/src/lib.rs b/crates/client/rpc/src/lib.rs index 993fcfc81e..e570f594d4 100644 --- a/crates/client/rpc/src/lib.rs +++ b/crates/client/rpc/src/lib.rs @@ -268,6 +268,12 @@ where ) -> RpcResult { let best_block_hash = self.client.info().best_hash; + let opt_sierra_contract_class = if let BroadcastedDeclareTransaction::V2(ref tx) = declare_transaction { + Some(flattened_sierra_to_sierra_contract_class(tx.contract_class.clone())) + } else { + None + }; + let transaction: UserTransaction = declare_transaction.try_into().map_err(|e| { error!("Failed to convert BroadcastedDeclareTransaction to UserTransaction, error: {e}"); StarknetRpcApiError::InternalServerError @@ -294,10 +300,20 @@ where let chain_id = Felt252Wrapper(self.chain_id()?.0); - Ok(DeclareTransactionResult { - transaction_hash: transaction.compute_hash::(chain_id, false).into(), - class_hash: class_hash.0, - }) + let tx_hash = transaction.compute_hash::(chain_id, false).into(); + + if let Some(sierra_contract_class) = opt_sierra_contract_class { + if let Some(e) = self + .backend + .sierra_classes() + .store_sierra_class(Felt252Wrapper::from(class_hash.0).into(), sierra_contract_class) + .err() + { + log::error!("Failed to store the sierra contract class for declare tx `{tx_hash:x}`: {e}") + } + } + + Ok(DeclareTransactionResult { transaction_hash: tx_hash, class_hash: class_hash.0 }) } /// Add an Invoke Transaction to invoke a contract function diff --git a/crates/client/rpc/src/madara_backend_client.rs b/crates/client/rpc/src/madara_backend_client.rs index 859f9f26cd..a08ba14e32 100644 --- a/crates/client/rpc/src/madara_backend_client.rs +++ b/crates/client/rpc/src/madara_backend_client.rs @@ -8,7 +8,7 @@ use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use crate::errors::StarknetRpcApiError; -pub fn load_hash(client: &C, backend: &mc_db::Backend, hash: H256) -> Result, String> +pub fn load_hash(client: &C, backend: &mc_db::Backend, hash: H256) -> anyhow::Result> where B: BlockT, C: HeaderBackend + 'static, diff --git a/crates/client/storage/Cargo.toml b/crates/client/storage/Cargo.toml index 7652f5f001..6749879bea 100644 --- a/crates/client/storage/Cargo.toml +++ b/crates/client/storage/Cargo.toml @@ -21,8 +21,8 @@ madara-runtime = { workspace = true, features = ["std"] } mp-storage = { workspace = true, features = ["std"] } pallet-starknet = { workspace = true, features = ["std"] } pallet-starknet-runtime-api = { workspace = true, features = ["std"] } +parity-scale-codec = { workspace = true, features = ["std"] } sc-client-api = { workspace = true } -scale-codec = { workspace = true, features = ["std"] } sp-api = { workspace = true, features = ["std"] } sp-blockchain = { workspace = true } sp-core = { workspace = true, features = ["std"] } diff --git a/crates/client/storage/src/lib.rs b/crates/client/storage/src/lib.rs index 14da745ab8..a13172db34 100644 --- a/crates/client/storage/src/lib.rs +++ b/crates/client/storage/src/lib.rs @@ -14,8 +14,8 @@ use std::sync::Arc; use mp_storage::{StarknetStorageSchemaVersion, PALLET_STARKNET_SCHEMA}; pub use overrides::*; use pallet_starknet_runtime_api::StarknetRuntimeApi; +use parity_scale_codec::Decode; use sc_client_api::backend::{Backend, StorageProvider}; -use scale_codec::Decode; use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; use sp_runtime::traits::Block as BlockT; diff --git a/crates/client/storage/src/overrides/schema_v1_override.rs b/crates/client/storage/src/overrides/schema_v1_override.rs index 3205213b45..e11ef97d19 100644 --- a/crates/client/storage/src/overrides/schema_v1_override.rs +++ b/crates/client/storage/src/overrides/schema_v1_override.rs @@ -5,9 +5,9 @@ use blockifier::execution::contract_class::ContractClass; use mp_storage::{ PALLET_STARKNET, STARKNET_CONTRACT_CLASS, STARKNET_CONTRACT_CLASS_HASH, STARKNET_NONCE, STARKNET_STORAGE, }; +use parity_scale_codec::{Decode, Encode}; // Substrate use sc_client_api::backend::{Backend, StorageProvider}; -use scale_codec::{Decode, Encode}; use sp_blockchain::HeaderBackend; use sp_runtime::traits::Block as BlockT; use sp_storage::StorageKey;