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

feat(forge): Add call tracing support #192

Merged
merged 40 commits into from
Dec 23, 2021
Merged
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b470925
first pass
brockelmore Dec 2, 2021
bfa42a8
fixes
brockelmore Dec 2, 2021
7106cd7
fmt
brockelmore Dec 2, 2021
f83b226
better fmting
brockelmore Dec 2, 2021
bf17ebb
updates
brockelmore Dec 2, 2021
23687c4
fmt
brockelmore Dec 2, 2021
f4671a9
updates
brockelmore Dec 11, 2021
e61cbda
Merge branch 'master' into brock/tracing
brockelmore Dec 11, 2021
f397646
fmt
brockelmore Dec 11, 2021
95cb702
fix after master merge
brockelmore Dec 11, 2021
0594faa
fix tests post master merge
brockelmore Dec 11, 2021
34f75e2
warning fixes
brockelmore Dec 11, 2021
62406e1
fmt
brockelmore Dec 11, 2021
79d210f
lots of fixes
brockelmore Dec 12, 2021
4b91be7
fmt
brockelmore Dec 12, 2021
3f8c2b7
fix
brockelmore Dec 12, 2021
cc7e2ed
cyan color
brockelmore Dec 12, 2021
f558e82
fixes
brockelmore Dec 12, 2021
ba514cb
prettier raw logs + parse setup contracts
brockelmore Dec 12, 2021
133685f
Merge branch 'master' into brock/tracing
brockelmore Dec 12, 2021
36b4cf6
update diff_score threshold
brockelmore Dec 12, 2021
c827d33
better printing
brockelmore Dec 16, 2021
58c6c1b
remove integration tests
brockelmore Dec 22, 2021
38ca049
Merge branch 'master' into brock/tracing
brockelmore Dec 22, 2021
1f759c0
improvements
brockelmore Dec 23, 2021
637c50a
improvements + fmt + clippy
brockelmore Dec 23, 2021
08ee0da
fixes
brockelmore Dec 23, 2021
6271684
more cleanup
brockelmore Dec 23, 2021
ee88892
cleanup and verbosity > 3 setup print
brockelmore Dec 23, 2021
dc10a70
refactor printing
brockelmore Dec 23, 2021
c93da0a
documentation + cleanup
brockelmore Dec 23, 2021
6c825a0
fix negative number printing
brockelmore Dec 23, 2021
54a975f
fix tests to match master and fix tracing_enabled
brockelmore Dec 23, 2021
06eafba
Merge branch 'master' into brock/tracing
brockelmore Dec 23, 2021
a918468
fix unnecessary trace_index set
brockelmore Dec 23, 2021
57affc9
Merge branch 'brock/tracing' of https://github.com/gakonst/foundry in…
brockelmore Dec 23, 2021
b2e9922
refactor runner tracing + tracing_enabled
brockelmore Dec 23, 2021
bb9a707
nits + value printing
brockelmore Dec 23, 2021
4660e3c
Merge branch 'master' into brock/tracing
brockelmore Dec 23, 2021
90d81d2
last nits
brockelmore Dec 23, 2021
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
Prev Previous commit
Next Next commit
refactor printing
  • Loading branch information
brockelmore committed Dec 23, 2021
commit dc10a70f4d066021ffea722adadb9d974f57cd7b
248 changes: 126 additions & 122 deletions evm-adapters/src/call_tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ impl CallTraceArena {
contracts,
identified_contracts,
evm,
left.clone(),
&left,
);
println!(
"{} └─ {} {} bytes of code",
Expand Down Expand Up @@ -196,7 +196,7 @@ impl CallTraceArena {
contracts,
identified_contracts,
evm,
left.clone(),
&left,
);
println!(
"{} └─ {} {} bytes of code",
Expand All @@ -206,14 +206,19 @@ impl CallTraceArena {
);
} else {
// we couldn't identify, print the children and logs without the abi
println!("{}→ new <Unknown>@{}", left, trace.addr);
println!(
"{}{} <Unknown>@{}",
left,
Colour::Yellow.paint("→ new"),
trace.addr
);
self.print_children_and_logs(
idx,
None,
contracts,
identified_contracts,
evm,
left.clone(),
&left,
);
println!(
"{} └─ {} {} bytes of code",
Expand All @@ -227,15 +232,14 @@ impl CallTraceArena {
} else {
match (abi, name) {
(Some(abi), Some(name)) => {
let output =
Self::print_func_call(trace, Some(&abi), Some(&name), color, &left);
let output = trace.print_func_call(Some(&abi), Some(&name), color, &left);
self.print_children_and_logs(
idx,
Some(&abi),
contracts,
identified_contracts,
evm,
left.clone(),
&left,
);
Self::print_output(color, output, left);
}
Expand All @@ -248,14 +252,14 @@ impl CallTraceArena {
// re-enter this function at this level if we found the contract
self.pretty_print(idx, contracts, identified_contracts, evm, left);
} else {
let output = Self::print_func_call(trace, None, None, color, &left);
let output = trace.print_func_call(None, None, color, &left);
self.print_children_and_logs(
idx,
None,
contracts,
identified_contracts,
evm,
left.clone(),
&left,
);
Self::print_output(color, output, left);
}
Expand All @@ -271,11 +275,11 @@ impl CallTraceArena {
contracts: &BTreeMap<String, (Abi, Vec<u8>)>,
identified_contracts: &mut BTreeMap<H160, (String, Abi)>,
evm: &'a E,
left: String,
left: &str,
) {
self.arena[node_idx].ordering.iter().for_each(|ordering| match ordering {
LogCallOrder::Log(index) => {
self.print_log(&self.arena[node_idx].logs[*index], abi, &left);
self.arena[node_idx].print_log(*index, abi, left);
}
LogCallOrder::Call(index) => {
self.pretty_print(
Expand All @@ -289,80 +293,49 @@ impl CallTraceArena {
});
}

/// Prints function call, optionally returning the decoded output
pub fn print_func_call(
trace: &CallTrace,
abi: Option<&Abi>,
name: Option<&String>,
color: Colour,
left: &str,
) -> Output {
if let (Some(abi), Some(name)) = (abi, name) {
if trace.data.len() >= 4 {
for (func_name, overloaded_funcs) in abi.functions.iter() {
for func in overloaded_funcs.iter() {
if func.selector() == trace.data[0..4] {
let mut strings = "".to_string();
if !trace.data[4..].is_empty() {
let params = func
.decode_input(&trace.data[4..])
.expect("Bad func data decode");
strings = params
.into_iter()
.map(format_token)
.collect::<Vec<String>>()
.join(", ");
}

println!(
"{}[{}] {}::{}({})",
left,
trace.cost,
color.paint(name),
color.paint(func_name),
strings
);

if !trace.output.is_empty() {
return Output::Token(
func.decode_output(&trace.output[..])
.expect("Bad func output decode"),
)
} else {
return Output::Raw(vec![])
}
}
pub fn print_output(color: Colour, output: Output, left: String) {
match output {
Output::Token(token) => {
let strings =
token.into_iter().map(format_token).collect::<Vec<String>>().join(", ");
println!(
"{} └─ {} {}",
left.replace("├─", "│").replace("└─", " "),
color.paint("←"),
if strings.is_empty() { "()" } else { &*strings }
);
}
Output::Raw(bytes) => {
println!(
"{} └─ {} {}",
left.replace("├─", "│").replace("└─", " "),
color.paint("←"),
if bytes.is_empty() {
"()".to_string()
} else {
"0x".to_string() + &hex::encode(&bytes)
}
}
} else {
// fallback function
println!("{}[{}] {}::fallback()", left, trace.cost, color.paint(name),);

return Output::Raw(trace.output[..].to_vec())
);
}
}

println!(
"{}[{}] {}::{}({})",
left,
trace.cost,
color.paint(format!("{}", trace.addr)),
if trace.data.len() >= 4 {
hex::encode(&trace.data[0..4])
} else {
hex::encode(&trace.data[..])
},
if trace.data.len() >= 4 {
hex::encode(&trace.data[4..])
} else {
hex::encode(&vec![][..])
}
);

Output::Raw(trace.output[..].to_vec())
}
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct CallTraceNode {
pub parent: Option<usize>,
pub children: Vec<usize>,
pub idx: usize,
pub trace: CallTrace,
/// Logs
#[serde(skip)]
pub logs: Vec<RawLog>,
pub ordering: Vec<LogCallOrder>,
}

pub fn print_log(&self, log: &RawLog, abi: Option<&Abi>, left: &str) {
impl CallTraceNode {
pub fn print_log(&self, index: usize, abi: Option<&Abi>, left: &str) {
let log = &self.logs[index];
let right = " ├─ ";
if let Some(abi) = abi {
for (event_name, overloaded_events) in abi.events.iter() {
Expand Down Expand Up @@ -410,48 +383,6 @@ impl CallTraceArena {
Colour::Cyan.paint(format!("0x{}", hex::encode(&log.data)))
)
}

pub fn print_output(color: Colour, output: Output, left: String) {
match output {
Output::Token(token) => {
let strings = token
.iter()
.map(|param| format!("{}", param))
.collect::<Vec<String>>()
.join(", ");
println!(
"{} └─ {} {}",
left.replace("├─", "│").replace("└─", " "),
color.paint("←"),
if strings.is_empty() { "()" } else { &*strings }
);
}
Output::Raw(bytes) => {
println!(
"{} └─ {} {}",
left.replace("├─", "│").replace("└─", " "),
color.paint("←"),
if bytes.is_empty() {
"()".to_string()
} else {
"0x".to_string() + &hex::encode(&bytes)
}
);
}
}
}
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct CallTraceNode {
pub parent: Option<usize>,
pub children: Vec<usize>,
pub idx: usize,
pub trace: CallTrace,
/// Logs
#[serde(skip)]
pub logs: Vec<RawLog>,
pub ordering: Vec<LogCallOrder>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -489,6 +420,79 @@ impl CallTrace {
self.data = new_trace.data;
self.addr = new_trace.addr;
}

/// Prints function call, returning the decoded or raw output
pub fn print_func_call(
&self,
abi: Option<&Abi>,
name: Option<&String>,
color: Colour,
left: &str,
) -> Output {
if let (Some(abi), Some(name)) = (abi, name) {
if self.data.len() >= 4 {
for (func_name, overloaded_funcs) in abi.functions.iter() {
for func in overloaded_funcs.iter() {
if func.selector() == self.data[0..4] {
let mut strings = "".to_string();
if !self.data[4..].is_empty() {
let params = func
.decode_input(&self.data[4..])
.expect("Bad func data decode");
strings = params
.into_iter()
.map(format_token)
.collect::<Vec<String>>()
.join(", ");
}

println!(
"{}[{}] {}::{}({})",
left,
self.cost,
color.paint(name),
color.paint(func_name),
strings
);

if !self.output.is_empty() {
return Output::Token(
func.decode_output(&self.output[..])
.expect("Bad func output decode"),
)
} else {
return Output::Raw(vec![])
}
}
}
}
} else {
// fallback function
println!("{}[{}] {}::fallback()", left, self.cost, color.paint(name),);

return Output::Raw(self.output[..].to_vec())
}
}

println!(
"{}[{}] {}::{}({})",
left,
self.cost,
color.paint(format!("{}", self.addr)),
if self.data.len() >= 4 {
hex::encode(&self.data[0..4])
} else {
hex::encode(&self.data[..])
},
if self.data.len() >= 4 {
hex::encode(&self.data[4..])
} else {
hex::encode(&vec![][..])
}
);

Output::Raw(self.output[..].to_vec())
}
}

fn format_token(param: ethers::abi::Token) -> String {
Expand Down