Skip to content

Commit

Permalink
feat(fgw): get_block_trace (#363)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbcaron authored Oct 25, 2024
1 parent b80fcab commit a2f57e6
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 30 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

- feat: add fgw get_block_traces
- refactor: use `hyper` & `tower` instead of `reqwest` for feeder client
- fix(namespace): versioning now works for methods without `starknet` namesapce
- fix(compile): wrong struct field being used in state map conversion
Expand Down
66 changes: 65 additions & 1 deletion crates/client/gateway/src/server/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use std::fmt::{self, Display};

use hyper::Response;
use mc_db::MadaraStorageError;
use mc_rpc::StarknetRpcApiError;

use crate::error::StarknetError;
use crate::error::{StarknetError, StarknetErrorCode};

use super::helpers::internal_error_response;

Expand Down Expand Up @@ -62,3 +63,66 @@ impl<T> OptionExt<T> for Option<T> {
}
}
}

impl From<StarknetRpcApiError> for GatewayError {
fn from(e: StarknetRpcApiError) -> Self {
match e {
StarknetRpcApiError::InternalServerError => {
GatewayError::InternalServerError("Internal server error".to_string())
}
StarknetRpcApiError::BlockNotFound => GatewayError::StarknetError(StarknetError::block_not_found()),
StarknetRpcApiError::InvalidContractClass => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::InvalidContractClass,
"Invalid contract class".to_string(),
)),
StarknetRpcApiError::ClassAlreadyDeclared => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::ClassAlreadyDeclared,
"Class already declared".to_string(),
)),
StarknetRpcApiError::InsufficientMaxFee => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::InsufficientMaxFee,
"Insufficient max fee".to_string(),
)),
StarknetRpcApiError::InsufficientAccountBalance => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::InsufficientAccountBalance,
"Insufficient account balance".to_string(),
)),
StarknetRpcApiError::ValidationFailure { error } => {
GatewayError::StarknetError(StarknetError::new(StarknetErrorCode::ValidateFailure, error))
}
StarknetRpcApiError::CompilationFailed => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::CompilationFailed,
"Compilation failed".to_string(),
)),
StarknetRpcApiError::ContractClassSizeTooLarge => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::ContractBytecodeSizeTooLarge,
"Contract class size is too large".to_string(),
)),
StarknetRpcApiError::NonAccount => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::NotPermittedContract,
"Sender address is not an account contract".to_string(),
)),
StarknetRpcApiError::DuplicateTxn => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::DuplicatedTransaction,
"A transaction with the same hash already exists in the mempool".to_string(),
)),
StarknetRpcApiError::CompiledClassHashMismatch => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::InvalidCompiledClassHash,
"The compiled class hash did not match the one supplied in the transaction".to_string(),
)),
StarknetRpcApiError::UnsupportedTxnVersion => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::InvalidTransactionVersion,
"The transaction version is not supported".to_string(),
)),
StarknetRpcApiError::UnsupportedContractClassVersion => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::InvalidContractClassVersion,
"The contract class version is not supported".to_string(),
)),
StarknetRpcApiError::ErrUnexpectedError { data } => GatewayError::StarknetError(StarknetError::new(
StarknetErrorCode::TransactionFailed,
format!("An unexpected error occurred: {}", data),
)),
e => GatewayError::InternalServerError(format!("Unexpected error: {:#?}", e)),
}
}
}
29 changes: 27 additions & 2 deletions crates/client/gateway/src/server/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@ use bytes::Buf;
use http_body_util::BodyExt;
use hyper::{body::Incoming, Request, Response};
use mc_db::MadaraBackend;
use mc_rpc::providers::AddTransactionProvider;
use mc_rpc::{
providers::AddTransactionProvider,
versions::v0_7_1::methods::trace::trace_block_transactions::trace_block_transactions as v0_7_1_trace_block_transactions,
Starknet,
};
use mp_block::{BlockId, BlockTag, MadaraBlock, MadaraMaybePendingBlockInfo, MadaraPendingBlock};
use mp_class::{ClassInfo, ContractClass};
use mp_gateway::{
block::{BlockStatus, ProviderBlock, ProviderBlockPending, ProviderBlockSignature},
state_update::{ProviderStateUpdate, ProviderStateUpdatePending},
};
use serde::Serialize;
use serde_json::json;
use starknet_core::types::{
BroadcastedDeclareTransaction, BroadcastedDeployAccountTransaction, BroadcastedInvokeTransaction,
BroadcastedTransaction,
BroadcastedTransaction, TransactionTraceWithHash,
};
use starknet_types_core::felt::Felt;

Expand Down Expand Up @@ -221,6 +226,26 @@ pub async fn handle_get_state_update(
}
}

pub async fn handle_get_block_traces(
req: Request<Incoming>,
backend: Arc<MadaraBackend>,
add_transaction_provider: Arc<dyn AddTransactionProvider>,
) -> Result<Response<String>, GatewayError> {
let params = get_params_from_request(&req);
let block_id = block_id_from_params(&params).or_internal_server_error("Retrieving block id")?;

#[derive(Serialize)]
struct BlockTraces {
traces: Vec<TransactionTraceWithHash>,
}

let traces =
v0_7_1_trace_block_transactions(&Starknet::new(backend, add_transaction_provider), block_id.into()).await?;
let block_traces = BlockTraces { traces };

Ok(create_json_response(hyper::StatusCode::OK, &block_traces))
}

pub async fn handle_get_class_by_hash(
req: Request<Incoming>,
backend: Arc<MadaraBackend>,
Expand Down
13 changes: 10 additions & 3 deletions crates/client/gateway/src/server/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use mc_db::MadaraBackend;
use mc_rpc::providers::AddTransactionProvider;

use super::handler::{
handle_add_transaction, handle_get_block, handle_get_class_by_hash, handle_get_compiled_class_by_class_hash,
handle_get_contract_addresses, handle_get_public_key, handle_get_signature, handle_get_state_update,
handle_add_transaction, handle_get_block, handle_get_block_traces, handle_get_class_by_hash,
handle_get_compiled_class_by_class_hash, handle_get_contract_addresses, handle_get_public_key,
handle_get_signature, handle_get_state_update,
};
use super::helpers::{not_found_response, service_unavailable_response};

Expand All @@ -21,7 +22,9 @@ pub(crate) async fn main_router(
let path = req.uri().path().split('/').filter(|segment| !segment.is_empty()).collect::<Vec<_>>().join("/");
match (path.as_ref(), feeder_gateway_enable, gateway_enable) {
("health", _, _) => Ok(Response::new("OK".to_string())),
(path, true, _) if path.starts_with("feeder_gateway/") => feeder_gateway_router(req, path, backend).await,
(path, true, _) if path.starts_with("feeder_gateway/") => {
feeder_gateway_router(req, path, backend, add_transaction_provider).await
}
(path, _, true) if path.starts_with("gateway/") => gateway_router(req, path, add_transaction_provider).await,
(path, false, _) if path.starts_with("feeder_gateway/") => Ok(service_unavailable_response("Feeder Gateway")),
(path, _, false) if path.starts_with("gateway/") => Ok(service_unavailable_response("Feeder")),
Expand All @@ -37,6 +40,7 @@ async fn feeder_gateway_router(
req: Request<Incoming>,
path: &str,
backend: Arc<MadaraBackend>,
add_transaction_provider: Arc<dyn AddTransactionProvider>,
) -> Result<Response<String>, Infallible> {
match (req.method(), path) {
(&Method::GET, "feeder_gateway/get_block") => {
Expand All @@ -48,6 +52,9 @@ async fn feeder_gateway_router(
(&Method::GET, "feeder_gateway/get_state_update") => {
Ok(handle_get_state_update(req, backend).await.unwrap_or_else(Into::into))
}
(&Method::GET, "feeder_gateway/get_block_traces") => {
Ok(handle_get_block_traces(req, backend, add_transaction_provider).await.unwrap_or_else(Into::into))
}
(&Method::GET, "feeder_gateway/get_class_by_hash") => {
Ok(handle_get_class_by_hash(req, backend).await.unwrap_or_else(Into::into))
}
Expand Down
17 changes: 8 additions & 9 deletions crates/client/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,30 @@ use mp_block::{MadaraMaybePendingBlock, MadaraMaybePendingBlockInfo};
use mp_chain_config::{ChainConfig, RpcVersion};
use mp_convert::ToFelt;

use errors::{StarknetRpcApiError, StarknetRpcResult};
pub use errors::{StarknetRpcApiError, StarknetRpcResult};
use providers::AddTransactionProvider;
use utils::ResultExt;

/// A Starknet RPC server for Madara
#[derive(Clone)]
pub struct Starknet {
backend: Arc<MadaraBackend>,
chain_config: Arc<ChainConfig>,
pub(crate) add_transaction_provider: Arc<dyn AddTransactionProvider>,
}

impl Starknet {
pub fn new(
backend: Arc<MadaraBackend>,
chain_config: Arc<ChainConfig>,
add_transaction_provider: Arc<dyn AddTransactionProvider>,
) -> Self {
Self { backend, add_transaction_provider, chain_config }
pub fn new(backend: Arc<MadaraBackend>, add_transaction_provider: Arc<dyn AddTransactionProvider>) -> Self {
Self { backend, add_transaction_provider }
}

pub fn clone_backend(&self) -> Arc<MadaraBackend> {
Arc::clone(&self.backend)
}

pub fn clone_chain_config(&self) -> Arc<ChainConfig> {
Arc::clone(self.backend.chain_config())
}

pub fn get_block_info(
&self,
block_id: &impl DbBlockIdResolvable,
Expand All @@ -71,7 +70,7 @@ impl Starknet {
}

pub fn chain_id(&self) -> Felt {
self.chain_config.chain_id.clone().to_felt()
self.backend.chain_config().chain_id.clone().to_felt()
}

pub fn current_block_number(&self) -> StarknetRpcResult<u64> {
Expand Down
2 changes: 1 addition & 1 deletion crates/client/rpc/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl AddTransactionProvider for TestTransactionProvider {
pub fn rpc_test_setup() -> (Arc<MadaraBackend>, Starknet) {
let chain_config = Arc::new(ChainConfig::madara_test());
let backend = MadaraBackend::open_for_testing(chain_config.clone());
let rpc = Starknet::new(backend.clone(), chain_config.clone(), Arc::new(TestTransactionProvider));
let rpc = Starknet::new(backend.clone(), Arc::new(TestTransactionProvider));
(backend, rpc)
}

Expand Down
1 change: 1 addition & 0 deletions crates/client/rpc/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub(crate) mod transaction;
use std::fmt;

use crate::StarknetRpcApiError;
pub use transaction::to_blockifier_transactions;

pub fn display_internal_server_error(err: impl fmt::Display) {
log::error!(target: "rpc_errors", "{:#}", err);
Expand Down
13 changes: 7 additions & 6 deletions crates/client/rpc/src/utils/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
use std::sync::Arc;

use blockifier::execution::contract_class::ClassInfo;
use blockifier::transaction::transaction_execution as btx;
use mc_db::MadaraBackend;
use mp_block::BlockId;
use mp_convert::ToFelt;
use starknet_api::transaction::{Transaction, TransactionHash};

use crate::errors::{StarknetRpcApiError, StarknetRpcResult};
use crate::Starknet;

/// Convert an starknet-api Transaction to a blockifier Transaction
///
/// **note:** this function does not support deploy transaction
/// because it is not supported by blockifier
pub(crate) fn to_blockifier_transactions(
starknet: &Starknet,
pub fn to_blockifier_transactions(
backend: Arc<MadaraBackend>,
block_id: BlockId,
transaction: mp_transactions::Transaction,
tx_hash: &TransactionHash,
Expand All @@ -28,15 +30,14 @@ pub(crate) fn to_blockifier_transactions(
Transaction::Declare(ref declare_tx) => {
let class_hash = declare_tx.class_hash();

let Ok(Some(class_info)) = starknet.backend.get_class_info(&block_id, &class_hash.to_felt()) else {
let Ok(Some(class_info)) = backend.get_class_info(&block_id, &class_hash.to_felt()) else {
log::error!("Failed to retrieve class from class_hash '{class_hash}'");
return Err(StarknetRpcApiError::ContractNotFound);
};

match class_info {
mp_class::ClassInfo::Sierra(info) => {
let compiled_class = starknet
.backend
let compiled_class = backend
.get_sierra_compiled(&block_id, &info.compiled_class_hash)
.map_err(|e| {
log::error!("Failed to retrieve sierra compiled class from class_hash '{class_hash}': {e}");
Expand Down
2 changes: 1 addition & 1 deletion crates/client/rpc/src/versions/v0_7_1/methods/trace/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub(crate) mod simulate_transactions;
pub(crate) mod trace_block_transactions;
pub mod trace_block_transactions;
pub(crate) mod trace_transaction;

use jsonrpsee::core::{async_trait, RpcResult};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ pub async fn trace_block_transactions(
.transactions
.into_iter()
.zip(block.info.tx_hashes())
.map(|(tx, hash)| to_blockifier_transactions(starknet, block_id.into(), tx, &TransactionHash(*hash)))
.map(|(tx, hash)| {
to_blockifier_transactions(starknet.clone_backend(), block_id.into(), tx, &TransactionHash(*hash))
})
.collect::<Result<_, _>>()?;

let executions_results = exec_context.re_execute_transactions([], transactions, true, true)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ pub async fn trace_transaction(

let exec_context = ExecutionContext::new_in_block(Arc::clone(&starknet.backend), &block.info)?;

let mut block_txs = Iterator::zip(block.inner.transactions.into_iter(), block.info.tx_hashes())
.map(|(tx, hash)| to_blockifier_transactions(starknet, block.info.as_block_id(), tx, &TransactionHash(*hash)));
let mut block_txs =
Iterator::zip(block.inner.transactions.into_iter(), block.info.tx_hashes()).map(|(tx, hash)| {
to_blockifier_transactions(starknet.clone_backend(), block.info.as_block_id(), tx, &TransactionHash(*hash))
});

// takes up until not including last tx
let transactions_before: Vec<_> = block_txs.by_ref().take(tx_index.0 as usize).collect::<Result<_, _>>()?;
Expand Down
1 change: 0 additions & 1 deletion crates/node/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ async fn main() -> anyhow::Result<()> {
let rpc_service = RpcService::new(
&run_cmd.rpc_params,
&db_service,
Arc::clone(&chain_config),
prometheus_service.registry(),
Arc::clone(&rpc_add_txs_method_provider),
)
Expand Down
4 changes: 1 addition & 3 deletions crates/node/src/service/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use tokio::task::JoinSet;
use mc_db::DatabaseService;
use mc_metrics::MetricsRegistry;
use mc_rpc::{providers::AddTransactionProvider, versioned_rpc_api, Starknet};
use mp_chain_config::ChainConfig;
use mp_utils::service::Service;

use metrics::RpcMetrics;
Expand All @@ -26,7 +25,6 @@ impl RpcService {
pub fn new(
config: &RpcParams,
db: &DatabaseService,
chain_config: Arc<ChainConfig>,
metrics_handle: &MetricsRegistry,
add_txs_method_provider: Arc<dyn AddTransactionProvider>,
) -> anyhow::Result<Self> {
Expand All @@ -47,7 +45,7 @@ impl RpcService {
}
};
let (read, write, trace, internal, ws) = (rpcs, rpcs, rpcs, node_operator, rpcs);
let starknet = Starknet::new(Arc::clone(db.backend()), chain_config.clone(), add_txs_method_provider);
let starknet = Starknet::new(Arc::clone(db.backend()), add_txs_method_provider);
let metrics = RpcMetrics::register(metrics_handle)?;

Ok(Self {
Expand Down

0 comments on commit a2f57e6

Please sign in to comment.