Skip to content

Commit

Permalink
feat(zink): example of revert
Browse files Browse the repository at this point in the history
  • Loading branch information
clearloop committed Nov 4, 2024
1 parent b635d97 commit c6d7aea
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 13 deletions.
1 change: 1 addition & 0 deletions codegen/src/visitor/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ impl Function {
HostFunc::Evm(OpCode::LOG3) => self.log(3),
HostFunc::Evm(OpCode::LOG4) => self.log(4),
HostFunc::Evm(op) => self.masm.emit_op(op),
HostFunc::Revert(count) => self.revert(count),
HostFunc::NoOp | HostFunc::Label(_) => Ok(()),
_ => {
tracing::error!("unsupported host function {func:?}");
Expand Down
31 changes: 31 additions & 0 deletions codegen/src/visitor/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,35 @@ impl Function {

Ok(())
}

/// Revert with message.
pub fn revert(&mut self, count: usize) -> Result<()> {
let mut message = Vec::<Vec<u8>>::default();
for slot in 0..count {
let (offset, size) = self.data()?;
let size = size as usize;
let data = self.env.data.load(offset, size)?;

self.masm.push(&data)?;
if slot == 0 {
self.masm._push0()?;
} else {
self.masm.push(&slot.to_ls_bytes())?;
}
self.masm._mstore()?;
message.push(data);
}

tracing::debug!(
"revert message: {}",
String::from_utf8_lossy(&message.into_iter().flatten().collect::<Vec<u8>>())
);

self.masm.push(&(count * 32).to_ls_bytes())?;
self.masm._push0()?;

// 3. run log for the data
self.masm._revert()?;
Ok(())
}
}
24 changes: 24 additions & 0 deletions examples/revert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Example of revert
#![cfg_attr(target_arch = "wasm32", no_std)]
#![cfg_attr(target_arch = "wasm32", no_main)]

extern crate zink;

#[cfg(not(target_arch = "wasm32"))]
fn main() {}

/// check if the passing address is owner
#[zink::external]
pub fn run_revert() {
zink::revert!("revert works")
}

#[test]
fn test_revert() -> anyhow::Result<()> {
use zint::Contract;
let mut contract = Contract::search("revert")?.compile()?;

let info = contract.execute(["revert()".as_bytes()])?;
assert_eq!(info.revert, Some("revert works".into()));
Ok(())
}
3 changes: 2 additions & 1 deletion zink/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
extern crate proc_macro;

use proc_macro::TokenStream;
use syn::{parse_macro_input, Attribute, DeriveInput, ItemFn, ItemStruct};
use syn::{parse_macro_input, Attribute, DeriveInput, ItemFn, ItemStruct, LitStr};

mod event;
mod revert;
Expand All @@ -16,6 +16,7 @@ mod storage;
/// Only raw string is supported, formatter currently doesn't work.
#[proc_macro]
pub fn revert(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStr);
revert::parse(input)
}

Expand Down
13 changes: 7 additions & 6 deletions zink/codegen/src/revert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@
use proc_macro::TokenStream;
use proc_macro2::{Literal, Span};
use quote::quote;
use syn::Ident;
use quote::{quote, ToTokens};
use syn::{Ident, LitStr};

/// Revert with message
pub fn parse(input: TokenStream) -> TokenStream {
let message = input.to_string();
pub fn parse(input: LitStr) -> TokenStream {
let message = input.value();
let len = message.len() as i32;
if len > 128 {
panic!("Only support revert message less than 128 bytes atm.");
}

let lit = Literal::string(&message);
// TODO: handle the string correctly
let lit = Literal::string(&message.replace("\"", ""));
let rev = Ident::new(
&format!(
"revert{}",
Expand All @@ -29,7 +30,7 @@ pub fn parse(input: TokenStream) -> TokenStream {
);

quote! {
zink::ffi::asm::#rev(#lit)
unsafe { zink::ffi::asm::#rev(#lit) }
}
.into()
}
8 changes: 4 additions & 4 deletions zink/src/ffi/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ extern "C" {
pub fn push_address(address: Address);

/// Revert with message in 32 bytes
pub fn revert1(message: &'static [u8]);
pub fn revert1(message: &'static str);

/// Revert with message in 64 bytes
pub fn revert2(message: &'static [u8]);
pub fn revert2(message: &'static str);

/// Revert with message in 96 bytes
pub fn revert3(message: &'static [u8]);
pub fn revert3(message: &'static str);

/// Revert with message in 128 bytes
pub fn revert4(message: &'static [u8]);
pub fn revert4(message: &'static str);

/// Load a 8-bit signed integer from the storage.
pub fn sload_i8() -> i8;
Expand Down
2 changes: 1 addition & 1 deletion zink/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub mod storage;

pub use self::{asm::Asm, event::Event};
pub use storage::{DoubleKeyMapping, Mapping, Storage};
pub use zink_codegen::{external, storage, Event};
pub use zink_codegen::{external, revert, storage, Event};

/// Generate a keccak hash of the input (sha3)
#[cfg(not(target_family = "wasm"))]
Expand Down
11 changes: 10 additions & 1 deletion zint/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ pub struct Info {
pub logs: Vec<Log>,
/// Transaction halt reason.
pub halt: Option<HaltReason>,
/// The revert message.
pub revert: Option<String>,
}

impl TryFrom<ExecutionResult> for Info {
Expand Down Expand Up @@ -166,7 +168,14 @@ impl TryFrom<ExecutionResult> for Info {
ExecutionResult::Halt { reason, .. } => {
info.halt = Some(reason);
}
_ => unreachable!("This should never happen"),
ExecutionResult::Revert { gas_used, output } => {
info.gas = gas_used;
info.revert = Some(
String::from_utf8_lossy(&output)
.trim_start_matches("\0")
.to_string(),
);
}
}

Ok(info)
Expand Down

0 comments on commit c6d7aea

Please sign in to comment.