Skip to content

Commit

Permalink
trace logs and storage (#2739)
Browse files Browse the repository at this point in the history
  • Loading branch information
ermalkaleci committed Apr 17, 2024
1 parent a3e2aae commit 344699e
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 74 deletions.
128 changes: 128 additions & 0 deletions modules/evm-bridge/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,131 @@ fn liquidation_err_fails_as_expected() {
);
});
}

#[cfg(feature = "tracing")]
#[test]
fn tracing_should_work() {
use module_evm::runner::tracing;
use primitives::evm::tracing::TracerConfig;

ExtBuilder::default()
.balances(vec![(alice(), 1_000_000_000_000), (bob(), 1_000_000_000_000)])
.build()
.execute_with(|| {
deploy_contracts();
let mut tracer = tracing::Tracer::new(TracerConfig::CallTracer);
tracing::using(&mut tracer, || {
assert_err!(
EVMBridge::<Runtime>::transfer(
InvokeContext {
contract: erc20_address(),
sender: bob_evm_addr(),
origin: bob_evm_addr(),
},
alice_evm_addr(),
10
),
Error::<Runtime>::ExecutionRevert
);
});
let expected = r#"[
{
"type": "CALL",
"from": "0x1000000000000000000000000000000000000002",
"to": "0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643",
"input": "0xa9059cbb0000000000000000000000001000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a",
"value": "0x0",
"gas": 200000,
"gasUsed": 200000,
"output": null,
"error": null,
"revertReason": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002645524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63650000000000000000000000000000000000000000000000000000",
"depth": 0,
"logs": [
{
"sLoad": {
"address": "0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643",
"index": "0xfb750de6f7d0583f749efc558ce6626b24fed04efd7219dc3f4294c408699e8c",
"value": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
}
],
"calls": []
}
]"#;
let expected = serde_json::from_str::<Vec<tracing::CallTrace>>(expected).unwrap();
assert_eq!(tracer.finalize(), tracing::TraceOutcome::Calls(expected));

tracing::using(&mut tracer, || {
assert_ok!(EVMBridge::<Runtime>::transfer(
InvokeContext {
contract: erc20_address(),
sender: alice_evm_addr(),
origin: alice_evm_addr(),
},
bob_evm_addr(),
100
));
});

let expected = r#"[
{
"type": "CALL",
"from": "0x1000000000000000000000000000000000000001",
"to": "0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643",
"input": "0xa9059cbb00000000000000000000000010000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000064",
"value": "0x0",
"gas": 200000,
"gasUsed": 51929,
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
"error": null,
"revertReason": null,
"depth": 0,
"logs": [
{
"sLoad": {
"address": "0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643",
"index": "0xe6f18b3f6d2cdeb50fb82c61f7a7a249abf7b534575880ddcfde84bba07ce81d",
"value": "0x00000000000000000000000000000000000000000000152d02c7e14af6800000"
}
},
{
"sStore": {
"address": "0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643",
"index": "0xe6f18b3f6d2cdeb50fb82c61f7a7a249abf7b534575880ddcfde84bba07ce81d",
"value": "0x00000000000000000000000000000000000000000000152d02c7e14af67fff9c"
}
},
{
"sLoad": {
"address": "0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643",
"index": "0xfb750de6f7d0583f749efc558ce6626b24fed04efd7219dc3f4294c408699e8c",
"value": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
{
"sStore": {
"address": "0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643",
"index": "0xfb750de6f7d0583f749efc558ce6626b24fed04efd7219dc3f4294c408699e8c",
"value": "0x0000000000000000000000000000000000000000000000000000000000000064"
}
},
{
"log": {
"address": "0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000001000000000000000000000000000000000000001",
"0x0000000000000000000000001000000000000000000000000000000000000002"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000000064"
}
}
],
"calls": []
}
]"#;
let expected = serde_json::from_str::<Vec<tracing::CallTrace>>(expected).unwrap();
assert_eq!(tracer.finalize(), tracing::TraceOutcome::Calls(expected));
});
}
5 changes: 5 additions & 0 deletions modules/evm/src/runner/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,11 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler
}

fn log(&mut self, address: H160, topics: Vec<H256>, data: Vec<u8>) -> Result<(), ExitError> {
event!(Log {
address,
topics: &topics,
data: &data,
});
self.state.log(address, topics, data);
Ok(())
}
Expand Down
136 changes: 65 additions & 71 deletions modules/evm/src/runner/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use module_evm_utility::{
use sp_core::{H160, H256, U256};
use sp_std::prelude::*;

pub use primitives::evm::tracing::{CallTrace, CallType, OpcodeConfig, Step, TraceOutcome, TracerConfig};
pub use primitives::evm::tracing::{CallTrace, CallType, LogTrace, OpcodeConfig, Step, TraceOutcome, TracerConfig};

#[derive(Debug, Copy, Clone)]
pub enum Event<'a> {
Expand Down Expand Up @@ -84,6 +84,11 @@ pub enum Event<'a> {
Enter {
depth: u32,
},
Log {
address: H160,
topics: &'a Vec<H256>,
data: &'a Vec<u8>,
},
}

pub struct Tracer {
Expand Down Expand Up @@ -220,7 +225,20 @@ impl Tracer {
step.gas = self.gas;
}
}
_ => {}
evm_runtime::tracing::Event::SLoad { address, index, value } => {
if !self.trace_call() {
return;
}
let trace = self.stack.last_mut().expect("missing call trace");
trace.logs.push(LogTrace::SLoad { address, index, value });
}
evm_runtime::tracing::Event::SStore { address, index, value } => {
if !self.trace_call() {
return;
}
let trace = self.stack.last_mut().expect("missing call trace");
trace.logs.push(LogTrace::SStore { address, index, value });
}
};
}

Expand Down Expand Up @@ -263,12 +281,7 @@ impl Tracer {
input: input.to_vec(),
value: transfer.as_ref().map_or(U256::zero(), |x| x.value),
gas: target_gas.unwrap_or(self.gas),
gas_used: 0,
output: None,
error: None,
revert_reason: None,
calls: Vec::new(),
depth: 0,
..Default::default()
});
}
Event::TransactCall {
Expand All @@ -285,12 +298,7 @@ impl Tracer {
input: data.to_vec(),
value,
gas: gas_limit,
gas_used: 0,
output: None,
error: None,
revert_reason: None,
calls: Vec::new(),
depth: 0,
..Default::default()
});
}
Event::Create {
Expand All @@ -307,12 +315,7 @@ impl Tracer {
input: init_code.to_vec(),
value,
gas: target_gas.unwrap_or(self.gas),
gas_used: 0,
output: None,
error: None,
revert_reason: None,
calls: Vec::new(),
depth: 0,
..Default::default()
});
}
Event::TransactCreate {
Expand All @@ -337,75 +340,66 @@ impl Tracer {
input: init_code.to_vec(),
value,
gas: gas_limit,
gas_used: 0,
output: None,
error: None,
revert_reason: None,
calls: Vec::new(),
depth: 0,
..Default::default()
});
}
Event::Suicide {
address,
target,
balance,
} => {
if let Some(trace) = self.stack.last_mut() {
trace.calls.push(CallTrace {
call_type: CallType::SUICIDE,
from: address,
to: target,
input: vec![],
value: balance,
gas: 0,
gas_used: 0,
output: None,
error: None,
revert_reason: None,
calls: Vec::new(),
depth: 0,
});
}
let trace = self.stack.last_mut().expect("missing call trace");
trace.calls.push(CallTrace {
call_type: CallType::SUICIDE,
from: address,
to: target,
value: balance,
..Default::default()
});
}
Event::Exit { reason, return_value } => {
if let Some(mut trace) = self.stack.pop() {
match reason {
ExitReason::Succeed(ExitSucceed::Returned) => {
if !return_value.is_empty() {
trace.output = Some(return_value.to_vec());
}
let mut trace = self.stack.pop().expect("missing call trace");
match reason {
ExitReason::Succeed(ExitSucceed::Returned) => {
if !return_value.is_empty() {
trace.output = Some(return_value.to_vec());
}
ExitReason::Succeed(_) => {}
ExitReason::Error(e) => trace.error = Some(e.stringify().as_bytes().to_vec()),
ExitReason::Revert(_) => {
if !return_value.is_empty() {
trace.revert_reason = Some(return_value.to_vec());
}
}
ExitReason::Succeed(_) => {}
ExitReason::Error(e) => trace.error = Some(e.stringify().as_bytes().to_vec()),
ExitReason::Revert(_) => {
if !return_value.is_empty() {
trace.revert_reason = Some(return_value.to_vec());
}
ExitReason::Fatal(e) => trace.error = Some(e.stringify().as_bytes().to_vec()),
}
ExitReason::Fatal(e) => trace.error = Some(e.stringify().as_bytes().to_vec()),
}

trace.gas_used = trace.gas.saturating_sub(self.gas);
trace.gas_used = trace.gas.saturating_sub(self.gas);

if let Some(index) = self.calls.iter().position(|x| x.depth > trace.depth) {
let mut subcalls = self.calls.drain(index..).collect::<Vec<_>>();
if matches!(reason, ExitReason::Succeed(ExitSucceed::Suicided)) {
let mut suicide_call = trace.calls.pop().expect("suicide call should be injected");
suicide_call.depth = trace.depth + 1;
subcalls.push(suicide_call);
}
trace.calls = subcalls;
if let Some(index) = self.calls.iter().position(|x| x.depth > trace.depth) {
let mut subcalls = self.calls.drain(index..).collect::<Vec<_>>();
if matches!(reason, ExitReason::Succeed(ExitSucceed::Suicided)) {
let mut suicide_call = trace.calls.pop().expect("suicide call should be injected");
suicide_call.depth = trace.depth + 1;
subcalls.push(suicide_call);
}

self.calls.push(trace);
} else {
panic!("exit event without call event")
trace.calls = subcalls;
}

self.calls.push(trace);
}
Event::Enter { depth } => {
if let Some(event) = self.stack.last_mut() {
event.depth = depth;
}
let trace = self.stack.last_mut().expect("missing call trace");
trace.depth = depth;
}
Event::Log { address, topics, data } => {
let trace = self.stack.last_mut().expect("missing call trace");
trace.logs.push(LogTrace::Log {
address,
topics: topics.clone(),
data: data.clone(),
});
}
}
}
Expand Down
Loading

0 comments on commit 344699e

Please sign in to comment.