Skip to content

Commit

Permalink
feat(codegen): implement all log APIs (#125)
Browse files Browse the repository at this point in the history
* refactor(zink): remove size in log API

* refactor(codegen): wrap dataset with extra methods

* feat(codegen): support log1

* refactor(codegen): introduce new log parser

* feat(compiler): tests for log3 and log4
  • Loading branch information
clearloop authored Oct 2, 2023
1 parent b93ea3e commit d0a7535
Show file tree
Hide file tree
Showing 19 changed files with 363 additions and 103 deletions.
4 changes: 2 additions & 2 deletions codegen/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ impl Assembler {
pub fn increment_sp(&mut self, items: u8) -> Result<()> {
self.sp += items;

// TODO: fix this limitation: should be 1024.
if self.sp > 12 {
// TODO: fix this limitation: should be 1024. (#127)
if self.sp > 254 {
return Err(Error::StackOverflow(self.sp));
}

Expand Down
43 changes: 43 additions & 0 deletions codegen/src/data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//! Dataset in code generation
use crate::{Error, Result};
use std::{
collections::BTreeMap,
ops::{Deref, DerefMut},
};

/// Data section conversion
///
/// NOTE: current only support constant expression.
#[derive(Default, Clone, Debug)]
pub struct DataSet(BTreeMap<i32, Vec<u8>>);

impl DataSet {
/// Load data from offset and size
pub fn load(&self, offset: i32, size: usize) -> Result<Vec<u8>> {
for ptr in self.0.keys().cloned().rev() {
if offset >= ptr {
let start = (offset - ptr) as usize;
let data = self.get(&ptr).ok_or(Error::DataNotFound(offset, size))?;

return Ok(data[start..start + size].to_vec());
}
}

Err(Error::DataNotFound(offset, size))
}
}

impl Deref for DataSet {
type Target = BTreeMap<i32, Vec<u8>>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for DataSet {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
35 changes: 28 additions & 7 deletions codegen/src/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ pub enum Func {
Sstore,
/// Run function log0.
Log0,
/// Run function log1.
Log1,
/// Run function log2.
Log2,
/// Run function log3.
Log3,
/// Run function log4.
Log4,
}

impl Func {
Expand All @@ -55,6 +63,10 @@ impl Func {
Self::Sload => 1,
Self::Sstore => 2,
Self::Log0 => 2,
Self::Log1 => 4,
Self::Log2 => 6,
Self::Log3 => 8,
Self::Log4 => 10,
}
}

Expand All @@ -65,6 +77,10 @@ impl Func {
Self::Sload => 1,
Self::Sstore => 0,
Self::Log0 => 0,
Self::Log1 => 0,
Self::Log2 => 0,
Self::Log3 => 0,
Self::Log4 => 0,
}
}

Expand Down Expand Up @@ -101,12 +117,7 @@ impl Func {
/// are necessary to just stay in the code
/// section #109
pub fn is_embedded(&self) -> bool {
match self {
Self::Select => true,
Self::Sload => true,
Self::Sstore => true,
Self::Log0 => true,
}
true
}
}

Expand All @@ -115,11 +126,21 @@ impl TryFrom<(&str, &str)> for Func {

fn try_from(import: (&str, &str)) -> Result<Self> {
let (module, name) = import;
// NOTE: `select` is not external call
// so we don't need to check process it
// here
match import {
("evm", "sload") => Ok(Self::Sload),
("evm", "sstore") => Ok(Self::Sstore),
("evm", "log0") => Ok(Self::Log0),
_ => Err(Error::HostFuncNotFound(module.into(), name.into())),
("evm", "log1") => Ok(Self::Log1),
("evm", "log2") => Ok(Self::Log2),
("evm", "log3") => Ok(Self::Log3),
("evm", "log4") => Ok(Self::Log4),
_ => {
tracing::error!("Failed to load host function: {:?}", import);
Err(Error::HostFuncNotFound(module.into(), name.into()))
}
}
}
}
10 changes: 2 additions & 8 deletions codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,26 @@
#![deny(missing_docs)]
#![recursion_limit = "1024"]

use std::collections::BTreeMap;

pub use crate::{
abi::{ToLSBytes, Type},
asm::Assembler,
codegen::CodeGen,
control::{ControlStack, ControlStackFrame, ControlStackFrameType},
data::DataSet,
func::Func,
jump::{Code, JumpTable},
local::{LocalSlot, Locals},
masm::MacroAssembler,
result::{Error, Result},
};
// use indexmap::IndexMap;
use smallvec::SmallVec;

pub mod abi;
mod asm;
mod backtrace;
mod codegen;
mod control;
mod data;
mod func;
mod jump;
mod local;
Expand All @@ -40,8 +39,3 @@ pub type Buffer = SmallVec<[u8; BUFFER_LIMIT]>;
/// Imported functions.
/// pub type Imports = IndexMap<u32, Func>;
pub type Imports = Vec<Func>;

/// Data section conversion
///
/// NOTE: current only support constant expression.
pub type DataSet = BTreeMap<i32, Vec<u8>>;
5 changes: 4 additions & 1 deletion codegen/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub enum Error {
/// Failed to pop control stack frame.
#[error("Control stack underflow")]
ControlStackUnderflow,
/// Data not found in data section.
#[error("Data not found in data setction, offset {0}, size {1}")]
DataNotFound(i32, usize),
/// Failed to register program counter to function index.
#[error("Function {0} already exists in jump table")]
DuplicateFunc(u32),
Expand Down Expand Up @@ -67,7 +70,7 @@ pub enum Error {
#[error("Stack index is out of range {0}, max is 32 (0x400)")]
StackIndexOutOfRange(u8),
/// Failed to increment stack pointer.
#[error("Stack overflow, max is 12 stack items, got {0}")]
#[error("Stack overflow, max is 1024 stack items, got {0}")]
StackOverflow(u8),
/// Failed to decrement stack pointer.
#[error("Stack underflow, current stack items {0}, expect at least {1}")]
Expand Down
11 changes: 9 additions & 2 deletions codegen/src/visitor/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,15 @@ impl CodeGen {
match func {
Func::Sstore => self.masm._sstore(),
Func::Sload => self.masm._sload(),
Func::Log0 => self.log0(),
_ => Err(Error::UnsupportedHostFunc(func)),
Func::Log0 => self.log(0),
Func::Log1 => self.log(1),
Func::Log2 => self.log(2),
Func::Log3 => self.log(3),
Func::Log4 => self.log(4),
_ => {
tracing::error!("unsupported embedded function {func:?}");
Err(Error::UnsupportedHostFunc(func))
}
}
}

Expand Down
54 changes: 36 additions & 18 deletions codegen/src/visitor/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl CodeGen {
bytes[..offset_len].copy_from_slice(&data[1..(1 + offset_len)]);
i32::from_le_bytes(bytes)
};
tracing::debug!("log0 offset: {:?}", offset);
tracing::debug!("log offset: {:?}", offset);

// Parse size.
if !(0x5e..0x8f).contains(&data[offset_len + 1]) {
Expand All @@ -39,34 +39,52 @@ impl CodeGen {
bytes[..size_bytes.len()].copy_from_slice(size_bytes);
i32::from_le_bytes(bytes)
};
tracing::debug!("log0 size: {:?}", size);

tracing::debug!("log size: {:?}", size);
Ok((offset, size))
}

/// Logs a message without topics.
pub fn log0(&mut self) -> Result<()> {
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));
/// Log a message with topics.
pub fn log(&mut self, count: usize) -> Result<()> {
let mut topics = Vec::<Vec<u8>>::default();
for topic in (1..=count).rev() {
let (offset, size) = self.log_data()?;
let size = size as usize;
let data = self.dataset.load(offset, size)?;

tracing::debug!("log{count} topic{topic}: {:?}", data);
topics.push(data);
}

let name = {
let (offset, size) = self.log_data()?;
let size = size as usize;
let data = self.dataset.load(offset, size)?;

tracing::debug!("log1 name: {:?}", data);
data
};

for topic in topics {
self.masm.push(&topic)?;
}

// 1. write data to memory
let MemoryInfo { offset, size } = self.masm.memory_write_bytes(data)?;
let MemoryInfo { offset, size } = self.masm.memory_write_bytes(&name)?;

// 2. prepare the offset and size of the data.
// 3. 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()?;
// 4. run log for the data
match count {
0 => self.masm._log0(),
1 => self.masm._log1(),
2 => self.masm._log2(),
3 => self.masm._log3(),
4 => self.masm._log4(),
_ => unreachable!("invalid topics"),
}?;

Ok(())
}
Expand Down
80 changes: 80 additions & 0 deletions compiler/tests/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,83 @@ fn log0() -> Result<()> {
assert_eq!(info.logs[0].data.to_vec(), b"Ping".to_vec().to_bytes32());
Ok(())
}

#[test]
fn log1() -> Result<()> {
let bytecode = common::load("log", "log1")?;

// returns the bigger number.
let info = EVM::run(&bytecode, &[]);
assert_eq!(info.logs[0].data.to_vec(), b"Ping".to_vec().to_bytes32());
assert_eq!(
info.logs[0].topics[0].to_vec(),
b"pong".to_vec().to_bytes32()
);
Ok(())
}

#[test]
fn log2() -> Result<()> {
let bytecode = common::load("log", "log2")?;

// returns the bigger number.
let info = EVM::run(&bytecode, &[]);
assert_eq!(info.logs[0].data.to_vec(), b"Ping".to_vec().to_bytes32());
assert_eq!(
info.logs[0].topics[0].to_vec(),
b"pong".to_vec().to_bytes32()
);
assert_eq!(
info.logs[0].topics[1].to_vec(),
b"ping".to_vec().to_bytes32()
);
Ok(())
}

#[test]
fn log3() -> Result<()> {
let bytecode = common::load("log", "log3")?;

// returns the bigger number.
let info = EVM::run(&bytecode, &[]);
assert_eq!(info.logs[0].data.to_vec(), b"Ping".to_vec().to_bytes32());
assert_eq!(
info.logs[0].topics[0].to_vec(),
b"pong".to_vec().to_bytes32()
);
assert_eq!(
info.logs[0].topics[1].to_vec(),
b"ping".to_vec().to_bytes32()
);
assert_eq!(
info.logs[0].topics[2].to_vec(),
b"pong".to_vec().to_bytes32()
);
Ok(())
}

#[test]
fn log4() -> Result<()> {
let bytecode = common::load("log", "log4")?;

// returns the bigger number.
let info = EVM::run(&bytecode, &[]);
assert_eq!(info.logs[0].data.to_vec(), b"Ping".to_vec().to_bytes32());
assert_eq!(
info.logs[0].topics[0].to_vec(),
b"pong".to_vec().to_bytes32()
);
assert_eq!(
info.logs[0].topics[1].to_vec(),
b"ping".to_vec().to_bytes32()
);
assert_eq!(
info.logs[0].topics[2].to_vec(),
b"pong".to_vec().to_bytes32()
);
assert_eq!(
info.logs[0].topics[3].to_vec(),
b"pong".to_vec().to_bytes32()
);
Ok(())
}
12 changes: 6 additions & 6 deletions compiler/wat/log/log0.wat
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
(module
(type (;0;) (func (param i32 i32)))
(type (;1;) (func))
(import "evm" "log0" (func (;0;) (type 0)))
(type (;0;) (func))
(type (;1;) (func (param i32 i32)))
(import "evm" "log0" (func (;0;) (type 1)))
(import "env" "memory" (memory (;0;) 17))
(func (;1;) (type 1)
(func (;2;) (type 0)
i32.const 1048576
i32.const 4
call 0)
(global (;0;) i32 (i32.const 1048580))
(global (;0;) i32 (i32.const 1048584))
(global (;1;) i32 (i32.const 1048592))
(export "log" (func 1))
(export "log0" (func 1))
(export "__data_end" (global 0))
(export "__heap_base" (global 1))
(data (;0;) (i32.const 1048576) "Ping"))
Loading

0 comments on commit d0a7535

Please sign in to comment.