Skip to content
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

EVM runtime: implement LOG{0..4} opcodes by emitting actor events (FIP-0049) #839

Merged
merged 14 commits into from
Nov 15, 2022
30 changes: 17 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion actors/account/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"]
[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
frc42_dispatch = "1.0.0"
fvm_shared = { version = "3.0.0-alpha.10", default-features = false }
fvm_shared = { version = "3.0.0-alpha.11", default-features = false }
serde = { version = "1.0.136", features = ["derive"] }
num-traits = "0.2.14"
num-derive = "0.3.3"
Expand Down
2 changes: 1 addition & 1 deletion actors/cron/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"]

[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
fvm_shared = { version = "3.0.0-alpha.10", default-features = false }
fvm_shared = { version = "3.0.0-alpha.11", default-features = false }
num-traits = "0.2.14"
num-derive = "0.3.3"
log = "0.4.14"
Expand Down
2 changes: 1 addition & 1 deletion actors/datacap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fvm_actor_utils = "0.1.0"
fvm_ipld_blockstore = "0.1.1"
fvm_ipld_encoding = "0.3.0"
fvm_ipld_hamt = "0.6.1"
fvm_shared = { version = "3.0.0-alpha.10", default-features = false }
fvm_shared = { version = "3.0.0-alpha.11", default-features = false }
lazy_static = "1.4.0"
num-derive = "0.3.3"
num-traits = "0.2.14"
Expand Down
10 changes: 7 additions & 3 deletions actors/datacap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,15 +390,19 @@ where
let res = self.rt.send(to, method, params.clone(), value.clone());

let rec = match res {
Ok(bytes) => {
Receipt { exit_code: ExitCode::OK, return_data: bytes, gas_used: fake_gas_used }
}
Ok(bytes) => Receipt {
exit_code: ExitCode::OK,
return_data: bytes,
gas_used: fake_gas_used,
events_root: None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ruuuuuuust, thats when you extra hate this lang.

},
Err(ae) => {
info!("datacap messenger failed: {}", ae.msg());
Receipt {
exit_code: ae.exit_code(),
return_data: RawBytes::default(),
gas_used: fake_gas_used,
events_root: None,
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion actors/eam/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fvm_ipld_blockstore = "0.1.1"
fvm_ipld_encoding = "0.3.0"
multihash = { version = "0.16.1", default-features = false }
cid = "0.8.6"
fvm_shared = { version = "3.0.0-alpha.10", default-features = false }
fvm_shared = { version = "3.0.0-alpha.11", default-features = false }
num-traits = "0.2.15"
num-derive = "0.3.3"
hex-literal = "0.3.4"
Expand Down
4 changes: 2 additions & 2 deletions actors/embryo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ keywords = ["filecoin", "web3", "wasm"]
crate-type = ["cdylib", "lib"]

[dependencies]
fvm_sdk = { version = "3.0.0-alpha.10", optional = true }
fvm_shared = { version = "3.0.0-alpha.10", optional = true }
fvm_sdk = { version = "3.0.0-alpha.11", optional = true }
fvm_shared = { version = "3.0.0-alpha.11", optional = true }

[features]
fil-actor = ["fvm_sdk", "fvm_shared"]
2 changes: 1 addition & 1 deletion actors/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"]

[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
fvm_shared = { version = "3.0.0-alpha.10", default-features = false }
fvm_shared = { version = "3.0.0-alpha.11", default-features = false }
fvm_ipld_hamt = "0.6.1"
serde = { version = "1.0.136", features = ["derive"] }
serde_tuple = "0.5"
Expand Down
57 changes: 43 additions & 14 deletions actors/evm/src/interpreter/instructions/log.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,57 @@
use crate::interpreter::instructions::memory::get_memory_region;
use fvm_ipld_encoding::{to_vec, BytesSer, RawBytes};
use fvm_shared::event::{Entry, Flags};
use {
crate::interpreter::{ExecutionState, StatusCode, System},
fil_actors_runtime::runtime::Runtime,
};

#[cfg(debug_assertions)]
pub fn log(
_state: &mut ExecutionState,
_system: &System<impl Runtime>,
_num_topics: usize,
) -> Result<(), StatusCode> {
todo!("unimplemented");
}
/// The event key for the Ethereum log data.
const EVENT_DATA_KEY: &str = "data";

/// The event keys for the Ethereum log topics.
const EVENT_TOPIC_KEYS: &[&str] = &["topic1", "topic2", "topic3", "topic4"];

#[cfg(not(debug_assertions))]
#[inline]
pub fn log(
state: &mut ExecutionState,
_system: &System<impl Runtime>,
system: &System<impl Runtime>,
num_topics: usize,
) -> Result<(), StatusCode> {
// TODO: Right now, we just drop everything. But we implement this in production anyways so
// things work.
for _ in 0..num_topics {
state.stack.pop();
// Handle the data.
// Passing in a zero-sized memory region omits the data key entirely.
// LOG0 + a zero-sized memory region emits an event with no entries whatsoever. In this case,
// the FVM will record a hollow event carrying only the emitter actor ID.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this useful?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ethereum can record events with no topics nor data; the only useful thing is the emitter address in that case. Not sure how much it gets used in practice, but it is possible with that runtime. So not doing it here would break compatibility.

let mem_index = state.stack.pop();
let size = state.stack.pop();
let region = get_memory_region(&mut state.memory, mem_index, size)
.map_err(|_| StatusCode::InvalidMemoryAccess)?;

// Extract the topics. Prefer to allocate an extra item than to incur in the cost of a
// decision based on the size of the data.
let mut entries: Vec<Entry> = Vec::with_capacity(num_topics + 1);
for key in EVENT_TOPIC_KEYS.iter().take(num_topics) {
let topic = state.stack.pop();
let entry = Entry {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to consider using Cow for the key/value to allow borrowing. Kind of a pain, something we can probably handle later, but may make things faster.

flags: Flags::FLAG_INDEXED_VALUE,
key: (*key).to_owned(),
value: to_vec(&topic)?.into(), // U256 serializes as a byte string.
};
entries.push(entry);
}

// Skip adding the data if it's zero-sized.
if let Some(r) = region {
let data = state.memory[r.offset..r.offset + r.size.get()].to_vec();
let entry = Entry {
flags: Flags::FLAG_INDEXED_VALUE,
key: EVENT_DATA_KEY.to_owned(),
value: RawBytes::serialize(BytesSer(&data))?,
};
entries.push(entry);
}

system.rt.emit_event(&entries.into())?;

Ok(())
}
133 changes: 133 additions & 0 deletions actors/evm/tests/events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
mod asm;

use fvm_ipld_encoding::{to_vec, RawBytes};
use fvm_shared::event::{ActorEvent, Entry, Flags};

mod util;

#[allow(dead_code)]
pub fn events_contract() -> Vec<u8> {
let init = r#"
"#;
let body = r#"
# method dispatch:
# - 0x00000000 -> log_zero_data
# - 0x00000001 -> log_zero_nodata
# - 0x00000002 -> log_four_data

%dispatch_begin()
%dispatch(0x00, log_zero_data)
%dispatch(0x01, log_zero_nodata)
%dispatch(0x02, log_four_data)
%dispatch_end()

#### log a zero topic event with data
log_zero_data:
jumpdest
push8 0x1122334455667788
push1 0x00
mstore
push1 0x08
push1 0x18 ## index 24 into memory as mstore writes a full word
log0
push1 0x00
push1 0x00
return

#### log a zero topic event with no data
log_zero_nodata:
jumpdest
push1 0x00
push1 0x00
log0
push1 0x00
push1 0x00
return

#### log a four topic event with data
log_four_data:
jumpdest
push8 0x1122334455667788
push1 0x00
mstore
push4 0x4444
push3 0x3333
push2 0x2222
push2 0x1111
push1 0x08
push1 0x18 ## index 24 into memory as mstore writes a full word
log4
push1 0x00
push1 0x00
return

"#;

asm::new_contract("events", init, body).unwrap()
}

#[test]
fn test_events() {
let contract = events_contract();

let mut rt = util::construct_and_verify(contract);

// log zero with data
let mut contract_params = vec![0u8; 32];
rt.expect_emitted_event(ActorEvent {
entries: vec![Entry {
flags: Flags::FLAG_INDEXED_VALUE,
key: "data".to_string(),
value: to_vec(&RawBytes::from(
[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88].to_vec(),
))
.unwrap()
.into(),
}],
});
util::invoke_contract(&mut rt, &contract_params);

// log zero without data
contract_params[3] = 0x01;
rt.expect_emitted_event(ActorEvent { entries: vec![] });
util::invoke_contract(&mut rt, &contract_params);

// log four with data
contract_params[3] = 0x02;
rt.expect_emitted_event(ActorEvent {
entries: vec![
Entry {
flags: Flags::FLAG_INDEXED_VALUE,
key: "topic1".to_string(),
value: to_vec(&RawBytes::from([0x11, 0x11].to_vec())).unwrap().into(),
},
Entry {
flags: Flags::FLAG_INDEXED_VALUE,
key: "topic2".to_string(),
value: to_vec(&RawBytes::from([0x22, 0x22].to_vec())).unwrap().into(),
},
Entry {
flags: Flags::FLAG_INDEXED_VALUE,
key: "topic3".to_string(),
value: to_vec(&RawBytes::from([0x33, 0x33].to_vec())).unwrap().into(),
},
Entry {
flags: Flags::FLAG_INDEXED_VALUE,
key: "topic4".to_string(),
value: to_vec(&RawBytes::from([0x44, 0x44].to_vec())).unwrap().into(),
},
Entry {
flags: Flags::FLAG_INDEXED_VALUE,
key: "data".to_string(),
value: to_vec(&RawBytes::from(
[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88].to_vec(),
))
.unwrap()
.into(),
},
],
});
util::invoke_contract(&mut rt, &contract_params);

rt.verify();
}
2 changes: 1 addition & 1 deletion actors/init/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"]

[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
fvm_shared = { version = "3.0.0-alpha.10", default-features = false }
fvm_shared = { version = "3.0.0-alpha.11", default-features = false }
fvm_ipld_hamt = "0.6.1"
serde = { version = "1.0.136", features = ["derive"] }
num-traits = "0.2.14"
Expand Down
Loading