Merge pull request #6967 from paritytech/wasm-elog
Events in WASM runtime
This commit is contained in:
parent
454b4518f2
commit
a2d5edb8f5
@ -1 +1 @@
|
|||||||
Subproject commit c8129ce2f36c26ed634eda786960978a28e28d0e
|
Subproject commit 94b7877b5826a53627b8732ea0feb45869dd04ab
|
@ -147,6 +147,11 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
|
|||||||
&[I32],
|
&[I32],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
|
Static(
|
||||||
|
"_elog",
|
||||||
|
&[I32; 4],
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
|
||||||
// TODO: Get rid of it also somehow?
|
// TODO: Get rid of it also somehow?
|
||||||
Static(
|
Static(
|
||||||
|
@ -55,6 +55,8 @@ pub enum UserTrap {
|
|||||||
Unknown,
|
Unknown,
|
||||||
/// Passed string had invalid utf-8 encoding
|
/// Passed string had invalid utf-8 encoding
|
||||||
BadUtf8,
|
BadUtf8,
|
||||||
|
/// Log event error
|
||||||
|
Log,
|
||||||
/// Other error in native code
|
/// Other error in native code
|
||||||
Other,
|
Other,
|
||||||
/// Panic with message
|
/// Panic with message
|
||||||
@ -75,6 +77,7 @@ impl ::std::fmt::Display for UserTrap {
|
|||||||
UserTrap::AllocationFailed => write!(f, "Memory allocation failed (OOM)"),
|
UserTrap::AllocationFailed => write!(f, "Memory allocation failed (OOM)"),
|
||||||
UserTrap::BadUtf8 => write!(f, "String encoding is bad utf-8 sequence"),
|
UserTrap::BadUtf8 => write!(f, "String encoding is bad utf-8 sequence"),
|
||||||
UserTrap::GasLimit => write!(f, "Invocation resulted in gas limit violated"),
|
UserTrap::GasLimit => write!(f, "Invocation resulted in gas limit violated"),
|
||||||
|
UserTrap::Log => write!(f, "Error occured while logging an event"),
|
||||||
UserTrap::Other => write!(f, "Other unspecified error"),
|
UserTrap::Other => write!(f, "Other unspecified error"),
|
||||||
UserTrap::Panic(ref msg) => write!(f, "Panic: {}", msg),
|
UserTrap::Panic(ref msg) => write!(f, "Panic: {}", msg),
|
||||||
}
|
}
|
||||||
@ -232,6 +235,21 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn overflow_charge<F>(&mut self, f: F) -> Result<(), InterpreterError>
|
||||||
|
where F: FnOnce(&vm::Schedule) -> Option<u64>
|
||||||
|
{
|
||||||
|
let amount = match f(self.ext.schedule()) {
|
||||||
|
Some(amount) => amount,
|
||||||
|
None => { return Err(UserTrap::GasLimit.into()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
if !self.charge_gas(amount as u64) {
|
||||||
|
Err(UserTrap::GasLimit.into())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Invoke create in the state runtime
|
/// Invoke create in the state runtime
|
||||||
pub fn create(&mut self, context: InterpreterCallerContext)
|
pub fn create(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
@ -749,6 +767,44 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
pub fn ext(&mut self) -> &mut vm::Ext {
|
pub fn ext(&mut self) -> &mut vm::Ext {
|
||||||
self.ext
|
self.ext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn log(&mut self, context: InterpreterCallerContext)
|
||||||
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
|
{
|
||||||
|
// signature is:
|
||||||
|
// pub fn elog(topic_ptr: *const u8, topic_count: u32, data_ptr: *const u8, data_len: u32);
|
||||||
|
let data_len = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
|
let data_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
|
let topic_count = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
|
let topic_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
|
|
||||||
|
if topic_count > 4 {
|
||||||
|
return Err(UserTrap::Log.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.overflow_charge(|schedule|
|
||||||
|
{
|
||||||
|
let topics_gas = schedule.log_gas as u64 + schedule.log_topic_gas as u64 * topic_count as u64;
|
||||||
|
(schedule.log_data_gas as u64)
|
||||||
|
.checked_mul(schedule.log_data_gas as u64)
|
||||||
|
.and_then(|data_gas| data_gas.checked_add(topics_gas))
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut topics: Vec<H256> = Vec::with_capacity(topic_count as usize);
|
||||||
|
topics.resize(topic_count as usize, H256::zero());
|
||||||
|
for i in 0..topic_count {
|
||||||
|
let offset = i.checked_mul(32).ok_or(UserTrap::MemoryAccessViolation)?
|
||||||
|
.checked_add(topic_ptr).ok_or(UserTrap::MemoryAccessViolation)?;
|
||||||
|
|
||||||
|
*topics.get_mut(i as usize)
|
||||||
|
.expect("topics is resized to `topic_count`, i is in 0..topic count iterator, get_mut uses i as an indexer, get_mut cannot fail; qed")
|
||||||
|
= H256::from(&self.memory.get(offset, 32)?[..]);
|
||||||
|
}
|
||||||
|
self.ext.log(topics, &self.memory.get(data_ptr, data_len as usize)?).map_err(|_| UserTrap::Log)?;
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
|
impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
|
||||||
@ -833,6 +889,9 @@ impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
|
|||||||
"_value" => {
|
"_value" => {
|
||||||
self.value(context)
|
self.value(context)
|
||||||
},
|
},
|
||||||
|
"_elog" => {
|
||||||
|
self.log(context)
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name);
|
trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name);
|
||||||
Ok(self.unknown_trap(context)?)
|
Ok(self.unknown_trap(context)?)
|
||||||
|
@ -680,7 +680,6 @@ fn externs() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn embedded_keccak() {
|
fn embedded_keccak() {
|
||||||
|
|
||||||
::ethcore_logger::init_log();
|
::ethcore_logger::init_log();
|
||||||
let mut code = load_sample!("keccak.wasm");
|
let mut code = load_sample!("keccak.wasm");
|
||||||
code.extend_from_slice(b"something");
|
code.extend_from_slice(b"something");
|
||||||
@ -704,3 +703,38 @@ fn embedded_keccak() {
|
|||||||
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
|
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
|
||||||
assert_eq!(gas_left, U256::from(80_452));
|
assert_eq!(gas_left, U256::from(80_452));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This test checks the correctness of log extern
|
||||||
|
/// Target test puts one event with two topic [keccak(input), reverse(keccak(input))]
|
||||||
|
/// and reversed input as a data
|
||||||
|
#[test]
|
||||||
|
fn events() {
|
||||||
|
::ethcore_logger::init_log();
|
||||||
|
let code = load_sample!("events.wasm");
|
||||||
|
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = U256::from(100_000);
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
params.data = Some(b"something".to_vec());
|
||||||
|
|
||||||
|
let mut ext = FakeExt::new();
|
||||||
|
|
||||||
|
let (gas_left, result) = {
|
||||||
|
let mut interpreter = wasm_interpreter();
|
||||||
|
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
|
||||||
|
match result {
|
||||||
|
GasLeft::Known(_) => { panic!("events should return payload"); },
|
||||||
|
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(ext.logs.len(), 1);
|
||||||
|
let log_entry = &ext.logs[0];
|
||||||
|
assert_eq!(log_entry.topics.len(), 2);
|
||||||
|
assert_eq!(&log_entry.topics[0], &H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
|
||||||
|
assert_eq!(&log_entry.topics[1], &H256::from("871d5ea37430753faab7dff7a7187783517d83bd822c02e28a164c887e1d3768"));
|
||||||
|
assert_eq!(&log_entry.data, b"gnihtemos");
|
||||||
|
|
||||||
|
assert_eq!(&result, b"gnihtemos");
|
||||||
|
assert_eq!(gas_left, U256::from(78039));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user