diff --git a/codegen/src/codegen.rs b/codegen/src/codegen.rs index 75b2fc4f2..8ae7a27bc 100644 --- a/codegen/src/codegen.rs +++ b/codegen/src/codegen.rs @@ -6,7 +6,7 @@ use crate::{ local::{LocalSlot, LocalSlotType, Locals}, masm::MacroAssembler, validator::ValidateThenVisit, - Buffer, Error, Imports, Result, + Buffer, DataSet, Error, Imports, Result, }; use wasmparser::{FuncType, FuncValidator, LocalsReader, OperatorsReader, ValidatorResources}; @@ -18,6 +18,8 @@ pub struct CodeGen { pub(crate) backtrace: Backtrace, /// Control stack frames. pub(crate) control: ControlStack, + /// Control stack frames. + pub(crate) dataset: DataSet, /// The function environment. pub(crate) env: FuncType, /// The defined locals for a function. @@ -34,7 +36,7 @@ pub struct CodeGen { impl CodeGen { /// Create a new code generator. - pub fn new(env: FuncType, imports: Imports, is_main: bool) -> Result { + pub fn new(env: FuncType, dataset: DataSet, imports: Imports, is_main: bool) -> Result { let mut params_count = 0; if !is_main { params_count = env.params().len() as u8; @@ -43,6 +45,7 @@ impl CodeGen { let mut codegen = Self { backtrace: Backtrace::default(), control: ControlStack::default(), + dataset, env, locals: Default::default(), masm: Default::default(), diff --git a/codegen/src/masm/mod.rs b/codegen/src/masm/mod.rs index 0f349d7e9..c2963b551 100644 --- a/codegen/src/masm/mod.rs +++ b/codegen/src/masm/mod.rs @@ -36,9 +36,18 @@ impl DerefMut for MacroAssembler { } } +/// Info for memory position. +pub struct MemoryInfo { + /// Memory offset. + pub offset: SmallVec<[u8; 8]>, + + /// Memory size + pub size: usize, +} + impl MacroAssembler { /// Store data in memory with at current memory byte pointer. - pub fn memory_write(&mut self, ty: impl Type) -> Result { + pub fn memory_write(&mut self, ty: impl Type) -> Result { let offset = self.mp.to_ls_bytes(); // mock the memory usages. @@ -47,7 +56,16 @@ impl MacroAssembler { // write memory self.memory_write_at(&offset)?; - Ok(size) + Ok(MemoryInfo { offset, size }) + } + + /// Write bytes to memory. + pub fn memory_write_bytes(&mut self, bytes: &[u8]) -> Result { + let len = bytes.len(); + + // TODO: if len is out of 32. + self.push(bytes)?; + self.memory_write(len) } /// Store data in memory at offset. diff --git a/codegen/src/result.rs b/codegen/src/result.rs index 6a3504c92..94bf7bcda 100644 --- a/codegen/src/result.rs +++ b/codegen/src/result.rs @@ -30,9 +30,6 @@ pub enum Error { /// Failed to find imported function by index in jump table. #[error("Imported Function {0} not found in jump table")] ImportedFuncNotFound(u32), - /// Failed to parse data segment. - #[error("Invalid data offset")] - InvalidDataOffset, /// Failed to mark else block for if block. #[error("Invalid else block for if block at {0}")] InvalidElseBlock(u16), @@ -45,6 +42,12 @@ pub enum Error { /// Failed to construct program counter for jump. #[error("Invalid program counter {0}")] InvalidPC(usize), + /// Failed to get data from the provided offset. + #[error("Invalid data offset {0}")] + InvalidDataOffset(i32), + /// Failed to get data from the provided offset. + #[error("Invalid data size {0}")] + InvalidDataSize(usize), /// Failed to get frame info of the given depth. #[error("Invalid contract stack fram depth {0}")] InvalidDepth(usize), diff --git a/codegen/src/visitor/handlers.rs b/codegen/src/visitor/handlers.rs index 84a914295..060f09397 100644 --- a/codegen/src/visitor/handlers.rs +++ b/codegen/src/visitor/handlers.rs @@ -20,7 +20,7 @@ impl CodeGen { return self.handle_empty_return(); } - let size = self.masm.memory_write(results)?; + let size = self.masm.memory_write(results)?.size; let offset = self .masm .mp_offset(|mp| mp.checked_sub(size).ok_or_else(|| Error::InvalidMP(0)))?; diff --git a/codegen/src/visitor/log.rs b/codegen/src/visitor/log.rs index 2b8ad44a1..09047458f 100644 --- a/codegen/src/visitor/log.rs +++ b/codegen/src/visitor/log.rs @@ -1,6 +1,6 @@ //! System instructions -use crate::{CodeGen, Error, Result}; +use crate::{masm::MemoryInfo, CodeGen, Error, Result, ToLSBytes}; impl CodeGen { /// Parse log data from the bytecode. @@ -18,7 +18,7 @@ impl CodeGen { // .. // PUSH32 0x8f if !(0x5e..0x8f).contains(&data[0]) { - return Err(Error::InvalidDataOffset); + return Err(Error::InvalidDataOffset(data[0].into())); } let offset_len = (data[0] - 0x5f) as usize; @@ -31,7 +31,7 @@ impl CodeGen { // Parse size. if !(0x5e..0x8f).contains(&data[offset_len + 1]) { - return Err(Error::InvalidDataOffset); + return Err(Error::InvalidDataOffset(data[offset_len + 1].into())); } let size = { let mut bytes = [0; 4]; @@ -41,14 +41,32 @@ impl CodeGen { }; tracing::debug!("log0 size: {:?}", size); - // Integrate with data section. - Ok((offset, size)) } - /// Logs a message with no topics. + /// Logs a message without topics. pub fn log0(&mut self) -> Result<()> { - let (_offset, _size) = self.log_data()?; + let (offset, size) = self.log_data()?; + let size = size as usize; + let data = self + .dataset + .get(&offset) + .ok_or(Error::InvalidDataOffset(offset))?; + + tracing::debug!("log0 data: {:?}", data); + if data.len() != size { + return Err(Error::InvalidDataSize(size)); + } + + // 1. write data to memory + let MemoryInfo { offset, size } = self.masm.memory_write_bytes(data)?; + + // 2. prepare the offset and size of the data. + self.masm.push(&size.to_ls_bytes())?; + self.masm.push(&offset)?; + + // 3. run log0 for the data + self.masm._log0()?; Ok(()) } diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index 4c0f784ab..818ad2c2e 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -2,7 +2,7 @@ use crate::{parser::Parser, Error, Result}; use wasmparser::{FuncToValidate, FunctionBody, ValidatorResources, WasmModuleResources}; -use zingen::{Buffer, CodeGen, Imports, JumpTable, BUFFER_LIMIT}; +use zingen::{Buffer, CodeGen, DataSet, Imports, JumpTable, BUFFER_LIMIT}; /// Zink Compiler #[derive(Default)] @@ -16,12 +16,12 @@ impl Compiler { pub fn compile(mut self, wasm: &[u8]) -> Result { let Parser { imports, - data: _data, + data, funcs, } = Parser::try_from(wasm)?; for (index, (func, body)) in funcs.into_iter() { - self.compile_func(index, imports.clone(), func, body)?; + self.compile_func(index, data.clone(), imports.clone(), func, body)?; } self.finish() @@ -31,6 +31,7 @@ impl Compiler { pub fn compile_func( &mut self, func_index: u32, + dataset: DataSet, imports: Imports, validator: FuncToValidate, body: FunctionBody, @@ -49,7 +50,7 @@ impl Compiler { tracing::debug!("compile function {}: {:?}", func_index, sig); let is_main = func_index == 0; - let mut codegen = CodeGen::new(sig, imports, is_main)?; + let mut codegen = CodeGen::new(sig, dataset, imports, is_main)?; let mut locals_reader = body.get_locals_reader()?; let mut ops_reader = body.get_operators_reader()?; diff --git a/compiler/tests/log.rs b/compiler/tests/log.rs index 6147c9a41..73578fbed 100644 --- a/compiler/tests/log.rs +++ b/compiler/tests/log.rs @@ -1,17 +1,16 @@ //! Tests for instruction `select`. use anyhow::Result; -use zint::EVM; +use zint::{Bytes32, EVM}; mod common; -#[ignore] #[test] fn log0() -> Result<()> { let bytecode = common::load("log", "log0")?; // returns the bigger number. - let _info = EVM::run(&bytecode, &[]); - + let info = EVM::run(&bytecode, &[]); + assert_eq!(info.logs[0].data.to_vec(), b"Ping".to_vec().to_bytes32()); Ok(()) } diff --git a/zint/src/bytes.rs b/zint/src/bytes.rs index 42fb10543..5e530fe42 100644 --- a/zint/src/bytes.rs +++ b/zint/src/bytes.rs @@ -32,4 +32,20 @@ macro_rules! impl_bytes32 { }; } +impl Bytes32 for Vec { + fn to_bytes32(&self) -> [u8; 32] { + let mut bytes = [0u8; 32]; + bytes[(32 - self.len())..].copy_from_slice(self); + bytes + } +} + +impl Bytes32 for &[u8] { + fn to_bytes32(&self) -> [u8; 32] { + let mut bytes = [0u8; 32]; + bytes[(32 - self.len())..].copy_from_slice(self); + bytes + } +} + impl_bytes32!(i8, u8, i16, u16, i32, u32, usize, i64, u64, i128, u128); diff --git a/zint/src/evm.rs b/zint/src/evm.rs index 38f1cac6d..650635afc 100644 --- a/zint/src/evm.rs +++ b/zint/src/evm.rs @@ -2,7 +2,7 @@ pub use revm::interpreter::{instruction_result::InstructionResult, primitives::U256}; use revm::interpreter::{ - primitives::{bytecode::Bytecode, specification::ShanghaiSpec}, + primitives::{bytecode::Bytecode, specification::ShanghaiSpec, Log}, Contract, DummyHost, Interpreter, }; use std::collections::HashMap; @@ -20,6 +20,8 @@ pub struct Info { pub ret: Vec, /// The storage. pub storage: HashMap, + /// Execution logs. + pub logs: Vec, } /// EVM interpreter. @@ -67,6 +69,7 @@ impl EVM { instr, ret, storage, + logs: self.host.log.clone(), } }