From 71b3bfe143e2d176a3b1ab8917aae4e2d9444215 Mon Sep 17 00:00:00 2001 From: Joshua Colvin Date: Sat, 9 Mar 2024 11:56:44 -0700 Subject: [PATCH 1/8] Initial implementation of wasmbinary to wat --- arbitrator/Cargo.lock | 7 + arbitrator/prover/src/binary.rs | 4 + arbitrator/prover/src/print.rs | 267 ++++++++++++++++++++++++++++- arbitrator/stylus/Cargo.toml | 1 + arbitrator/stylus/src/test/misc.rs | 29 +++- 5 files changed, 301 insertions(+), 7 deletions(-) diff --git a/arbitrator/Cargo.lock b/arbitrator/Cargo.lock index d54403153..7ab57c57c 100644 --- a/arbitrator/Cargo.lock +++ b/arbitrator/Cargo.lock @@ -663,6 +663,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hashbrown" version = "0.12.3" @@ -1647,6 +1653,7 @@ dependencies = [ "derivative", "eyre", "fnv", + "glob", "hex", "libc", "num-bigint", diff --git a/arbitrator/prover/src/binary.rs b/arbitrator/prover/src/binary.rs index 2fafbcb42..927bf188c 100644 --- a/arbitrator/prover/src/binary.rs +++ b/arbitrator/prover/src/binary.rs @@ -673,4 +673,8 @@ impl<'a> WasmBinary<'a> { } Ok(func) } + + pub fn name(&self) -> &str { + &self.names.module + } } diff --git a/arbitrator/prover/src/print.rs b/arbitrator/prover/src/print.rs index 138a01f4b..8ce038ac8 100644 --- a/arbitrator/prover/src/print.rs +++ b/arbitrator/prover/src/print.rs @@ -2,9 +2,10 @@ // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE use crate::{ + binary::{ExportKind, WasmBinary}, host::InternalFunc, machine::Module, - value::{FunctionType, Value}, + value::{ArbValueType, FunctionType, Value}, wavm::{self, Opcode}, }; use arbutil::Color; @@ -12,15 +13,22 @@ use fnv::FnvHashSet as HashSet; use num_traits::FromPrimitive; use std::fmt::{self, Display}; use wasmer_types::WASM_PAGE_SIZE; +use wasmparser::{DataKind, ElementItem, ElementKind, Operator}; + +impl ArbValueType { + fn wat_string(&self, index: usize, name_args: bool) -> String { + match name_args { + true => format!("{} {}", format!("$arg{index}").pink(), self.mint()), + false => format!("{}", self.mint()), + } + } +} impl FunctionType { fn wat_string(&self, name_args: bool) -> String { let params = if !self.inputs.is_empty() { let inputs = self.inputs.iter().enumerate(); - let params = inputs.fold(String::new(), |acc, (j, ty)| match name_args { - true => format!("{acc} {} {}", format!("$arg{j}").pink(), ty.mint()), - false => format!("{acc} {}", ty.mint()), - }); + let params = inputs.fold(String::new(), |acc, (j, ty)| format!("{acc} {}", ty.wat_string(j, name_args))); format!(" ({}{params})", "param".grey()) } else { String::new() @@ -28,7 +36,7 @@ impl FunctionType { let results = if !self.outputs.is_empty() { let outputs = self.outputs.iter(); - let results = outputs.fold(String::new(), |acc, t| format!("{acc} {t}")); + let results = outputs.fold(String::new(), |acc, t| format!("{acc} {}", t.wat_string(0, false))); format!(" ({}{})", "result".grey(), results.mint()) } else { String::new() @@ -301,3 +309,250 @@ impl Display for Module { Ok(()) } } + +impl WasmBinary<'_> { + fn func_name(&self, i: u32) -> String { + match self.maybe_func_name(i) { + Some(func) => format!("${func}"), + None => format!("$func_{i}"), + } + .pink() + } + + fn raw_func_name(&self, i: u32) -> String { + match self.maybe_func_name(i) { + Some(func) => format!("${func}"), + None => format!("{i}"), + } + .pink() + } + + fn maybe_func_name(&self, i: u32) -> Option { + if let Some(name) = self.names.functions.get(&i) { + Some(name.to_owned()) + } else { + let internals_offset = (self.imports.len() + self.codes.len()) as u32; + if i >= internals_offset { + InternalFunc::from_u32(i - internals_offset).map(|f| format!("{f:?}")) + } else { + None + } + } + } + + fn func_type(&self, i: u32) -> String { + if let Some(_) = self.names.functions.get(&i) { + self.types[i as usize].wat_string(false) + } else { + let internals_offset = (self.imports.len() + self.codes.len()) as u32; + if i >= internals_offset { + if let Some(internal) = InternalFunc::from_u32(i - internals_offset) { + return internal.ty().wat_string(false); + } + } + String::default() + } + } +} + +impl<'a> Display for WasmBinary<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut pad = 0; + + macro_rules! w { + ($($args:expr),*) => {{ + let text = format!($($args),*); + write!(f, "{:pad$}{text}", "")?; + }}; + } + macro_rules! wln { + ($($args:expr),*) => {{ + w!($($args),*); + writeln!(f)?; + }}; + } + + wln!("({} {}", "module".grey(), self.name().mint()); + pad += 4; + + for ty in &*self.types { + let ty = ty.wat_string(false); + wln!("({} ({}{ty}))", "type".grey(), "func".grey()); + } + + for import in self.imports.iter() { + wln!( + r#"({} "{}" "{}" ({} {}{}))"#, + "import".grey(), + import.module.pink(), + import.name.pink(), + "func".grey(), + self.func_name(import.offset), + self.func_type(import.offset) + ); + } + + wln!(""); + + for (i, ty) in self.tables.iter().enumerate() { + let initial = format!("{}", ty.initial).mint(); + let max = ty.maximum.map(|x| format!(" {x}")).unwrap_or_default(); + let type_str = format!("{:?}", ty.element_type).mint(); + wln!( + "({} {} {initial} {}{type_str})", + "table".grey(), + format!("{i}").pink(), + max.mint() + ); + } + + for elem in &self.elements { + let (table_index, mut init) = match elem.kind{ + ElementKind::Active { + table_index, + offset_expr, + } => (table_index, offset_expr.get_operators_reader()), + _ => continue, + }; + let offset = match (init.read(), init.read(), init.eof()) { + (Ok(Operator::I32Const { value }), Ok(Operator::End), true) => value as usize, + _ => continue, + }; + + let Ok(mut item_reader) = elem.items.get_items_reader() else { + continue; + }; + let item_count = item_reader.get_count(); + w!( + "({} {} (i32.const {})", + "elem".grey(), + format!("{table_index}").pink(), + format!("{offset}").mint() + ); + for _ in 0..item_count { + let Ok(item) = item_reader.read() else { + continue; + }; + let ElementItem::Func(index) = item else { + continue; + }; + write!(f, " {}", self.raw_func_name(index))?; + } + writeln!(f, ")")?; + } + + for limits in self.memories.iter() { + let max = match limits.maximum { + Some(max) => format!(" {max}"), + None => "".to_string(), + }; + wln!( + "({} {}{})", + "memory".grey(), + limits.initial.mint(), + max.mint() + ); + } + + for data in &self.datas { + let (_, mut init) = match data.kind { + DataKind::Active { + memory_index, + offset_expr, + } => (memory_index, offset_expr.get_operators_reader()), + _ => continue, + }; + + let offset = match (init.read(), init.read(), init.eof()) { + (Ok(Operator::I32Const { value }), Ok(Operator::End), true) => value as usize, + _ => continue, + }; + + let data = String::from_utf8( + data.data.iter() + .flat_map(|b| std::ascii::escape_default(*b)) + .collect::>() + ).unwrap(); + wln!( + r#"({} (i32.const {}) "{}")"#, + "data".grey(), + offset.mint(), + data.mint() + ); + } + + wln!(""); + + for (i, g) in self.globals.iter().enumerate() { + wln!("({} {i} {} ({})", "global".grey(), g.ty().mint(), g.wat_string()); + } + + for (export_name, (index, kind)) in &self.exports { + use ExportKind as E; + let (kind, name) = match kind { + E::Func => ("func", self.raw_func_name(*index)), + E::Table => ("table", format!("{index}")), + E::Memory => ("memory", format!("{index}")), + E::Global => ("global", format!("{index}")), + E::Tag => ("tag", format!("{index}")), + }; + wln!("({} \"{}\" ({} {}))", "export".grey(), export_name.pink(), kind.grey(), name.pink()); + } + + for (i, type_idx) in self.functions.iter().enumerate() { + let i1 = i as u32; + + let export_str = match self.maybe_func_name(i1 + 1) { + Some(name) => { + format!(r#" ({} "{}")"#, "export".grey(), name.pink()) + } + None => format!(" ").pink(), + }; + w!( + "({}{}{}", + "func".grey(), + export_str, + self.types[*type_idx as usize].wat_string(true) + ); + + wln!(""); + pad += 4; + for local in self.codes[i].locals.iter() { + wln!("(local {})", local.value.wat_string(local.index as usize, false)); + } + for op in &self.codes[i].expr { + let op_str = format!("{:?}", op).grey(); + wln!("{op_str}"); + } + pad -= 4; + wln!(")"); + } + + wln!(""); + + if let Some(start) = self.start { + wln!("({} {})", "start".grey(), self.raw_func_name(start)); + } + pad -= 4; + wln!(")"); + Ok(()) + } +} + +impl Value { + fn wat_string(&self) -> String { + match self { + Value::I32(value) => { + format!("{} {}", "i32.const".mint(), *value as i32) + } + Value::I64(value) => { + format!("{} {}", "i64.const".mint(), *value as i64) + } + Value::F32(value) => format!("{} {}", "f32.const".mint(), *value), + Value::F64(value) => format!("{} {}", "f64.const".mint(), *value), + Value::RefNull => format!("null").mint(), + Value::FuncRef(func) => format!("{} {func}", "func".mint()), + Value::InternalRef(pc) => format!("{pc}"), + } + } +} \ No newline at end of file diff --git a/arbitrator/stylus/Cargo.toml b/arbitrator/stylus/Cargo.toml index 01867e307..9ad60e8a3 100644 --- a/arbitrator/stylus/Cargo.toml +++ b/arbitrator/stylus/Cargo.toml @@ -26,6 +26,7 @@ hex = "0.4.3" [dev-dependencies] num-bigint = "0.4.4" +glob = "0.3.1" [features] default = ["rayon", "singlepass_rayon"] diff --git a/arbitrator/stylus/src/test/misc.rs b/arbitrator/stylus/src/test/misc.rs index 868bc2d16..b8acfd641 100644 --- a/arbitrator/stylus/src/test/misc.rs +++ b/arbitrator/stylus/src/test/misc.rs @@ -8,7 +8,9 @@ use crate::{ test::{check_instrumentation, new_test_machine}, }; use eyre::Result; -use prover::programs::{prelude::*, start::STYLUS_START}; +use glob::glob; +use prover::{binary, programs::{prelude::*, start::STYLUS_START}}; +use std::path::Path; use wasmer::{imports, Function}; #[test] @@ -80,3 +82,28 @@ fn test_console() -> Result<()> { machine.call_user_func(STYLUS_START, vec![], ink)?; check_instrumentation(native, machine) } + +#[test] +fn test_wasm_wat() -> Result<()> { + for filename in glob("../prover/test-cases/*.wat").expect("Failed to find wat files") { + if filename.is_err() { + return Err(filename.err().unwrap().into()) + } + let filename = filename.unwrap(); + let filename = match filename.to_str() { + Some(filename) => {filename}, + None => {continue}, + }; + let data = std::fs::read(filename).unwrap(); + let wasm = wasmer::wat2wasm(&data).unwrap(); + let bin = binary::parse(&wasm, Path::new("user")).unwrap(); + println!(); + println!("File: {filename}"); + let data2 = bin.to_string(); + print!("{}", data2); + //let wasm2 = wasmer::wat2wasm(&data2.as_bytes()).unwrap(); + //let bin2 = binary::parse(&wasm2, Path::new("user")).unwrap(); + //assert_eq!(bin, bin2); + } + Ok(()) +} From e160faf4479024321c385435adabb268a192f655 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 21 Mar 2024 21:49:01 -0600 Subject: [PATCH 2/8] cargo fmt --- arbitrator/prover/src/print.rs | 60 ++++++++++++++++++++---------- arbitrator/stylus/src/test/misc.rs | 11 ++++-- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/arbitrator/prover/src/print.rs b/arbitrator/prover/src/print.rs index 8ce038ac8..74583b02c 100644 --- a/arbitrator/prover/src/print.rs +++ b/arbitrator/prover/src/print.rs @@ -28,7 +28,9 @@ impl FunctionType { fn wat_string(&self, name_args: bool) -> String { let params = if !self.inputs.is_empty() { let inputs = self.inputs.iter().enumerate(); - let params = inputs.fold(String::new(), |acc, (j, ty)| format!("{acc} {}", ty.wat_string(j, name_args))); + let params = inputs.fold(String::new(), |acc, (j, ty)| { + format!("{acc} {}", ty.wat_string(j, name_args)) + }); format!(" ({}{params})", "param".grey()) } else { String::new() @@ -36,7 +38,9 @@ impl FunctionType { let results = if !self.outputs.is_empty() { let outputs = self.outputs.iter(); - let results = outputs.fold(String::new(), |acc, t| format!("{acc} {}", t.wat_string(0, false))); + let results = outputs.fold(String::new(), |acc, t| { + format!("{acc} {}", t.wat_string(0, false)) + }); format!(" ({}{})", "result".grey(), results.mint()) } else { String::new() @@ -316,7 +320,7 @@ impl WasmBinary<'_> { Some(func) => format!("${func}"), None => format!("$func_{i}"), } - .pink() + .pink() } fn raw_func_name(&self, i: u32) -> String { @@ -324,7 +328,7 @@ impl WasmBinary<'_> { Some(func) => format!("${func}"), None => format!("{i}"), } - .pink() + .pink() } fn maybe_func_name(&self, i: u32) -> Option { @@ -382,14 +386,14 @@ impl<'a> Display for WasmBinary<'a> { for import in self.imports.iter() { wln!( - r#"({} "{}" "{}" ({} {}{}))"#, - "import".grey(), - import.module.pink(), - import.name.pink(), - "func".grey(), - self.func_name(import.offset), - self.func_type(import.offset) - ); + r#"({} "{}" "{}" ({} {}{}))"#, + "import".grey(), + import.module.pink(), + import.name.pink(), + "func".grey(), + self.func_name(import.offset), + self.func_type(import.offset) + ); } wln!(""); @@ -407,7 +411,7 @@ impl<'a> Display for WasmBinary<'a> { } for elem in &self.elements { - let (table_index, mut init) = match elem.kind{ + let (table_index, mut init) = match elem.kind { ElementKind::Active { table_index, offset_expr, @@ -469,10 +473,12 @@ impl<'a> Display for WasmBinary<'a> { }; let data = String::from_utf8( - data.data.iter() + data.data + .iter() .flat_map(|b| std::ascii::escape_default(*b)) - .collect::>() - ).unwrap(); + .collect::>(), + ) + .unwrap(); wln!( r#"({} (i32.const {}) "{}")"#, "data".grey(), @@ -484,7 +490,12 @@ impl<'a> Display for WasmBinary<'a> { wln!(""); for (i, g) in self.globals.iter().enumerate() { - wln!("({} {i} {} ({})", "global".grey(), g.ty().mint(), g.wat_string()); + wln!( + "({} {i} {} ({})", + "global".grey(), + g.ty().mint(), + g.wat_string() + ); } for (export_name, (index, kind)) in &self.exports { @@ -496,7 +507,13 @@ impl<'a> Display for WasmBinary<'a> { E::Global => ("global", format!("{index}")), E::Tag => ("tag", format!("{index}")), }; - wln!("({} \"{}\" ({} {}))", "export".grey(), export_name.pink(), kind.grey(), name.pink()); + wln!( + "({} \"{}\" ({} {}))", + "export".grey(), + export_name.pink(), + kind.grey(), + name.pink() + ); } for (i, type_idx) in self.functions.iter().enumerate() { @@ -518,7 +535,10 @@ impl<'a> Display for WasmBinary<'a> { wln!(""); pad += 4; for local in self.codes[i].locals.iter() { - wln!("(local {})", local.value.wat_string(local.index as usize, false)); + wln!( + "(local {})", + local.value.wat_string(local.index as usize, false) + ); } for op in &self.codes[i].expr { let op_str = format!("{:?}", op).grey(); @@ -555,4 +575,4 @@ impl Value { Value::InternalRef(pc) => format!("{pc}"), } } -} \ No newline at end of file +} diff --git a/arbitrator/stylus/src/test/misc.rs b/arbitrator/stylus/src/test/misc.rs index b8acfd641..d6c5f9c09 100644 --- a/arbitrator/stylus/src/test/misc.rs +++ b/arbitrator/stylus/src/test/misc.rs @@ -9,7 +9,10 @@ use crate::{ }; use eyre::Result; use glob::glob; -use prover::{binary, programs::{prelude::*, start::STYLUS_START}}; +use prover::{ + binary, + programs::{prelude::*, start::STYLUS_START}, +}; use std::path::Path; use wasmer::{imports, Function}; @@ -87,12 +90,12 @@ fn test_console() -> Result<()> { fn test_wasm_wat() -> Result<()> { for filename in glob("../prover/test-cases/*.wat").expect("Failed to find wat files") { if filename.is_err() { - return Err(filename.err().unwrap().into()) + return Err(filename.err().unwrap().into()); } let filename = filename.unwrap(); let filename = match filename.to_str() { - Some(filename) => {filename}, - None => {continue}, + Some(filename) => filename, + None => continue, }; let data = std::fs::read(filename).unwrap(); let wasm = wasmer::wat2wasm(&data).unwrap(); From 2e20103c0e1d09e799ae3caca7b02fa9103e6259 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 21 Mar 2024 21:55:48 -0600 Subject: [PATCH 3/8] clippy and reductions --- arbitrator/jit/src/arbcompress.rs | 2 ++ arbitrator/prover/src/binary.rs | 1 + arbitrator/prover/src/host.rs | 2 +- arbitrator/prover/src/machine.rs | 2 +- arbitrator/prover/src/print.rs | 8 ++++---- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/arbitrator/jit/src/arbcompress.rs b/arbitrator/jit/src/arbcompress.rs index 0d8d14bc7..fda251d49 100644 --- a/arbitrator/jit/src/arbcompress.rs +++ b/arbitrator/jit/src/arbcompress.rs @@ -1,6 +1,8 @@ // Copyright 2022-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE +#![allow(clippy::too_many_arguments)] + use crate::caller_env::{JitEnv, JitExecEnv}; use crate::machine::Escape; use crate::machine::WasmEnvMut; diff --git a/arbitrator/prover/src/binary.rs b/arbitrator/prover/src/binary.rs index 927bf188c..91c068a8e 100644 --- a/arbitrator/prover/src/binary.rs +++ b/arbitrator/prover/src/binary.rs @@ -674,6 +674,7 @@ impl<'a> WasmBinary<'a> { Ok(func) } + /// The name of the module. pub fn name(&self) -> &str { &self.names.module } diff --git a/arbitrator/prover/src/host.rs b/arbitrator/prover/src/host.rs index f793bbee5..5239ed350 100644 --- a/arbitrator/prover/src/host.rs +++ b/arbitrator/prover/src/host.rs @@ -496,7 +496,7 @@ lazy_static! { &[ty.clone()], // only type needed is the func itself 0, // ----------------------------------- 0, // impls don't use other internals - &bin.names.module, + bin.name(), ), ty.clone(), &[] // impls don't make calls diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 6dc022128..11bb1355b 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -323,7 +323,7 @@ impl Module { let mut memory = Memory::default(); let mut tables = Vec::new(); let mut host_call_hooks = Vec::new(); - let bin_name = &bin.names.module; + let bin_name = bin.name(); for import in &bin.imports { let module = import.module; let have_ty = &bin.types[import.offset as usize]; diff --git a/arbitrator/prover/src/print.rs b/arbitrator/prover/src/print.rs index 74583b02c..2433b6303 100644 --- a/arbitrator/prover/src/print.rs +++ b/arbitrator/prover/src/print.rs @@ -19,7 +19,7 @@ impl ArbValueType { fn wat_string(&self, index: usize, name_args: bool) -> String { match name_args { true => format!("{} {}", format!("$arg{index}").pink(), self.mint()), - false => format!("{}", self.mint()), + false => self.mint(), } } } @@ -345,7 +345,7 @@ impl WasmBinary<'_> { } fn func_type(&self, i: u32) -> String { - if let Some(_) = self.names.functions.get(&i) { + if self.names.functions.get(&i).is_some() { self.types[i as usize].wat_string(false) } else { let internals_offset = (self.imports.len() + self.codes.len()) as u32; @@ -523,7 +523,7 @@ impl<'a> Display for WasmBinary<'a> { Some(name) => { format!(r#" ({} "{}")"#, "export".grey(), name.pink()) } - None => format!(" ").pink(), + None => " ".pink(), }; w!( "({}{}{}", @@ -570,7 +570,7 @@ impl Value { } Value::F32(value) => format!("{} {}", "f32.const".mint(), *value), Value::F64(value) => format!("{} {}", "f64.const".mint(), *value), - Value::RefNull => format!("null").mint(), + Value::RefNull => "null".mint(), Value::FuncRef(func) => format!("{} {func}", "func".mint()), Value::InternalRef(pc) => format!("{pc}"), } From edc15254814949b0bf30892355b83bb2e246d726 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Thu, 21 Mar 2024 22:23:18 -0600 Subject: [PATCH 4/8] move test and simplify --- arbitrator/Cargo.lock | 2 +- arbitrator/prover/Cargo.toml | 3 ++ arbitrator/prover/src/print.rs | 48 +++++++++++++++++++++--------- arbitrator/stylus/Cargo.toml | 1 - arbitrator/stylus/src/test/misc.rs | 32 +------------------- 5 files changed, 39 insertions(+), 47 deletions(-) diff --git a/arbitrator/Cargo.lock b/arbitrator/Cargo.lock index a30a247fe..51bc902bb 100644 --- a/arbitrator/Cargo.lock +++ b/arbitrator/Cargo.lock @@ -1259,6 +1259,7 @@ dependencies = [ "digest", "eyre", "fnv", + "glob", "hex", "itertools", "lazy_static", @@ -1696,7 +1697,6 @@ dependencies = [ "derivative", "eyre", "fnv", - "glob", "hex", "libc", "num-bigint", diff --git a/arbitrator/prover/Cargo.toml b/arbitrator/prover/Cargo.toml index 6ff6b5173..27dd5ae21 100644 --- a/arbitrator/prover/Cargo.toml +++ b/arbitrator/prover/Cargo.toml @@ -37,6 +37,9 @@ wasmparser.workspace = true num-derive = "0.4.1" num-traits = "0.2.17" +[dev-dependencies] +glob = "0.3.1" + [lib] name = "prover" crate-type = ["staticlib", "lib"] diff --git a/arbitrator/prover/src/print.rs b/arbitrator/prover/src/print.rs index 2433b6303..c2bca113b 100644 --- a/arbitrator/prover/src/print.rs +++ b/arbitrator/prover/src/print.rs @@ -326,7 +326,7 @@ impl WasmBinary<'_> { fn raw_func_name(&self, i: u32) -> String { match self.maybe_func_name(i) { Some(func) => format!("${func}"), - None => format!("{i}"), + None => i.to_string(), } .pink() } @@ -395,7 +395,6 @@ impl<'a> Display for WasmBinary<'a> { self.func_type(import.offset) ); } - wln!(""); for (i, ty) in self.tables.iter().enumerate() { @@ -405,7 +404,7 @@ impl<'a> Display for WasmBinary<'a> { wln!( "({} {} {initial} {}{type_str})", "table".grey(), - format!("{i}").pink(), + i.to_string().pink(), max.mint() ); } @@ -426,14 +425,13 @@ impl<'a> Display for WasmBinary<'a> { let Ok(mut item_reader) = elem.items.get_items_reader() else { continue; }; - let item_count = item_reader.get_count(); w!( "({} {} (i32.const {})", "elem".grey(), format!("{table_index}").pink(), format!("{offset}").mint() ); - for _ in 0..item_count { + for _ in 0..item_reader.get_count() { let Ok(item) = item_reader.read() else { continue; }; @@ -445,11 +443,8 @@ impl<'a> Display for WasmBinary<'a> { writeln!(f, ")")?; } - for limits in self.memories.iter() { - let max = match limits.maximum { - Some(max) => format!(" {max}"), - None => "".to_string(), - }; + for limits in &self.memories { + let max = limits.maximum.map(|x| format!(" {x}")).unwrap_or_default(); wln!( "({} {}{})", "memory".grey(), @@ -486,7 +481,6 @@ impl<'a> Display for WasmBinary<'a> { data.mint() ); } - wln!(""); for (i, g) in self.globals.iter().enumerate() { @@ -541,15 +535,25 @@ impl<'a> Display for WasmBinary<'a> { ); } for op in &self.codes[i].expr { - let op_str = format!("{:?}", op).grey(); + use Operator::*; + + match op { + End | Else => pad = (pad - 4).max(8), + _ => {} + } + + let op_str = format!("{op:?}").grey(); wln!("{op_str}"); + + match op { + Block { .. } | If { .. } | Else | Loop { .. } => pad += 4, + _ => {} + } } pad -= 4; wln!(")"); } - wln!(""); - if let Some(start) = self.start { wln!("({} {})", "start".grey(), self.raw_func_name(start)); } @@ -576,3 +580,19 @@ impl Value { } } } + +#[test] +fn test_wasm_wat() -> eyre::Result<()> { + use crate::binary; + use std::{fs, path::Path}; + + for file in glob::glob("../prover/test-cases/block.wat")? { + let file = file?; + let data = fs::read(&file)?; + let wasm = wasmer::wat2wasm(&data)?; + let bin = binary::parse(&wasm, Path::new("user"))?; + println!("{}", file.to_string_lossy().orange()); + println!("{bin}"); + } + Ok(()) +} diff --git a/arbitrator/stylus/Cargo.toml b/arbitrator/stylus/Cargo.toml index dc11e0818..c957707aa 100644 --- a/arbitrator/stylus/Cargo.toml +++ b/arbitrator/stylus/Cargo.toml @@ -27,7 +27,6 @@ hex = "0.4.3" [dev-dependencies] num-bigint = "0.4.4" -glob = "0.3.1" [features] default = ["rayon", "singlepass_rayon"] diff --git a/arbitrator/stylus/src/test/misc.rs b/arbitrator/stylus/src/test/misc.rs index d6c5f9c09..868bc2d16 100644 --- a/arbitrator/stylus/src/test/misc.rs +++ b/arbitrator/stylus/src/test/misc.rs @@ -8,12 +8,7 @@ use crate::{ test::{check_instrumentation, new_test_machine}, }; use eyre::Result; -use glob::glob; -use prover::{ - binary, - programs::{prelude::*, start::STYLUS_START}, -}; -use std::path::Path; +use prover::programs::{prelude::*, start::STYLUS_START}; use wasmer::{imports, Function}; #[test] @@ -85,28 +80,3 @@ fn test_console() -> Result<()> { machine.call_user_func(STYLUS_START, vec![], ink)?; check_instrumentation(native, machine) } - -#[test] -fn test_wasm_wat() -> Result<()> { - for filename in glob("../prover/test-cases/*.wat").expect("Failed to find wat files") { - if filename.is_err() { - return Err(filename.err().unwrap().into()); - } - let filename = filename.unwrap(); - let filename = match filename.to_str() { - Some(filename) => filename, - None => continue, - }; - let data = std::fs::read(filename).unwrap(); - let wasm = wasmer::wat2wasm(&data).unwrap(); - let bin = binary::parse(&wasm, Path::new("user")).unwrap(); - println!(); - println!("File: {filename}"); - let data2 = bin.to_string(); - print!("{}", data2); - //let wasm2 = wasmer::wat2wasm(&data2.as_bytes()).unwrap(); - //let bin2 = binary::parse(&wasm2, Path::new("user")).unwrap(); - //assert_eq!(bin, bin2); - } - Ok(()) -} From 6fada9fe5ff9573eca2f6ee8218d52435dc8e714 Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Fri, 22 Mar 2024 00:30:12 -0600 Subject: [PATCH 5/8] scoping and simplifications --- arbitrator/prover/src/print.rs | 118 ++++++++++++++------------------- arbitrator/prover/src/test.rs | 2 +- 2 files changed, 49 insertions(+), 71 deletions(-) diff --git a/arbitrator/prover/src/print.rs b/arbitrator/prover/src/print.rs index c2bca113b..598f639ac 100644 --- a/arbitrator/prover/src/print.rs +++ b/arbitrator/prover/src/print.rs @@ -10,6 +10,7 @@ use crate::{ }; use arbutil::Color; use fnv::FnvHashSet as HashSet; +use itertools::Itertools; use num_traits::FromPrimitive; use std::fmt::{self, Display}; use wasmer_types::WASM_PAGE_SIZE; @@ -70,6 +71,22 @@ impl Module { } } +impl Display for ExportKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + ExportKind::Func => "func", + ExportKind::Table => "table", + ExportKind::Memory => "memory", + ExportKind::Global => "global", + ExportKind::Tag => "tag", + } + ) + } +} + impl Display for Module { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut pad = 0; @@ -200,23 +217,17 @@ impl Display for Module { let i1 = i as u32; let padding = 12; - let export_str = match self.maybe_func_name(i1) { + let export = match self.maybe_func_name(i1) { Some(name) => { - let description = if (i1 as usize) < self.host_call_hooks.len() { - "import" - } else { - "export" + let description = match (i1 as usize) < self.host_call_hooks.len() { + true => "import", + false => "export", }; format!(r#" ({} "{}")"#, description.grey(), name.pink()) } None => format!(" $func_{i}").pink(), }; - w!( - "({}{}{}", - "func".grey(), - export_str, - func.ty.wat_string(true) - ); + w!("({}{}{}", "func".grey(), export, func.ty.wat_string(true)); pad += 4; if !func.local_types.is_empty() { @@ -277,20 +288,13 @@ impl Display for Module { MemoryLoad { .. } | MemoryStore { .. } | ReadInboxMessage => { format!(" {:#x}", op.argument_data).mint() } - _ => { - if op.argument_data == 0 { - String::new() - } else { - format!(" UNEXPECTED_ARG: {}", op.argument_data).mint() - } - } + _ => (op.argument_data != 0) + .then(|| format!(" UNEXPECTED_ARG: {}", op.argument_data).mint()) + .unwrap_or_default(), }; - let proof = op - .proving_argument_data - .map(hex::encode) - .unwrap_or_default() - .orange(); + let proof = op.proving_argument_data; + let proof = proof.map(hex::encode).unwrap_or_default().orange(); match labels.get(&j) { Some(label) => { @@ -336,11 +340,9 @@ impl WasmBinary<'_> { Some(name.to_owned()) } else { let internals_offset = (self.imports.len() + self.codes.len()) as u32; - if i >= internals_offset { - InternalFunc::from_u32(i - internals_offset).map(|f| format!("{f:?}")) - } else { - None - } + (i >= internals_offset) + .then(|| InternalFunc::from_u32(i - internals_offset).map(|f| format!("{f:?}"))) + .flatten() } } @@ -384,7 +386,7 @@ impl<'a> Display for WasmBinary<'a> { wln!("({} ({}{ty}))", "type".grey(), "func".grey()); } - for import in self.imports.iter() { + for import in &self.imports { wln!( r#"({} "{}" "{}" ({} {}{}))"#, "import".grey(), @@ -432,13 +434,9 @@ impl<'a> Display for WasmBinary<'a> { format!("{offset}").mint() ); for _ in 0..item_reader.get_count() { - let Ok(item) = item_reader.read() else { - continue; - }; - let ElementItem::Func(index) = item else { - continue; + if let Ok(ElementItem::Func(index)) = item_reader.read() { + write!(f, " {}", self.raw_func_name(index))?; }; - write!(f, " {}", self.raw_func_name(index))?; } writeln!(f, ")")?; } @@ -467,13 +465,7 @@ impl<'a> Display for WasmBinary<'a> { _ => continue, }; - let data = String::from_utf8( - data.data - .iter() - .flat_map(|b| std::ascii::escape_default(*b)) - .collect::>(), - ) - .unwrap(); + let data = data.data.iter().map(|x| format!("\\{x:02x}")).join(""); wln!( r#"({} (i32.const {}) "{}")"#, "data".grey(), @@ -493,14 +485,9 @@ impl<'a> Display for WasmBinary<'a> { } for (export_name, (index, kind)) in &self.exports { - use ExportKind as E; - let (kind, name) = match kind { - E::Func => ("func", self.raw_func_name(*index)), - E::Table => ("table", format!("{index}")), - E::Memory => ("memory", format!("{index}")), - E::Global => ("global", format!("{index}")), - E::Tag => ("tag", format!("{index}")), - }; + let name = (kind == &ExportKind::Func) + .then(|| self.raw_func_name(*index)) + .unwrap_or(index.to_string()); wln!( "({} \"{}\" ({} {}))", "export".grey(), @@ -511,22 +498,16 @@ impl<'a> Display for WasmBinary<'a> { } for (i, type_idx) in self.functions.iter().enumerate() { - let i1 = i as u32; - - let export_str = match self.maybe_func_name(i1 + 1) { - Some(name) => { - format!(r#" ({} "{}")"#, "export".grey(), name.pink()) - } - None => " ".pink(), + let export = match self.maybe_func_name(i as u32) { + Some(name) => format!(r#" ({} "{}")"#, "export".grey(), name.pink()), + None => " ".to_string(), }; - w!( - "({}{}{}", + wln!( + "({}{export}{}", "func".grey(), - export_str, self.types[*type_idx as usize].wat_string(true) ); - wln!(""); pad += 4; for local in self.codes[i].locals.iter() { wln!( @@ -537,17 +518,14 @@ impl<'a> Display for WasmBinary<'a> { for op in &self.codes[i].expr { use Operator::*; - match op { - End | Else => pad = (pad - 4).max(8), - _ => {} + if let End | Else = op { + pad = (pad - 4).max(8); } - let op_str = format!("{op:?}").grey(); - wln!("{op_str}"); + wln!("{}", format!("{op:?}").grey()); - match op { - Block { .. } | If { .. } | Else | Loop { .. } => pad += 4, - _ => {} + if let Block { .. } | If { .. } | Else | Loop { .. } = op { + pad += 4; } } pad -= 4; @@ -586,7 +564,7 @@ fn test_wasm_wat() -> eyre::Result<()> { use crate::binary; use std::{fs, path::Path}; - for file in glob::glob("../prover/test-cases/block.wat")? { + for file in glob::glob("../prover/test-cases/link.wat")? { let file = file?; let data = fs::read(&file)?; let wasm = wasmer::wat2wasm(&data)?; diff --git a/arbitrator/prover/src/test.rs b/arbitrator/prover/src/test.rs index 97170441f..409859ee6 100644 --- a/arbitrator/prover/src/test.rs +++ b/arbitrator/prover/src/test.rs @@ -64,7 +64,7 @@ pub fn test_compress() -> Result<()> { let deflate = brotli::compress(data, 11, 22, dict).unwrap(); let inflate = brotli::decompress(&deflate, dict).unwrap(); assert_eq!(hex::encode(inflate), hex::encode(data)); - assert!(&deflate != &last); + assert!(deflate != last); last = deflate; } Ok(()) From 7571b2f02ff7ff1bb4b1252e155d34a89eed643f Mon Sep 17 00:00:00 2001 From: Rachel Bousfield Date: Fri, 22 Mar 2024 00:35:52 -0600 Subject: [PATCH 6/8] revert glob --- arbitrator/prover/src/print.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbitrator/prover/src/print.rs b/arbitrator/prover/src/print.rs index 598f639ac..8266fa21b 100644 --- a/arbitrator/prover/src/print.rs +++ b/arbitrator/prover/src/print.rs @@ -564,7 +564,7 @@ fn test_wasm_wat() -> eyre::Result<()> { use crate::binary; use std::{fs, path::Path}; - for file in glob::glob("../prover/test-cases/link.wat")? { + for file in glob::glob("../prover/test-cases/*.wat")? { let file = file?; let data = fs::read(&file)?; let wasm = wasmer::wat2wasm(&data)?; From 81ef523e36f9c8e1ba587c24ffb4c5f9218edff4 Mon Sep 17 00:00:00 2001 From: Joshua Colvin Date: Mon, 15 Apr 2024 08:54:00 -0700 Subject: [PATCH 7/8] Work towards fixing function names --- arbitrator/prover/src/print.rs | 74 +++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/arbitrator/prover/src/print.rs b/arbitrator/prover/src/print.rs index 8266fa21b..4cc4852ea 100644 --- a/arbitrator/prover/src/print.rs +++ b/arbitrator/prover/src/print.rs @@ -321,7 +321,7 @@ impl Display for Module { impl WasmBinary<'_> { fn func_name(&self, i: u32) -> String { match self.maybe_func_name(i) { - Some(func) => format!("${func}"), + Some(func) => func, None => format!("$func_{i}"), } .pink() @@ -329,19 +329,62 @@ impl WasmBinary<'_> { fn raw_func_name(&self, i: u32) -> String { match self.maybe_func_name(i) { - Some(func) => format!("${func}"), + Some(func) => func, + None => i.to_string(), + } + .pink() + } + + fn raw_func_index_name(&self, i: u32) -> String { + match self.maybe_func_index_name(i) { + Some(func) => func, + None => i.to_string(), + } + .pink() + } + + fn raw_func_number_name(&self, i: u32) -> String { + match self.maybe_func_number_name(i) { + Some(func) => func, None => i.to_string(), } .pink() } + fn maybe_func_number_name(&self, i: u32) -> Option { + let i:usize = i as usize + self.imports.len(); + if let Some(name) = self.names.functions.get(&(i as u32)) { + Some(name.to_owned()) + } else { + None + } + } + + fn maybe_func_index_name(&self, i: u32) -> Option { + let i:usize = i as usize; + if let Some(name) = self.names.functions.get(&(i as u32)) { + Some(name.to_owned()) + } else { + let internals_offset = (self.imports.len() + self.codes.len()); + (i >= internals_offset) + .then(|| InternalFunc::from_usize(i - internals_offset).map(|f| format!("{f:?}"))) + .flatten() + } + } + fn maybe_func_name(&self, i: u32) -> Option { - if let Some(name) = self.names.functions.get(&i) { + let i:usize = i as usize; + if i < self.imports.len() { + return Some(self.imports[i as usize].name.to_string()) + } + let i = i + self.imports.len() as usize; + + if let Some(name) = self.names.functions.get(&(i as u32)) { Some(name.to_owned()) } else { - let internals_offset = (self.imports.len() + self.codes.len()) as u32; + let internals_offset = (self.imports.len() + self.codes.len()); (i >= internals_offset) - .then(|| InternalFunc::from_u32(i - internals_offset).map(|f| format!("{f:?}"))) + .then(|| InternalFunc::from_usize(i - internals_offset).map(|f| format!("{f:?}"))) .flatten() } } @@ -393,7 +436,7 @@ impl<'a> Display for WasmBinary<'a> { import.module.pink(), import.name.pink(), "func".grey(), - self.func_name(import.offset), + import.offset.to_string(), //self.raw_func_index_name(import.offset), self.func_type(import.offset) ); } @@ -473,7 +516,9 @@ impl<'a> Display for WasmBinary<'a> { data.mint() ); } - wln!(""); + if !self.datas.is_empty() { + wln!(""); + } for (i, g) in self.globals.iter().enumerate() { wln!( @@ -497,25 +542,26 @@ impl<'a> Display for WasmBinary<'a> { ); } - for (i, type_idx) in self.functions.iter().enumerate() { - let export = match self.maybe_func_name(i as u32) { + for (number, type_idx) in self.functions.iter().enumerate() { + let export = match self.maybe_func_number_name(number as u32) { Some(name) => format!(r#" ({} "{}")"#, "export".grey(), name.pink()), None => " ".to_string(), }; wln!( - "({}{export}{}", + "({} {}{export}{}", "func".grey(), + self.raw_func_number_name(number as u32).pink(), self.types[*type_idx as usize].wat_string(true) ); pad += 4; - for local in self.codes[i].locals.iter() { + for local in self.codes[number].locals.iter() { wln!( "(local {})", local.value.wat_string(local.index as usize, false) ); } - for op in &self.codes[i].expr { + for op in &self.codes[number].expr { use Operator::*; if let End | Else = op { @@ -533,7 +579,7 @@ impl<'a> Display for WasmBinary<'a> { } if let Some(start) = self.start { - wln!("({} {})", "start".grey(), self.raw_func_name(start)); + wln!("({} {})", "start".grey(), self.raw_func_index_name(start)); } pad -= 4; wln!(")"); @@ -564,7 +610,7 @@ fn test_wasm_wat() -> eyre::Result<()> { use crate::binary; use std::{fs, path::Path}; - for file in glob::glob("../prover/test-cases/*.wat")? { + for file in glob::glob("../prover/test-cases/dynamic.wat")? { let file = file?; let data = fs::read(&file)?; let wasm = wasmer::wat2wasm(&data)?; From 08c08e1a2b76b79fc1cd9222687b62d032a7e468 Mon Sep 17 00:00:00 2001 From: Joshua Colvin Date: Mon, 15 Apr 2024 11:43:53 -0700 Subject: [PATCH 8/8] Fix function name printing --- arbitrator/prover/src/print.rs | 51 +++++++++++++++------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/arbitrator/prover/src/print.rs b/arbitrator/prover/src/print.rs index 4cc4852ea..75c5b2573 100644 --- a/arbitrator/prover/src/print.rs +++ b/arbitrator/prover/src/print.rs @@ -321,32 +321,24 @@ impl Display for Module { impl WasmBinary<'_> { fn func_name(&self, i: u32) -> String { match self.maybe_func_name(i) { - Some(func) => func, + Some(func) => format!("${func}"), None => format!("$func_{i}"), } .pink() } - fn raw_func_name(&self, i: u32) -> String { - match self.maybe_func_name(i) { - Some(func) => func, - None => i.to_string(), - } - .pink() - } - fn raw_func_index_name(&self, i: u32) -> String { match self.maybe_func_index_name(i) { - Some(func) => func, - None => i.to_string(), + Some(func) => format!("${func}"), + None => format!("$func_{i}"), } .pink() } fn raw_func_number_name(&self, i: u32) -> String { match self.maybe_func_number_name(i) { - Some(func) => func, - None => i.to_string(), + Some(func) => format!("${func}"), + None => format!("$func_{i}"), } .pink() } @@ -429,14 +421,14 @@ impl<'a> Display for WasmBinary<'a> { wln!("({} ({}{ty}))", "type".grey(), "func".grey()); } - for import in &self.imports { + for (pos, import) in self.imports.iter().enumerate() { wln!( r#"({} "{}" "{}" ({} {}{}))"#, "import".grey(), import.module.pink(), import.name.pink(), "func".grey(), - import.offset.to_string(), //self.raw_func_index_name(import.offset), + self.raw_func_index_name(pos as u32), self.func_type(import.offset) ); } @@ -478,22 +470,12 @@ impl<'a> Display for WasmBinary<'a> { ); for _ in 0..item_reader.get_count() { if let Ok(ElementItem::Func(index)) = item_reader.read() { - write!(f, " {}", self.raw_func_name(index))?; + write!(f, " {}", self.func_name(index))?; }; } writeln!(f, ")")?; } - for limits in &self.memories { - let max = limits.maximum.map(|x| format!(" {x}")).unwrap_or_default(); - wln!( - "({} {}{})", - "memory".grey(), - limits.initial.mint(), - max.mint() - ); - } - for data in &self.datas { let (_, mut init) = match data.kind { DataKind::Active { @@ -531,7 +513,7 @@ impl<'a> Display for WasmBinary<'a> { for (export_name, (index, kind)) in &self.exports { let name = (kind == &ExportKind::Func) - .then(|| self.raw_func_name(*index)) + .then(|| self.func_name(*index)) .unwrap_or(index.to_string()); wln!( "({} \"{}\" ({} {}))", @@ -550,7 +532,7 @@ impl<'a> Display for WasmBinary<'a> { wln!( "({} {}{export}{}", "func".grey(), - self.raw_func_number_name(number as u32).pink(), + self.raw_func_number_name(number as u32), self.types[*type_idx as usize].wat_string(true) ); @@ -581,6 +563,17 @@ impl<'a> Display for WasmBinary<'a> { if let Some(start) = self.start { wln!("({} {})", "start".grey(), self.raw_func_index_name(start)); } + + for limits in &self.memories { + let max = limits.maximum.map(|x| format!(" {x}")).unwrap_or_default(); + wln!( + "({} {}{})", + "memory".grey(), + limits.initial.mint(), + max.mint() + ); + } + pad -= 4; wln!(")"); Ok(()) @@ -610,7 +603,7 @@ fn test_wasm_wat() -> eyre::Result<()> { use crate::binary; use std::{fs, path::Path}; - for file in glob::glob("../prover/test-cases/dynamic.wat")? { + for file in glob::glob("../prover/test-cases/*.wat")? { let file = file?; let data = fs::read(&file)?; let wasm = wasmer::wat2wasm(&data)?;