From c6d7aea2bc5577c5f4898887df394a873118bfb9 Mon Sep 17 00:00:00 2001 From: clearloop Date: Mon, 4 Nov 2024 23:56:11 +0800 Subject: [PATCH] feat(zink): example of revert --- codegen/src/visitor/call.rs | 1 + codegen/src/visitor/log.rs | 31 +++++++++++++++++++++++++++++++ examples/revert.rs | 24 ++++++++++++++++++++++++ zink/codegen/src/lib.rs | 3 ++- zink/codegen/src/revert.rs | 13 +++++++------ zink/src/ffi/asm.rs | 8 ++++---- zink/src/lib.rs | 2 +- zint/src/evm.rs | 11 ++++++++++- 8 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 examples/revert.rs diff --git a/codegen/src/visitor/call.rs b/codegen/src/visitor/call.rs index 540ea897c..72e94f9e2 100644 --- a/codegen/src/visitor/call.rs +++ b/codegen/src/visitor/call.rs @@ -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:?}"); diff --git a/codegen/src/visitor/log.rs b/codegen/src/visitor/log.rs index 33ea5bd61..801c95bd1 100644 --- a/codegen/src/visitor/log.rs +++ b/codegen/src/visitor/log.rs @@ -99,4 +99,35 @@ impl Function { Ok(()) } + + /// Revert with message. + pub fn revert(&mut self, count: usize) -> Result<()> { + let mut message = Vec::>::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::>()) + ); + + self.masm.push(&(count * 32).to_ls_bytes())?; + self.masm._push0()?; + + // 3. run log for the data + self.masm._revert()?; + Ok(()) + } } diff --git a/examples/revert.rs b/examples/revert.rs new file mode 100644 index 000000000..d0ae2d7cf --- /dev/null +++ b/examples/revert.rs @@ -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(()) +} diff --git a/zink/codegen/src/lib.rs b/zink/codegen/src/lib.rs index 5ca1527d1..9974113bf 100644 --- a/zink/codegen/src/lib.rs +++ b/zink/codegen/src/lib.rs @@ -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; @@ -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) } diff --git a/zink/codegen/src/revert.rs b/zink/codegen/src/revert.rs index 7cbc82591..e03076cdb 100644 --- a/zink/codegen/src/revert.rs +++ b/zink/codegen/src/revert.rs @@ -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{}", @@ -29,7 +30,7 @@ pub fn parse(input: TokenStream) -> TokenStream { ); quote! { - zink::ffi::asm::#rev(#lit) + unsafe { zink::ffi::asm::#rev(#lit) } } .into() } diff --git a/zink/src/ffi/asm.rs b/zink/src/ffi/asm.rs index c606339d7..fc8930e84 100644 --- a/zink/src/ffi/asm.rs +++ b/zink/src/ffi/asm.rs @@ -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; diff --git a/zink/src/lib.rs b/zink/src/lib.rs index 8fa280035..3aeee8205 100644 --- a/zink/src/lib.rs +++ b/zink/src/lib.rs @@ -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"))] diff --git a/zint/src/evm.rs b/zint/src/evm.rs index 9d82c6f47..d0d034ef1 100644 --- a/zint/src/evm.rs +++ b/zint/src/evm.rs @@ -124,6 +124,8 @@ pub struct Info { pub logs: Vec, /// Transaction halt reason. pub halt: Option, + /// The revert message. + pub revert: Option, } impl TryFrom for Info { @@ -166,7 +168,14 @@ impl TryFrom 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)