Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(katana-executor): test tx execution info conversion #1863

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 224 additions & 62 deletions crates/katana/executor/src/implementation/blockifier/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;

use blockifier::block_context::{BlockContext, BlockInfo, ChainInfo, FeeTokenAddresses, GasPrices};
use blockifier::execution::call_info::CallInfo;
use blockifier::execution::call_info::{
CallExecution, CallInfo, OrderedEvent, OrderedL2ToL1Message,
};
use blockifier::execution::common_hints::ExecutionMode;
use blockifier::execution::contract_class::{ContractClass, ContractClassV0, ContractClassV1};
use blockifier::execution::entry_point::{
Expand All @@ -29,7 +31,7 @@ use katana_primitives::trace::TxExecInfo;
use katana_primitives::transaction::{
DeclareTx, DeployAccountTx, ExecutableTx, ExecutableTxWithHash, InvokeTx,
};
use katana_primitives::FieldElement;
use katana_primitives::{event, message, trace, FieldElement};
use katana_provider::traits::contract::ContractClassProvider;
use starknet::core::types::PriceUnit;
use starknet::core::utils::parse_cairo_short_string;
Expand Down Expand Up @@ -525,73 +527,95 @@ pub fn to_exec_info(exec_info: TransactionExecutionInfo) -> TxExecInfo {
}
}

fn to_call_info(call_info: CallInfo) -> katana_primitives::trace::CallInfo {
katana_primitives::trace::CallInfo {
contract_address: to_address(call_info.call.storage_address),
caller_address: to_address(call_info.call.caller_address),
call_type: match call_info.call.call_type {
CallType::Call => katana_primitives::trace::CallType::Call,
CallType::Delegate => katana_primitives::trace::CallType::Delegate,
},
code_address: call_info.call.code_address.map(to_address),
class_hash: call_info.call.class_hash.map(|a| a.0.into()),
entry_point_selector: call_info.call.entry_point_selector.0.into(),
entry_point_type: match call_info.call.entry_point_type {
EntryPointType::External => katana_primitives::trace::EntryPointType::External,
EntryPointType::L1Handler => katana_primitives::trace::EntryPointType::L1Handler,
EntryPointType::Constructor => katana_primitives::trace::EntryPointType::Constructor,
},
calldata: call_info.call.calldata.0.iter().map(|f| (*f).into()).collect(),
retdata: call_info.execution.retdata.0.iter().map(|f| (*f).into()).collect(),
execution_resources: katana_primitives::trace::ExecutionResources {
n_steps: call_info.vm_resources.n_steps as u64,
n_memory_holes: call_info.vm_resources.n_memory_holes as u64,
builtin_instance_counter: call_info
.vm_resources
.builtin_instance_counter
.into_iter()
.map(|(k, v)| (k, v as u64))
.collect(),
},
events: call_info
.execution
.events
.iter()
.map(|e| katana_primitives::event::OrderedEvent {
order: e.order as u64,
keys: e.event.keys.iter().map(|f| f.0.into()).collect(),
data: e.event.data.0.iter().map(|f| (*f).into()).collect(),
})
.collect(),
l2_to_l1_messages: call_info
.execution
.l2_to_l1_messages
.iter()
.map(|m| katana_primitives::message::OrderedL2ToL1Message {
order: m.order as u64,
from_address: to_address(call_info.call.storage_address),
to_address: starknet_api_ethaddr_to_felt(m.message.to_address),
payload: m.message.payload.0.iter().map(|f| (*f).into()).collect(),
})
.collect(),
storage_read_values: call_info.storage_read_values.into_iter().map(|f| f.into()).collect(),
accessed_storage_keys: call_info
.accessed_storage_keys
.into_iter()
.map(|sk| (*sk.0.key()).into())
.collect(),
inner_calls: call_info.inner_calls.iter().map(|c| to_call_info(c.clone())).collect(),
gas_consumed: call_info.execution.gas_consumed as u128,
failed: call_info.execution.failed,
fn to_call_info(call: CallInfo) -> trace::CallInfo {
let contract_address = to_address(call.call.storage_address);
let caller_address = to_address(call.call.caller_address);
let code_address = call.call.code_address.map(to_address);
let class_hash = call.call.class_hash.map(|a| a.0.into());
let entry_point_selector = call.call.entry_point_selector.0.into();
let calldata = call.call.calldata.0.iter().map(|f| (*f).into()).collect();
let retdata = call.execution.retdata.0.into_iter().map(|f| f.into()).collect();

let builtin_counter = call.vm_resources.builtin_instance_counter;
let execution_resources = trace::ExecutionResources {
n_steps: call.vm_resources.n_steps as u64,
n_memory_holes: call.vm_resources.n_memory_holes as u64,
builtin_instance_counter: builtin_counter.into_iter().map(|(k, v)| (k, v as u64)).collect(),
};

let CallExecution { events, l2_to_l1_messages, .. } = call.execution;

let events = events.into_iter().map(to_ordered_event).collect();
let l1_msg =
l2_to_l1_messages.into_iter().map(|m| to_l2_l1_messages(m, contract_address)).collect();

let call_type = match call.call.call_type {
CallType::Call => trace::CallType::Call,
CallType::Delegate => trace::CallType::Delegate,
};

let entry_point_type = match call.call.entry_point_type {
EntryPointType::External => trace::EntryPointType::External,
EntryPointType::L1Handler => trace::EntryPointType::L1Handler,
EntryPointType::Constructor => trace::EntryPointType::Constructor,
};

let storage_read_values = call.storage_read_values.into_iter().map(|f| f.into()).collect();
let storg_keys = call.accessed_storage_keys.into_iter().map(|k| (*k.0.key()).into()).collect();
let inner_calls = call.inner_calls.into_iter().map(to_call_info).collect();

trace::CallInfo {
contract_address,
caller_address,
call_type,
code_address,
class_hash,
entry_point_selector,
entry_point_type,
calldata,
retdata,
execution_resources,
events,
l2_to_l1_messages: l1_msg,
storage_read_values,
accessed_storage_keys: storg_keys,
inner_calls,
gas_consumed: call.execution.gas_consumed as u128,
failed: call.execution.failed,
}
}

fn to_ordered_event(e: OrderedEvent) -> event::OrderedEvent {
event::OrderedEvent {
order: e.order as u64,
keys: e.event.keys.into_iter().map(|f| f.0.into()).collect(),
data: e.event.data.0.into_iter().map(FieldElement::from).collect(),
}
}

fn to_l2_l1_messages(
m: OrderedL2ToL1Message,
from_address: katana_primitives::contract::ContractAddress,
) -> message::OrderedL2ToL1Message {
let order = m.order as u64;
let to_address = starknet_api_ethaddr_to_felt(m.message.to_address);
let payload = m.message.payload.0.into_iter().map(FieldElement::from).collect();
message::OrderedL2ToL1Message { order, from_address, to_address, payload }
}

#[cfg(test)]
mod tests {

use std::collections::HashSet;

use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
use katana_primitives::chain::{ChainId, NamedChainId};
use starknet::core::utils::parse_cairo_short_string;
use starknet_api::core::EntryPointSelector;
use starknet_api::hash::StarkFelt;
use starknet_api::stark_felt;
use starknet_api::transaction::{EventContent, EventData, EventKey};

use crate::implementation::blockifier::utils::to_blk_chain_id;
use super::*;

#[test]
fn convert_chain_id() {
Expand All @@ -603,4 +627,142 @@ mod tests {
assert_eq!(goerli.0, parse_cairo_short_string(&NamedChainId::Goerli.id()).unwrap());
assert_eq!(sepolia.0, parse_cairo_short_string(&NamedChainId::Sepolia.id()).unwrap());
}

fn create_blockifier_call_info() -> CallInfo {
let top_events = vec![OrderedEvent {
order: 0,
event: EventContent {
data: EventData(vec![888u128.into()]),
keys: vec![EventKey(999u128.into())],
},
}];
let nested_events = vec![
OrderedEvent {
order: 1,
event: EventContent {
data: EventData(vec![889u128.into()]),
keys: vec![EventKey(990u128.into())],
},
},
OrderedEvent {
order: 2,
event: EventContent {
data: EventData(vec![0u128.into()]),
keys: vec![EventKey(9u128.into())],
},
},
];

let nested_call = CallInfo {
execution: CallExecution { events: nested_events, ..Default::default() },
..Default::default()
};

CallInfo {
call: CallEntryPoint {
class_hash: None,
initial_gas: 77,
call_type: CallType::Call,
caller_address: 200u128.into(),
storage_address: 100u128.into(),
code_address: Some(100u128.into()),
entry_point_type: EntryPointType::External,
calldata: Calldata(Arc::new(vec![stark_felt!(1_u8)])),
entry_point_selector: EntryPointSelector(stark_felt!(999_u32)),
},
execution: CallExecution {
failed: true,
gas_consumed: 12345,
events: top_events,
..Default::default()
},
storage_read_values: vec![stark_felt!(1_u8), stark_felt!(2_u8)],
accessed_storage_keys: HashSet::from([3u128.into(), 4u128.into(), 5u128.into()]),
vm_resources: ExecutionResources {
n_steps: 1_000_000,
n_memory_holes: 9_000,
builtin_instance_counter: HashMap::from([
("ecdsa_builtin".into(), 50),
("pedersen_builtin".into(), 9),
]),
},
inner_calls: vec![nested_call],
}
}

#[test]
fn convert_call_info() {
// setup expected values
let call = create_blockifier_call_info();

let expected_contract_address = to_address(call.call.storage_address);
let expected_caller_address = to_address(call.call.caller_address);
let expected_code_address = call.call.code_address.map(to_address);
let expected_class_hash = call.call.class_hash.map(|a| a.0.into());
let expected_entry_point_selector = call.call.entry_point_selector.0.into();
let expected_calldata: Vec<FieldElement> =
call.call.calldata.0.iter().map(|f| (*f).into()).collect();
let expected_retdata: Vec<FieldElement> =
call.execution.retdata.0.iter().map(|f| (*f).into()).collect();

let builtin_counter = call.vm_resources.builtin_instance_counter.clone();
let expected_execution_resources = trace::ExecutionResources {
n_steps: call.vm_resources.n_steps as u64,
n_memory_holes: call.vm_resources.n_memory_holes as u64,
builtin_instance_counter: builtin_counter
.into_iter()
.map(|(k, v)| (k, v as u64))
.collect(),
};

let CallExecution { events, l2_to_l1_messages, .. } = call.execution.clone();
let expected_events: Vec<_> = events.into_iter().map(to_ordered_event).collect();
let expected_l2_to_l1_msg: Vec<_> = l2_to_l1_messages
.into_iter()
.map(|m| to_l2_l1_messages(m, expected_contract_address))
.collect();

let expected_call_type = match call.call.call_type {
CallType::Call => trace::CallType::Call,
CallType::Delegate => trace::CallType::Delegate,
};

let expected_entry_point_type = match call.call.entry_point_type {
EntryPointType::External => trace::EntryPointType::External,
EntryPointType::L1Handler => trace::EntryPointType::L1Handler,
EntryPointType::Constructor => trace::EntryPointType::Constructor,
};

let expected_storage_read_values: Vec<FieldElement> =
call.storage_read_values.iter().map(|f| (*f).into()).collect();
let expected_storage_keys: HashSet<FieldElement> =
call.accessed_storage_keys.iter().map(|k| (*k.0.key()).into()).collect();
let expected_inner_calls: Vec<_> =
call.inner_calls.clone().into_iter().map(to_call_info).collect();

let expected_gas_consumed = call.execution.gas_consumed as u128;
let expected_failed = call.execution.failed;

// convert to call info
let call = to_call_info(call.clone());

// assert actual values
assert_eq!(call.contract_address, expected_contract_address);
assert_eq!(call.caller_address, expected_caller_address);
assert_eq!(call.code_address, expected_code_address);
assert_eq!(call.class_hash, expected_class_hash);
assert_eq!(call.entry_point_selector, expected_entry_point_selector);
assert_eq!(call.calldata, expected_calldata);
assert_eq!(call.retdata, expected_retdata);
assert_eq!(call.execution_resources, expected_execution_resources);
assert_eq!(call.events, expected_events);
assert_eq!(call.l2_to_l1_messages, expected_l2_to_l1_msg);
assert_eq!(call.call_type, expected_call_type);
assert_eq!(call.entry_point_type, expected_entry_point_type);
assert_eq!(call.storage_read_values, expected_storage_read_values);
assert_eq!(call.accessed_storage_keys, expected_storage_keys);
assert_eq!(call.inner_calls, expected_inner_calls);
assert_eq!(call.gas_consumed, expected_gas_consumed);
assert_eq!(call.failed, expected_failed);
}
}
Loading