mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-27 22:07:59 +00:00
@@ -10,7 +10,10 @@ use primitive_types::{H160, H256, U256};
|
||||
static RUNTIME_ETABLE: Etable<RuntimeState, UnimplementedHandler, CallCreateTrap> =
|
||||
Etable::runtime();
|
||||
|
||||
pub struct UnimplementedHandler;
|
||||
#[derive(Default)]
|
||||
pub struct UnimplementedHandler {
|
||||
logs: Vec<Log>,
|
||||
}
|
||||
|
||||
impl RuntimeEnvironment for UnimplementedHandler {
|
||||
fn block_hash(&self, _number: U256) -> H256 {
|
||||
@@ -87,8 +90,9 @@ impl RuntimeBackend for UnimplementedHandler {
|
||||
fn set_storage(&mut self, _address: H160, _index: H256, _value: H256) -> Result<(), ExitError> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn log(&mut self, _log: Log) -> Result<(), ExitError> {
|
||||
unimplemented!()
|
||||
fn log(&mut self, log: Log) -> Result<(), ExitError> {
|
||||
self.logs.push(log);
|
||||
Ok(())
|
||||
}
|
||||
fn mark_delete(&mut self, _address: H160) {
|
||||
unimplemented!()
|
||||
@@ -147,12 +151,9 @@ pub fn prepare(code: Vec<u8>, data: Vec<u8>) -> PreparedEvm {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(pre: PreparedEvm) -> Vec<u8> {
|
||||
pub fn execute(pre: PreparedEvm) -> (Vec<u8>, Vec<Log>) {
|
||||
let mut vm = EtableInterpreter::new_valid(pre.vm, &RUNTIME_ETABLE, pre.valids);
|
||||
vm.run(&mut UnimplementedHandler {})
|
||||
.exit()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
vm.retval.clone()
|
||||
let mut handler = UnimplementedHandler::default();
|
||||
vm.run(&mut handler).exit().unwrap().unwrap();
|
||||
(vm.retval.clone(), handler.logs)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"Baseline": 3944,
|
||||
"Computation": 7444,
|
||||
"DivisionArithmetics": 42784,
|
||||
"ERC20": 53524,
|
||||
"ERC20": 56199,
|
||||
"Events": 4784,
|
||||
"FibonacciIterative": 6019,
|
||||
"Flipper": 4392,
|
||||
"SHA1": 36107
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8;
|
||||
|
||||
contract DivisionArithmetics {
|
||||
function div(uint n, uint d) public pure returns (uint q) {
|
||||
assembly {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8;
|
||||
|
||||
contract Events {
|
||||
event A() anonymous;
|
||||
event E(uint, uint indexed, uint indexed, uint indexed);
|
||||
|
||||
function emitEvent(uint topics) public {
|
||||
if (topics == 0) {
|
||||
emit A();
|
||||
} else {
|
||||
emit E(topics, 1, 2, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,15 @@ sol!(
|
||||
}
|
||||
);
|
||||
|
||||
sol!(
|
||||
contract Events {
|
||||
event A(uint) anonymous;
|
||||
event E(uint indexed, uint indexed, uint indexed);
|
||||
|
||||
function emitEvent(uint topics) public;
|
||||
}
|
||||
);
|
||||
|
||||
impl Contract {
|
||||
/// Execute the contract.
|
||||
///
|
||||
@@ -338,6 +347,18 @@ impl Contract {
|
||||
calldata: MStore8::mStore8Call::new((value,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(topics: U256) -> Self {
|
||||
let code = include_str!("../contracts/Events.sol");
|
||||
let name = "Events";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Events::emitEventCall::new((topics,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -389,6 +410,7 @@ mod tests {
|
||||
Contract::erc20 as fn() -> Contract,
|
||||
(|| Contract::sha1(Vec::new())) as fn() -> Contract,
|
||||
(|| Contract::division_arithmetics_div(U256::ZERO, U256::ZERO)) as fn() -> Contract,
|
||||
(|| Contract::event(U256::ZERO)) as fn() -> Contract,
|
||||
]
|
||||
.into_par_iter()
|
||||
.map(extract_code_size)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use alloy_primitives::{Address, U256};
|
||||
use cases::Contract;
|
||||
use mock_runtime::{CallOutput, State};
|
||||
|
||||
use crate::mock_runtime::ReturnFlags;
|
||||
use crate::mock_runtime::{Event, ReturnFlags};
|
||||
|
||||
pub mod cases;
|
||||
pub mod mock_runtime;
|
||||
@@ -82,7 +83,25 @@ pub fn assert_success(contract: &Contract, differential: bool) -> (State, CallOu
|
||||
if differential {
|
||||
let evm =
|
||||
revive_differential::prepare(contract.evm_runtime.clone(), contract.calldata.clone());
|
||||
assert_eq!(output.data.clone(), revive_differential::execute(evm));
|
||||
let (evm_output, evm_log) = revive_differential::execute(evm);
|
||||
|
||||
assert_eq!(output.data.clone(), evm_output);
|
||||
assert_eq!(output.events.len(), evm_log.len());
|
||||
assert_eq!(
|
||||
output.events,
|
||||
evm_log
|
||||
.iter()
|
||||
.map(|log| Event {
|
||||
address: Address::from_slice(log.address.as_bytes()),
|
||||
data: log.data.clone(),
|
||||
topics: log
|
||||
.topics
|
||||
.iter()
|
||||
.map(|topic| U256::from_be_bytes(topic.0))
|
||||
.collect(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
(state, output)
|
||||
|
||||
@@ -17,8 +17,9 @@ struct Account {
|
||||
}
|
||||
|
||||
/// Emitted event data.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
pub struct Event {
|
||||
pub address: Address,
|
||||
pub data: Vec<u8>,
|
||||
pub topics: Vec<U256>,
|
||||
}
|
||||
@@ -31,7 +32,7 @@ pub struct CallOutput {
|
||||
/// The contract call output.
|
||||
pub data: Vec<u8>,
|
||||
/// The emitted events.
|
||||
pub events: Event,
|
||||
pub events: Vec<Event>,
|
||||
}
|
||||
|
||||
/// The contract blob export to be called.
|
||||
@@ -570,6 +571,43 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
linker
|
||||
.func_wrap(
|
||||
runtime_api::DEPOSIT_EVENT,
|
||||
|caller: Caller<Transaction>,
|
||||
topics_ptr: u32,
|
||||
topics_len: u32,
|
||||
data_ptr: u32,
|
||||
data_len: u32| {
|
||||
let (caller, transaction) = caller.split();
|
||||
|
||||
let address = transaction.top_frame().callee;
|
||||
let data = if data_len != 0 {
|
||||
caller.read_memory_into_vec(data_ptr, data_len)?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let topics = if topics_len != 0 {
|
||||
caller
|
||||
.read_memory_into_vec(topics_ptr, topics_len)?
|
||||
.chunks(32)
|
||||
.map(|chunk| U256::from_be_slice(chunk))
|
||||
.collect()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
transaction.top_frame_mut().output.events.push(Event {
|
||||
address,
|
||||
data,
|
||||
topics,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
linker
|
||||
}
|
||||
|
||||
|
||||
@@ -415,3 +415,9 @@ fn signed_remainder() {
|
||||
assert_eq!(received, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn events() {
|
||||
assert_success(&Contract::event(U256::ZERO), true);
|
||||
assert_success(&Contract::event(U256::from(123)), true);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ pub static BLOCK_NUMBER: &str = "block_number";
|
||||
|
||||
pub static CALLER: &str = "caller";
|
||||
|
||||
pub static DEPOSIT_EVENT: &str = "deposit_event";
|
||||
|
||||
pub static GET_STORAGE: &str = "get_storage";
|
||||
|
||||
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
|
||||
@@ -32,10 +34,15 @@ pub static VALUE_TRANSFERRED: &str = "value_transferred";
|
||||
|
||||
/// All imported runtime API symbols..
|
||||
/// Useful for configuring common attributes and linkage.
|
||||
pub static IMPORTS: [&str; 6] = [
|
||||
pub static IMPORTS: [&str; 11] = [
|
||||
ADDRESS,
|
||||
BLOCK_NUMBER,
|
||||
CALLER,
|
||||
DEPOSIT_EVENT,
|
||||
GET_STORAGE,
|
||||
HASH_KECCAK_256,
|
||||
INPUT,
|
||||
NOW,
|
||||
RETURN,
|
||||
SET_STORAGE,
|
||||
VALUE_TRANSFERRED,
|
||||
|
||||
@@ -1,72 +1,80 @@
|
||||
//! Translates a log or event call.
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
use crate::polkavm_const::runtime_api;
|
||||
|
||||
/// Translates a log or event call.
|
||||
/// The decoding logic is implemented in a system contract, which is called from here.
|
||||
/// There are several cases of the translation for the sake of efficiency, since the front-end
|
||||
/// emits topics and values sequentially by one, but the LLVM intrinsic and bytecode instruction
|
||||
/// accept two at once.
|
||||
///
|
||||
/// TODO: Splitting up into dedicated functions (log0..log4)
|
||||
/// could potentially decrease code sizes (LLVM can still decide to inline).
|
||||
/// However, passing many i256 parameters could also hurt a bit.
|
||||
/// Should be reviewed after 64bit support.
|
||||
pub fn log<'ctx, D>(
|
||||
_context: &mut Context<'ctx, D>,
|
||||
_input_offset: inkwell::values::IntValue<'ctx>,
|
||||
_input_length: inkwell::values::IntValue<'ctx>,
|
||||
_topics: Vec<inkwell::values::IntValue<'ctx>>,
|
||||
context: &mut Context<'ctx, D>,
|
||||
input_offset: inkwell::values::IntValue<'ctx>,
|
||||
input_length: inkwell::values::IntValue<'ctx>,
|
||||
topics: Vec<inkwell::values::IntValue<'ctx>>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
/*
|
||||
let failure_block = context.append_basic_block("event_failure_block");
|
||||
let join_block = context.append_basic_block("event_join_block");
|
||||
|
||||
let gas = crate::polkavm::evm::ether_gas::gas(context)?.into_int_value();
|
||||
let abi_data = crate::polkavm::utils::abi_data(
|
||||
context,
|
||||
input_offset,
|
||||
input_length,
|
||||
Some(gas),
|
||||
AddressSpace::Heap,
|
||||
true,
|
||||
)?;
|
||||
let mut extra_abi_data = Vec::with_capacity(1 + topics.len());
|
||||
extra_abi_data.push(context.field_const(topics.len() as u64));
|
||||
extra_abi_data.extend(topics);
|
||||
|
||||
let result = context
|
||||
.build_call(
|
||||
context.llvm_runtime().far_call,
|
||||
crate::polkavm::utils::external_call_arguments(
|
||||
context,
|
||||
abi_data.as_basic_value_enum(),
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_EVENT_WRITER as u64),
|
||||
extra_abi_data,
|
||||
None,
|
||||
)
|
||||
.as_slice(),
|
||||
"event_writer_call_external",
|
||||
)
|
||||
.expect("Always returns a value");
|
||||
|
||||
let result_status_code_boolean = context
|
||||
.builder()
|
||||
.build_extract_value(
|
||||
result.into_struct_value(),
|
||||
1,
|
||||
"event_writer_external_result_status_code_boolean",
|
||||
)
|
||||
.expect("Always exists");
|
||||
context.build_conditional_branch(
|
||||
result_status_code_boolean.into_int_value(),
|
||||
join_block,
|
||||
failure_block,
|
||||
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
|
||||
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
|
||||
let input_pointer = context.builder().build_ptr_to_int(
|
||||
context.build_heap_gep(input_offset, input_length)?.value,
|
||||
context.xlen_type(),
|
||||
"event_input_offset",
|
||||
)?;
|
||||
|
||||
context.set_basic_block(failure_block);
|
||||
crate::polkavm::evm::r#return::revert(context, context.field_const(0), context.field_const(0))?;
|
||||
let arguments = if topics.is_empty() {
|
||||
[
|
||||
context.xlen_type().const_zero().as_basic_value_enum(),
|
||||
context.xlen_type().const_zero().as_basic_value_enum(),
|
||||
input_pointer.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
]
|
||||
} else {
|
||||
let topics_buffer_size = topics.len() * revive_common::BYTE_LENGTH_WORD;
|
||||
let topics_buffer_pointer = context.build_alloca(
|
||||
context.byte_type().array_type(topics_buffer_size as u32),
|
||||
"topics_buffer",
|
||||
);
|
||||
for (n, topic) in topics.iter().enumerate() {
|
||||
let topic_buffer_offset = context
|
||||
.xlen_type()
|
||||
.const_int((n * revive_common::BYTE_LENGTH_WORD) as u64, false);
|
||||
context.build_store(
|
||||
context.build_gep(
|
||||
topics_buffer_pointer,
|
||||
&[context.xlen_type().const_zero(), topic_buffer_offset],
|
||||
context.byte_type(),
|
||||
"topic_buffer_gep",
|
||||
),
|
||||
context.build_byte_swap(topic.as_basic_value_enum())?,
|
||||
)?;
|
||||
}
|
||||
[
|
||||
context
|
||||
.builder()
|
||||
.build_ptr_to_int(
|
||||
topics_buffer_pointer.value,
|
||||
context.xlen_type(),
|
||||
"event_topics_offset",
|
||||
)?
|
||||
.as_basic_value_enum(),
|
||||
context
|
||||
.xlen_type()
|
||||
.const_int(topics_buffer_size as u64, false)
|
||||
.as_basic_value_enum(),
|
||||
input_pointer.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
]
|
||||
};
|
||||
|
||||
let _ = context.build_runtime_call(runtime_api::DEPOSIT_EVENT, &arguments);
|
||||
|
||||
context.set_basic_block(join_block);
|
||||
*/
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user