Skip to content

Commit

Permalink
feat(compiler): support log0 (#120)
Browse files Browse the repository at this point in the history
* feat(zint): introduce logs in zint result

* feat(codegen): introduce log0

* feat(compiler): provide tests for log0

* chore(zint): make clippy happy
  • Loading branch information
clearloop authored Oct 1, 2023
1 parent b94d0aa commit b93ea3e
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 24 deletions.
7 changes: 5 additions & 2 deletions codegen/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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.
Expand All @@ -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<Self> {
pub fn new(env: FuncType, dataset: DataSet, imports: Imports, is_main: bool) -> Result<Self> {
let mut params_count = 0;
if !is_main {
params_count = env.params().len() as u8;
Expand All @@ -43,6 +45,7 @@ impl CodeGen {
let mut codegen = Self {
backtrace: Backtrace::default(),
control: ControlStack::default(),
dataset,
env,
locals: Default::default(),
masm: Default::default(),
Expand Down
22 changes: 20 additions & 2 deletions codegen/src/masm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize> {
pub fn memory_write(&mut self, ty: impl Type) -> Result<MemoryInfo> {
let offset = self.mp.to_ls_bytes();

// mock the memory usages.
Expand All @@ -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<MemoryInfo> {
let len = bytes.len();

// TODO: if len is out of 32.
self.push(bytes)?;
self.memory_write(len)
}

/// Store data in memory at offset.
Expand Down
9 changes: 6 additions & 3 deletions codegen/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion codegen/src/visitor/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)))?;
Expand Down
32 changes: 25 additions & 7 deletions codegen/src/visitor/log.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand All @@ -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];
Expand All @@ -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(())
}
Expand Down
9 changes: 5 additions & 4 deletions compiler/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -16,12 +16,12 @@ impl Compiler {
pub fn compile(mut self, wasm: &[u8]) -> Result<Buffer> {
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()
Expand All @@ -31,6 +31,7 @@ impl Compiler {
pub fn compile_func(
&mut self,
func_index: u32,
dataset: DataSet,
imports: Imports,
validator: FuncToValidate<ValidatorResources>,
body: FunctionBody,
Expand All @@ -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()?;

Expand Down
7 changes: 3 additions & 4 deletions compiler/tests/log.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
16 changes: 16 additions & 0 deletions zint/src/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,20 @@ macro_rules! impl_bytes32 {
};
}

impl Bytes32 for Vec<u8> {
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);
5 changes: 4 additions & 1 deletion zint/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -20,6 +20,8 @@ pub struct Info {
pub ret: Vec<u8>,
/// The storage.
pub storage: HashMap<U256, U256>,
/// Execution logs.
pub logs: Vec<Log>,
}

/// EVM interpreter.
Expand Down Expand Up @@ -67,6 +69,7 @@ impl EVM {
instr,
ret,
storage,
logs: self.host.log.clone(),
}
}

Expand Down

0 comments on commit b93ea3e

Please sign in to comment.