-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Syscall Log & Event Automation #14
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ use contract_derive::{contract, payable}; | |
use eth_riscv_runtime::types::Mapping; | ||
|
||
use alloy_core::primitives::{Address, address, U256}; | ||
extern crate alloc; | ||
|
||
#[derive(Default)] | ||
pub struct ERC20 { | ||
|
@@ -19,7 +20,7 @@ impl ERC20 { | |
self.balance.read(owner) | ||
} | ||
|
||
pub fn transfer(&self, from: Address, to: Address, value: u64) { | ||
pub fn transfer(&self, from: Address, to: Address, value: u64) -> bool { | ||
let from_balance = self.balance.read(from); | ||
let to_balance = self.balance.read(to); | ||
|
||
|
@@ -29,16 +30,42 @@ impl ERC20 { | |
|
||
self.balance.write(from, from_balance - value); | ||
self.balance.write(to, to_balance + value); | ||
|
||
emit!("Transfer", idx from, idx to, value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't it the case that in Solidity one defines which fields are indexed when declaring the event, not when using it? What will happen if another There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I completely understand what you're saying. We could do something more similar to what we have with Solidity and write it that way. It's interesting to hear your opinions to define what would be most appropriate in this case. All of this is provisional. We could do something like this. What do you think? #[event]
struct Transfer {
#[indexed]
from: Address,
#[indexed]
to: Address,
value: U256
}
pub fn transfer(&self, from: Address, to: Address, value: u64) -> bool {
...
emit!(Transfer, from, to, value);
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, struct makes more sense. As for emitting there a couple of options:
Although one might argue that 2&3 are just sugar in top of 1. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've just uploaded the update, would the structure be something like this? Now I need to test everything byte by byte to ensure that everything matches 100% of the time There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's awesome @scab24 , thanks for doing this. Please, don't treat everything I write as a command :) I really appreciate jumping on this immediately but I don't want to add you work unnecessarily. Let's see what @gakonst and @leonardoalt think as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @deuszx Whenever I have some free time at work, I spend a bit on it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually like the struct idea. What I don't necessarily like 100% is the emit!(Transfer, from, to, value); In this version, the type is only coupled with the arguments because of the macro. In this case we don't really need the struct to exist, the macro could do everything. log::emit(Transfer::new(from, to, value)); An alternative to that would be the above, where the user gives the full instance, and uses a function instead of a macro. The function could rely on a trait "EncodeLog" (for example), which is implemented for the type when deriving @scab24 let's all agree on a solution here before you implement anything @gakonst wdyt? |
||
true | ||
} | ||
|
||
#[payable] | ||
pub fn mint(&self, to: Address, value: u64) { | ||
pub fn mint(&self, to: Address, value: u64) -> bool { | ||
let owner = msg_sender(); | ||
if owner != address!("0000000000000000000000000000000000000007") { | ||
revert(); | ||
} | ||
|
||
let to_balance = self.balance.read(to); | ||
self.balance.write(to, to_balance + value); | ||
emit!("Mint", idx to, value); | ||
true | ||
} | ||
|
||
pub fn burn(&self, from: Address, value: u64) -> bool { | ||
let from_balance = self.balance.read(from); | ||
if from_balance < value { | ||
revert(); | ||
} | ||
|
||
self.balance.write(from, from_balance - value); | ||
emit!("Burn", idx from, value); | ||
true | ||
} | ||
|
||
pub fn set_paused(&self, paused: bool) -> bool { | ||
emit!("PauseChanged", paused); | ||
true | ||
} | ||
|
||
pub fn update_metadata(&self, data: [u8; 32]) -> bool { | ||
emit!("MetadataUpdated", data); | ||
true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
use alloy_core::primitives::B256; | ||
|
||
pub fn emit_log(data: &[u8], topics: &[B256]) { | ||
let mut all_topics = [0u8; 96]; | ||
for (i, topic) in topics.iter().enumerate() { | ||
if i >= 3 { break; } | ||
let start = i * 32; | ||
all_topics[start..start + 32].copy_from_slice(topic.as_ref()); | ||
} | ||
Comment on lines
+5
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is |
||
|
||
crate::log( | ||
data.as_ptr() as u64, | ||
data.len() as u64, | ||
all_topics.as_ptr() as u64, | ||
topics.len() as u64 | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,4 +71,5 @@ syscalls!( | |
(0xf1, Call, "call"), | ||
(0xf3, Return, "return"), | ||
(0xfd, Revert, "revert"), | ||
(0xA0, Log, "log"), | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ use revm::{ | |
CallInputs, CallScheme, CallValue, Host, InstructionResult, Interpreter, InterpreterAction, | ||
InterpreterResult, SharedMemory, | ||
}, | ||
primitives::{address, Address, Bytes, ExecutionResult, Output, TransactTo, U256}, | ||
primitives::{address, Address, Bytes, ExecutionResult, Output, TransactTo, U256, B256, Log}, | ||
Database, Evm, Frame, FrameOrResult, InMemoryDB, | ||
}; | ||
use rvemu::{emulator::Emulator, exception::Exception}; | ||
|
@@ -39,8 +39,12 @@ pub fn deploy_contract(db: &mut InMemoryDB, bytecode: Bytes) -> Address { | |
result => panic!("Unexpected result: {:?}", result), | ||
} | ||
} | ||
pub struct TxResult { | ||
pub output: Vec<u8>, | ||
pub logs: Vec<Log> | ||
} | ||
|
||
pub fn run_tx(db: &mut InMemoryDB, addr: &Address, calldata: Vec<u8>) { | ||
pub fn run_tx(db: &mut InMemoryDB, addr: &Address, calldata: Vec<u8>) -> TxResult { | ||
let mut evm = Evm::builder() | ||
.with_db(db) | ||
.modify_tx_env(|tx| { | ||
|
@@ -57,10 +61,17 @@ pub fn run_tx(db: &mut InMemoryDB, addr: &Address, calldata: Vec<u8>) { | |
match result { | ||
ExecutionResult::Success { | ||
output: Output::Call(value), | ||
logs, | ||
.. | ||
} => println!("Tx result: {:?}", value), | ||
} => { | ||
println!("Tx result: {:?}", value); | ||
TxResult { | ||
output:value.into(), | ||
logs | ||
} | ||
}, | ||
result => panic!("Unexpected result: {:?}", result), | ||
}; | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
|
@@ -324,6 +335,39 @@ fn execute_riscv( | |
emu.cpu.xregs.write(12, limbs[2]); | ||
emu.cpu.xregs.write(13, limbs[3]); | ||
} | ||
Syscall::Log => { | ||
let data_ptr: u64 = emu.cpu.xregs.read(10); | ||
let data_size: u64 = emu.cpu.xregs.read(11); | ||
let topics_ptr: u64 = emu.cpu.xregs.read(12); | ||
let topics_size: u64 = emu.cpu.xregs.read(13); | ||
|
||
let data = if data_size > 0 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there's a helper for this now |
||
emu.cpu.bus | ||
.get_dram_slice(data_ptr..(data_ptr + data_size)) | ||
.unwrap_or_default() | ||
.to_vec() | ||
} else { | ||
vec![] | ||
}; | ||
|
||
let mut topics = Vec::new(); | ||
if topics_size > 0 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This if is not needed, the for loop already covers the 0 case |
||
for i in 0..topics_size { | ||
let topic_ptr = topics_ptr + (i * 32); | ||
if let Ok(topic_data) = emu.cpu.bus | ||
.get_dram_slice(topic_ptr..(topic_ptr + 32)) | ||
{ | ||
topics.push(B256::from_slice(topic_data)); | ||
} | ||
} | ||
} | ||
|
||
host.log(Log::new_unchecked( | ||
interpreter.contract.target_address, | ||
topics, | ||
data.into(), | ||
)); | ||
} | ||
} | ||
} | ||
_ => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's for sure an
alloy
function that does this conversion