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],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_elog",
|
||||
&[I32; 4],
|
||||
None,
|
||||
),
|
||||
|
||||
// TODO: Get rid of it also somehow?
|
||||
Static(
|
||||
|
@ -55,6 +55,8 @@ pub enum UserTrap {
|
||||
Unknown,
|
||||
/// Passed string had invalid utf-8 encoding
|
||||
BadUtf8,
|
||||
/// Log event error
|
||||
Log,
|
||||
/// Other error in native code
|
||||
Other,
|
||||
/// Panic with message
|
||||
@ -75,6 +77,7 @@ impl ::std::fmt::Display for UserTrap {
|
||||
UserTrap::AllocationFailed => write!(f, "Memory allocation failed (OOM)"),
|
||||
UserTrap::BadUtf8 => write!(f, "String encoding is bad utf-8 sequence"),
|
||||
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::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
|
||||
pub fn create(&mut self, context: InterpreterCallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||
@ -749,6 +767,44 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
||||
pub fn ext(&mut self) -> &mut vm::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> {
|
||||
@ -833,6 +889,9 @@ impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
|
||||
"_value" => {
|
||||
self.value(context)
|
||||
},
|
||||
"_elog" => {
|
||||
self.log(context)
|
||||
},
|
||||
_ => {
|
||||
trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name);
|
||||
Ok(self.unknown_trap(context)?)
|
||||
|
@ -680,7 +680,6 @@ fn externs() {
|
||||
|
||||
#[test]
|
||||
fn embedded_keccak() {
|
||||
|
||||
::ethcore_logger::init_log();
|
||||
let mut code = load_sample!("keccak.wasm");
|
||||
code.extend_from_slice(b"something");
|
||||
@ -703,4 +702,39 @@ fn embedded_keccak() {
|
||||
|
||||
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
|
||||
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