Skip to content
This repository has been archived by the owner on Aug 2, 2024. It is now read-only.

Commit

Permalink
dev: impl get_state_updates by re executing transactions (#1602)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonypony220 authored May 21, 2024
1 parent 9ff0b4a commit 003a0d6
Show file tree
Hide file tree
Showing 11 changed files with 297 additions and 67 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Next release

- dev: impl get_state_updates using get_transaction_re_execution_state_diff
- feat: support strk as fee token
- dev: pallet test for estimate_fee that skip validation
- feat: add versioned constants to pallet constants
Expand Down
36 changes: 0 additions & 36 deletions crates/client/db/src/da_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ use sp_database::Database;
// Starknet
use starknet_api::block::BlockHash;
use starknet_api::hash::StarkFelt;
use starknet_api::state::ThinStateDiff;
use uuid::Uuid;

use crate::{DbError, DbHash};

Expand All @@ -18,40 +16,6 @@ pub struct DaDb {

// TODO: purge old cairo job keys
impl DaDb {
pub fn state_diff(&self, block_hash: &BlockHash) -> Result<ThinStateDiff, DbError> {
match self.db.get(crate::columns::DA, block_hash.0.bytes()) {
Some(raw) => Ok(ThinStateDiff::decode(&mut &raw[..])?),
None => Err(DbError::ValueNotInitialized(crate::columns::DA, block_hash.to_string())),
}
}

pub fn store_state_diff(&self, block_hash: &BlockHash, diff: &ThinStateDiff) -> Result<(), DbError> {
let mut transaction = sp_database::Transaction::new();

transaction.set(crate::columns::DA, block_hash.0.bytes(), &diff.encode());

self.db.commit(transaction)?;

Ok(())
}

pub fn cairo_job(&self, block_hash: &BlockHash) -> Result<Option<Uuid>, DbError> {
match self.db.get(crate::columns::DA, block_hash.0.bytes()) {
Some(raw) => Ok(Some(Uuid::from_slice(&raw[..])?)),
None => Ok(None),
}
}

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)?;

Ok(())
}

pub fn last_proved_block(&self) -> Result<BlockHash, DbError> {
match self.db.get(crate::columns::DA, crate::static_keys::LAST_PROVED_BLOCK) {
Some(raw) => {
Expand Down
31 changes: 9 additions & 22 deletions crates/client/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ use sp_blockchain::HeaderBackend;
use sp_core::H256;
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
use sp_runtime::transaction_validity::InvalidTransaction;
use starknet_api::block::BlockHash;
use starknet_api::core::Nonce;
use starknet_api::hash::StarkFelt;
use starknet_api::transaction::{Calldata, Fee, TransactionHash, TransactionVersion};
Expand All @@ -70,6 +69,7 @@ use starknet_core::types::{
TransactionExecutionStatus, TransactionFinalityStatus, TransactionReceipt,
};
use starknet_core::utils::get_selector_from_name;
use trace_api::get_previous_block_substrate_hash;

use crate::constants::{MAX_EVENTS_CHUNK_SIZE, MAX_EVENTS_KEYS};
use crate::types::RpcEventFilter;
Expand Down Expand Up @@ -206,22 +206,6 @@ where

Ok(starknet_block.header().block_number)
}

/// Returns the state diff for the given block.
///
/// # Arguments
///
/// * `starknet_block_hash` - The hash of the block containing the state diff (starknet block).
fn get_state_diff(&self, starknet_block_hash: &BlockHash) -> Result<StateDiff, StarknetRpcApiError> {
let state_diff = self.backend.da().state_diff(starknet_block_hash).map_err(|e| {
error!("Failed to retrieve state diff from cache for block with hash {}: {e}", starknet_block_hash);
StarknetRpcApiError::InternalServerError
})?;

let rpc_state_diff = to_rpc_state_diff(state_diff);

Ok(rpc_state_diff)
}
}

/// Taken from https://github.com/paritytech/substrate/blob/master/client/rpc/src/author/mod.rs#L78
Expand Down Expand Up @@ -1162,12 +1146,15 @@ where
FieldElement::default()
};

let starknet_block_hash = BlockHash(starknet_block.header().hash().into());
let block_transactions = starknet_block.transactions();

let state_diff = self.get_state_diff(&starknet_block_hash).map_err(|e| {
error!("Failed to get state diff. Starknet block hash: {starknet_block_hash}, error: {e}");
StarknetRpcApiError::InternalServerError
})?;
let previous_block_substrate_hash = get_previous_block_substrate_hash(self, substrate_block_hash)?;

let state_diff = self.get_transaction_re_execution_state_diff(
previous_block_substrate_hash,
vec![],
block_transactions.clone(),
)?;

let state_update = StateUpdate {
block_hash: starknet_block.header().hash().into(),
Expand Down
38 changes: 37 additions & 1 deletion crates/client/rpc/src/trace_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,42 @@ where
})?)
}

pub fn get_transaction_re_execution_state_diff(
&self,
previous_block_substrate_hash: B::Hash,
transactions_before: Vec<Transaction>,
transactions_to_trace: Vec<Transaction>,
) -> RpcResult<StateDiff> {
let commitment_state_diff = self
.client
.runtime_api()
.get_transaction_re_execution_state_diff(
previous_block_substrate_hash,
transactions_before,
transactions_to_trace,
)
.map_err(|e| {
error!("Failed to execute runtime API call: {e}");
StarknetRpcApiError::InternalServerError
})?
.map_err(|e| {
error!("Failed to reexecute the block transactions: {e:?}");
StarknetRpcApiError::InternalServerError
})?
.map_err(|_| {
error!(
"One of the transaction failed during it's reexecution. This should not happen, as the block has \
already been executed successfully in the past. There is a bug somewhere."
);
StarknetRpcApiError::InternalServerError
})?;

Ok(blockifier_to_rpc_state_diff_types(commitment_state_diff).map_err(|e| {
error!("Failed to get state diff from reexecution info, error: {e}");
StarknetRpcApiError::InternalServerError
})?)
}

fn execution_info_to_transaction_trace(
execution_infos: Vec<(TransactionExecutionInfo, CommitmentStateDiff)>,
block_transactions: &[Transaction],
Expand Down Expand Up @@ -446,7 +482,7 @@ fn tx_execution_infos_to_tx_trace(
Ok(tx_trace)
}

fn get_previous_block_substrate_hash<A, B, BE, G, C, P, H>(
pub fn get_previous_block_substrate_hash<A, B, BE, G, C, P, H>(
starknet: &Starknet<A, B, BE, G, C, P, H>,
substrate_block_hash: B::Hash,
) -> Result<B::Hash, StarknetRpcApiError>
Expand Down
8 changes: 5 additions & 3 deletions crates/pallets/starknet/runtime_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ use mp_felt::Felt252Wrapper;
pub extern crate alloc;
use alloc::vec::Vec;

use mp_simulations::{InternalSubstrateError, SimulationError, SimulationFlags, TransactionSimulationResult};
use mp_simulations::{
InternalSubstrateError, ReExecutionResult, SimulationError, SimulationFlags, TransactionSimulationResult,
};
use sp_api::BlockT;
use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce};
use starknet_api::hash::StarkFelt;
use starknet_api::state::StorageKey;
use starknet_api::transaction::{Calldata, Event as StarknetEvent, MessageToL1, TransactionHash};

type ReExecutionResult = Result<Vec<(TransactionExecutionInfo, Option<CommitmentStateDiff>)>, SimulationError>;

sp_api::decl_runtime_apis! {
pub trait StarknetRuntimeApi {
/// Returns the nonce associated with the given address in the given block
Expand Down Expand Up @@ -73,6 +73,8 @@ sp_api::decl_runtime_apis! {
/// If any of the transactions (from both arguments) fails, an error is returned.
fn re_execute_transactions(transactions_before: Vec<Transaction>, transactions_to_trace: Vec<Transaction>, with_state_diff: bool) -> Result<ReExecutionResult, InternalSubstrateError>;

fn get_transaction_re_execution_state_diff(transactions_before: Vec<Transaction>, transactions: Vec<Transaction>) -> Result<Result<CommitmentStateDiff, SimulationError>, InternalSubstrateError>;

fn get_index_and_tx_for_tx_hash(xts: Vec<<Block as BlockT>::Extrinsic>, tx_hash: TransactionHash) -> Option<(u32, Transaction)>;

fn get_events_for_tx_by_hash(tx_hash: TransactionHash) -> Vec<StarknetEvent>;
Expand Down
55 changes: 52 additions & 3 deletions crates/pallets/starknet/src/simulations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use blockifier::transaction::objects::TransactionExecutionInfo;
use blockifier::transaction::transaction_execution::Transaction;
use blockifier::transaction::transactions::{ExecutableTransaction, L1HandlerTransaction};
use frame_support::storage;
use mp_simulations::{InternalSubstrateError, SimulationError, SimulationFlags, TransactionSimulationResult};
use mp_simulations::{
InternalSubstrateError, ReExecutionResult, SimulationError, SimulationFlags, TransactionSimulationResult,
};
use mp_transactions::execution::{
commit_transactional_state, execute_l1_handler_transaction, run_non_revertible_transaction,
run_revertible_transaction, MutRefState, SetArbitraryNonce,
Expand All @@ -19,8 +21,6 @@ use starknet_api::transaction::TransactionVersion;
use crate::blockifier_state_adapter::BlockifierStateAdapter;
use crate::{log, Config, Error, Pallet};

type ReExecutionResult = Result<Vec<(TransactionExecutionInfo, Option<CommitmentStateDiff>)>, SimulationError>;

impl<T: Config> Pallet<T> {
pub fn estimate_fee(
transactions: Vec<AccountTransaction>,
Expand Down Expand Up @@ -269,6 +269,55 @@ impl<T: Config> Pallet<T> {
Ok(execution_infos)
}

pub fn get_transaction_re_execution_state_diff(
transactions_before: Vec<Transaction>,
transactions_to_trace: Vec<Transaction>,
) -> Result<Result<CommitmentStateDiff, SimulationError>, InternalSubstrateError> {
storage::transactional::with_transaction(|| {
let res = Self::get_transaction_re_execution_state_diff_inner(transactions_before, transactions_to_trace);
storage::TransactionOutcome::Rollback(Result::<_, DispatchError>::Ok(Ok(res)))
})
.map_err(|e| {
log::error!("Failed to reexecute a tx: {:?}", e);
InternalSubstrateError::FailedToCreateATransactionalStorageExecution
})?
}

fn get_transaction_re_execution_state_diff_inner(
transactions_before: Vec<Transaction>,
transactions_to_trace: Vec<Transaction>,
) -> Result<CommitmentStateDiff, SimulationError> {
let block_context = Self::get_block_context();
let mut state = BlockifierStateAdapter::<T>::default();

transactions_before.iter().try_for_each(|tx| {
Self::execute_transaction(tx, &mut state, &block_context, &SimulationFlags::default()).map_err(|e| {
log::error!("Failed to reexecute a tx: {}", e);
SimulationError::from(e)
})?;
Ok::<(), SimulationError>(())
})?;

let mut transactional_state = CachedState::new(MutRefState::new(&mut state), GlobalContractCache::new(1));

transactions_to_trace.iter().try_for_each(|tx| {
Self::execute_transaction(tx, &mut transactional_state, &block_context, &SimulationFlags::default())
.map_err(|e| {
log::error!("Failed to reexecute a tx: {}", e);
SimulationError::from(e)
})?;
Ok::<(), SimulationError>(())
})?;

let state_diff = transactional_state.to_state_diff();
commit_transactional_state(transactional_state).map_err(|e| {
log::error!("Failed to commit state changes: {:?}", e);
SimulationError::from(e)
})?;

Ok(state_diff)
}

fn execute_transaction<S: State + SetArbitraryNonce>(
transaction: &Transaction,
state: &mut S,
Expand Down
2 changes: 2 additions & 0 deletions crates/primitives/simulations/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use blockifier::state::cached_state::CommitmentStateDiff;
use blockifier::state::errors::StateError;
use blockifier::transaction::errors::TransactionExecutionError;
use blockifier::transaction::objects::TransactionExecutionInfo;
Expand Down Expand Up @@ -32,6 +33,7 @@ impl From<StateError> for SimulationError {
}
}

pub type ReExecutionResult = Result<Vec<(TransactionExecutionInfo, Option<CommitmentStateDiff>)>, SimulationError>;
pub type TransactionSimulationResult = Result<TransactionExecutionInfo, SimulationError>;

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down
10 changes: 8 additions & 2 deletions crates/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ pub use frame_support::weights::{IdentityFee, Weight};
pub use frame_support::{construct_runtime, parameter_types, StorageValue};
pub use frame_system::Call as SystemCall;
use mp_felt::Felt252Wrapper;
use mp_simulations::{InternalSubstrateError, SimulationError, SimulationFlags, TransactionSimulationResult};
use mp_simulations::{
InternalSubstrateError, ReExecutionResult, SimulationError, SimulationFlags, TransactionSimulationResult,
};
use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList};
/// Import the Starknet pallet.
pub use pallet_starknet;
Expand Down Expand Up @@ -279,10 +281,14 @@ impl_runtime_apis! {
Starknet::estimate_fee(transactions, &simulation_flags)
}

fn re_execute_transactions(transactions_before: Vec<Transaction>, transactions_to_trace: Vec<Transaction>, with_state_diff: bool) -> Result<Result<Vec<(TransactionExecutionInfo, Option<CommitmentStateDiff>)>, SimulationError>, InternalSubstrateError> {
fn re_execute_transactions(transactions_before: Vec<Transaction>, transactions_to_trace: Vec<Transaction>, with_state_diff: bool) -> Result<ReExecutionResult, InternalSubstrateError> {
Starknet::re_execute_transactions(transactions_before, transactions_to_trace, with_state_diff)
}

fn get_transaction_re_execution_state_diff( transactions_before: Vec<Transaction>, transactions_to_trace: Vec<Transaction>) -> Result<Result<CommitmentStateDiff, SimulationError>, InternalSubstrateError> {
Starknet::get_transaction_re_execution_state_diff(transactions_before, transactions_to_trace)
}

fn estimate_message_fee(message: L1HandlerTransaction) -> Result<Result<(u128, u128, u128), SimulationError>, InternalSubstrateError> {
Starknet::estimate_message_fee(message)
}
Expand Down
4 changes: 4 additions & 0 deletions starknet-rpc-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ path = "estimate_message_fee.rs"
name = "starknet_simulate_transaction"
path = "simulate_transaction.rs"

[[test]]
name = "starknet_get_state_update"
path = "get_state_update.rs"

[[test]]
name = "starknet_trace_block_transactions"
path = "trace_block.rs"
Expand Down
15 changes: 15 additions & 0 deletions starknet-rpc-test/add_declare_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,21 @@ async fn fail_execution_step_with_no_storage_change(madara: &ThreadSafeMadaraCli
let included_txs = rpc.get_block_transaction_count(BlockId::Number(block_number)).await?;
assert_eq!(included_txs, 1);

// add some balance back for other tests
let account = build_single_owner_account(&rpc, SIGNER_PRIVATE, ARGENT_CONTRACT_ADDRESS, true);
let balance =
read_erc20_balance(&rpc, FieldElement::from_hex_be(ETH_FEE_TOKEN_ADDRESS).unwrap(), account.address()).await;
{
let mut madara_write_lock = madara.write().await;
let txs = madara_write_lock
.create_block_with_txs(vec![Transaction::Execution(account.transfer_tokens_u256(
oz_account.address(),
U256 { low: FieldElement::ONE, high: balance[1] },
None,
))])
.await?;
assert!(txs[0].is_ok());
}
Ok(())
}

Expand Down
Loading

0 comments on commit 003a0d6

Please sign in to comment.