Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit 7abb894

Browse files
authored
Add RPCState::get_transaction_trace (#909)
* Add RPCState::get_transaction_receipt * Deserialize signature * Parse execution resources * Parse retdata * Add parse_felt_array * Fix tests * Add feeder gateway link * Fix link * Add get_chain_name * Add #[cfg(test)] * Update test name
1 parent f05da40 commit 7abb894

File tree

5 files changed

+252
-2
lines changed

5 files changed

+252
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rpc_state_reader/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ starknet = { workspace = true }
1919
thiserror = { workspace = true }
2020
serde_with = "3.0.0"
2121
dotenv = "0.15.0"
22+
cairo-vm = "0.8.5"

rpc_state_reader/src/lib.rs

Lines changed: 192 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ use serde_with::{serde_as, DeserializeAs};
66
use starknet::core::types::ContractClass;
77
use starknet_in_rust::{
88
core::errors::state_errors::StateError,
9+
execution::CallInfo,
910
felt::Felt252,
1011
services::api::contract_classes::compiled_class::CompiledClass,
1112
state::{state_api::StateReader, state_cache::StorageEntry},
12-
utils::{Address, ClassHash, CompiledClassHash},
13+
utils::{parse_felt_array, Address, ClassHash, CompiledClassHash},
1314
};
1415
use std::env;
1516
use thiserror::Error;
@@ -138,6 +139,63 @@ impl RpcState {
138139
}
139140
}
140141

142+
#[derive(Debug, Clone)]
143+
pub struct TransactionTrace {
144+
pub validate_invocation: CallInfo,
145+
pub function_invocation: CallInfo,
146+
pub fee_transfer_invocation: CallInfo,
147+
pub signature: Vec<Felt252>,
148+
}
149+
150+
impl<'de> Deserialize<'de> for TransactionTrace {
151+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
152+
where
153+
D: Deserializer<'de>,
154+
{
155+
let value: serde_json::Value = Deserialize::deserialize(deserializer)?;
156+
157+
let validate_invocation = value["validate_invocation"].clone();
158+
let function_invocation = value["function_invocation"].clone();
159+
let fee_transfer_invocation = value["fee_transfer_invocation"].clone();
160+
let signature_value = value["signature"].clone();
161+
let signature = parse_felt_array(signature_value.as_array().unwrap());
162+
163+
Ok(TransactionTrace {
164+
validate_invocation: serde_json::from_value(validate_invocation)
165+
.map_err(serde::de::Error::custom)?,
166+
function_invocation: serde_json::from_value(function_invocation)
167+
.map_err(serde::de::Error::custom)?,
168+
fee_transfer_invocation: serde_json::from_value(fee_transfer_invocation)
169+
.map_err(serde::de::Error::custom)?,
170+
signature,
171+
})
172+
}
173+
}
174+
175+
#[cfg(test)]
176+
impl RpcState {
177+
pub fn get_transaction_trace(&self, hash: Felt252) -> TransactionTrace {
178+
let chain_name = self.get_chain_name();
179+
let response = ureq::get(&format!(
180+
"https://{}.starknet.io/feeder_gateway/get_transaction_trace",
181+
chain_name
182+
))
183+
.query("transactionHash", &format!("0x{}", hash.to_str_radix(16)))
184+
.call()
185+
.unwrap();
186+
187+
serde_json::from_str(&response.into_string().unwrap()).unwrap()
188+
}
189+
190+
fn get_chain_name(&self) -> String {
191+
match self.chain {
192+
RpcChain::MainNet => "alpha-mainnet".to_string(),
193+
RpcChain::TestNet => "alpha4".to_string(),
194+
RpcChain::TestNet2 => "alpha4-2".to_string(),
195+
}
196+
}
197+
}
198+
141199
impl StateReader for RpcState {
142200
fn get_contract_class(&self, class_hash: &ClassHash) -> Result<CompiledClass, StateError> {
143201
let params = ureq::json!({
@@ -212,7 +270,8 @@ impl StateReader for RpcState {
212270

213271
#[cfg(test)]
214272
mod tests {
215-
use std::sync::Arc;
273+
use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
274+
use std::{collections::HashMap, sync::Arc};
216275

217276
use super::*;
218277
use starknet_in_rust::{
@@ -658,4 +717,135 @@ mod tests {
658717
.execute(&mut state, &block_context, 0)
659718
.unwrap();
660719
}
720+
721+
// https://alpha4-2.starknet.io/feeder_gateway/get_transaction_trace?transactionHash=0x019feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc
722+
#[test]
723+
fn test_get_transaction_trace() {
724+
let state_reader = RpcState::new(
725+
RpcChain::TestNet2,
726+
BlockValue::Number(serde_json::to_value(838683).unwrap()),
727+
);
728+
729+
let tx_hash_str = "19feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc";
730+
let tx_hash = felt_str!(format!("{}", tx_hash_str), 16);
731+
732+
let tx_trace = state_reader.get_transaction_trace(tx_hash);
733+
734+
assert_eq!(
735+
tx_trace.signature,
736+
vec![
737+
felt_str!(
738+
"ffab1c47d8d5e5b76bdcc4af79e98205716c36b440f20244c69599a91ace58",
739+
16
740+
),
741+
felt_str!(
742+
"6aa48a0906c9c1f7381c1a040c043b649eeac1eea08f24a9d07813f6b1d05fe",
743+
16
744+
),
745+
]
746+
);
747+
748+
assert_eq!(
749+
tx_trace.validate_invocation.calldata,
750+
vec![
751+
felt_str!("1", 16),
752+
felt_str!(
753+
"690c876e61beda61e994543af68038edac4e1cb1990ab06e52a2d27e56a1232",
754+
16
755+
),
756+
felt_str!(
757+
"1f24f689ced5802b706d7a2e28743fe45c7bfa37431c97b1c766e9622b65573",
758+
16
759+
),
760+
felt_str!("0", 16),
761+
felt_str!("9", 16),
762+
felt_str!("9", 16),
763+
felt_str!("4", 16),
764+
felt_str!("4254432d55534443", 16),
765+
felt_str!("f02e7324ecbd65ce267", 16),
766+
felt_str!("5754492d55534443", 16),
767+
felt_str!("8e13050d06d8f514c", 16),
768+
felt_str!("4554482d55534443", 16),
769+
felt_str!("f0e4a142c3551c149d", 16),
770+
felt_str!("4a50592d55534443", 16),
771+
felt_str!("38bd34c31a0a5c", 16),
772+
]
773+
);
774+
assert_eq!(tx_trace.validate_invocation.retdata, vec![]);
775+
assert_eq!(
776+
tx_trace.validate_invocation.execution_resources,
777+
ExecutionResources {
778+
n_steps: 790,
779+
n_memory_holes: 51,
780+
builtin_instance_counter: HashMap::from([
781+
("range_check_builtin".to_string(), 20),
782+
("ecdsa_builtin".to_string(), 1),
783+
("pedersen_builtin".to_string(), 2),
784+
]),
785+
}
786+
);
787+
788+
assert_eq!(
789+
tx_trace.function_invocation.calldata,
790+
vec![
791+
felt_str!("1", 16),
792+
felt_str!(
793+
"690c876e61beda61e994543af68038edac4e1cb1990ab06e52a2d27e56a1232",
794+
16
795+
),
796+
felt_str!(
797+
"1f24f689ced5802b706d7a2e28743fe45c7bfa37431c97b1c766e9622b65573",
798+
16
799+
),
800+
felt_str!("0", 16),
801+
felt_str!("9", 16),
802+
felt_str!("9", 16),
803+
felt_str!("4", 16),
804+
felt_str!("4254432d55534443", 16),
805+
felt_str!("f02e7324ecbd65ce267", 16),
806+
felt_str!("5754492d55534443", 16),
807+
felt_str!("8e13050d06d8f514c", 16),
808+
felt_str!("4554482d55534443", 16),
809+
felt_str!("f0e4a142c3551c149d", 16),
810+
felt_str!("4a50592d55534443", 16),
811+
felt_str!("38bd34c31a0a5c", 16),
812+
]
813+
);
814+
assert_eq!(tx_trace.function_invocation.retdata, vec![0.into()]);
815+
assert_eq!(
816+
tx_trace.function_invocation.execution_resources,
817+
ExecutionResources {
818+
n_steps: 2808,
819+
n_memory_holes: 136,
820+
builtin_instance_counter: HashMap::from([
821+
("range_check_builtin".to_string(), 49),
822+
("pedersen_builtin".to_string(), 14),
823+
]),
824+
}
825+
);
826+
827+
assert_eq!(
828+
tx_trace.fee_transfer_invocation.calldata,
829+
vec![
830+
felt_str!(
831+
"1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8",
832+
16
833+
),
834+
felt_str!("2b0322a23ba4", 16),
835+
felt_str!("0", 16),
836+
]
837+
);
838+
assert_eq!(tx_trace.fee_transfer_invocation.retdata, vec![1.into()]);
839+
assert_eq!(
840+
tx_trace.fee_transfer_invocation.execution_resources,
841+
ExecutionResources {
842+
n_steps: 586,
843+
n_memory_holes: 42,
844+
builtin_instance_counter: HashMap::from([
845+
("range_check_builtin".to_string(), 21),
846+
("pedersen_builtin".to_string(), 4),
847+
]),
848+
}
849+
);
850+
}
661851
}

src/execution/mod.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod os_usage;
44

55
use crate::definitions::constants::QUERY_VERSION_BASE;
66
use crate::services::api::contract_classes::deprecated_contract_class::EntryPointType;
7+
use crate::utils::parse_felt_array;
78
use crate::{
89
definitions::{constants::CONSTRUCTOR_ENTRY_POINT_SELECTOR, transaction_type::TransactionType},
910
state::state_cache::StorageEntry,
@@ -18,6 +19,7 @@ use cairo_vm::{
1819
};
1920
use getset::Getters;
2021
use num_traits::{ToPrimitive, Zero};
22+
use serde::{Deserialize, Deserializer};
2123
use std::collections::{HashMap, HashSet};
2224

2325
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -232,6 +234,46 @@ impl Default for CallInfo {
232234
}
233235
}
234236

237+
impl<'de> Deserialize<'de> for CallInfo {
238+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
239+
where
240+
D: Deserializer<'de>,
241+
{
242+
let value: serde_json::Value = Deserialize::deserialize(deserializer)?;
243+
244+
// Parse execution_resources
245+
let execution_resources_value = value["execution_resources"].clone();
246+
247+
let execution_resources = ExecutionResources {
248+
n_steps: serde_json::from_value(execution_resources_value["n_steps"].clone())
249+
.map_err(serde::de::Error::custom)?,
250+
n_memory_holes: serde_json::from_value(
251+
execution_resources_value["n_memory_holes"].clone(),
252+
)
253+
.map_err(serde::de::Error::custom)?,
254+
builtin_instance_counter: serde_json::from_value(
255+
execution_resources_value["builtin_instance_counter"].clone(),
256+
)
257+
.map_err(serde::de::Error::custom)?,
258+
};
259+
260+
// Parse retdata
261+
let retdata_value = value["result"].clone();
262+
let retdata = parse_felt_array(retdata_value.as_array().unwrap());
263+
264+
// Parse calldata
265+
let calldata_value = value["calldata"].clone();
266+
let calldata = parse_felt_array(calldata_value.as_array().unwrap());
267+
268+
Ok(CallInfo {
269+
execution_resources,
270+
retdata,
271+
calldata,
272+
..Default::default()
273+
})
274+
}
275+
}
276+
235277
// ----------------------
236278
// CallResult structure
237279
// ----------------------

src/utils.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use cairo_vm::{
1919
use cairo_vm::{types::relocatable::Relocatable, vm::vm_core::VirtualMachine};
2020
use num_traits::{Num, ToPrimitive};
2121
use serde::{Deserialize, Serialize};
22+
use serde_json::Value;
2223
use sha3::{Digest, Keccak256};
2324
use starknet_crypto::FieldElement;
2425
use std::{
@@ -346,6 +347,21 @@ pub(crate) fn parse_builtin_names(
346347
.collect()
347348
}
348349

350+
/// Parses an array of strings representing Felt252 as hex
351+
pub fn parse_felt_array(felt_strings: &[Value]) -> Vec<Felt252> {
352+
let mut felts = vec![];
353+
354+
for felt in felt_strings {
355+
let felt_string = felt.as_str().unwrap();
356+
felts.push(match felt_string.starts_with("0x") {
357+
true => Felt252::parse_bytes(felt_string[2..].as_bytes(), 16).unwrap(),
358+
false => Felt252::parse_bytes(felt_string.as_bytes(), 16).unwrap(),
359+
})
360+
}
361+
362+
felts
363+
}
364+
349365
//* -------------------
350366
//* Macros
351367
//* -------------------

0 commit comments

Comments
 (0)