diff --git a/ethcore/res/wasm-tests b/ethcore/res/wasm-tests
index 9a1fcbf0d..d4920761f 160000
--- a/ethcore/res/wasm-tests
+++ b/ethcore/res/wasm-tests
@@ -1 +1 @@
-Subproject commit 9a1fcbf0d4e73bea437577e807bc38c7ba243d80
+Subproject commit d4920761f959a785c3ec15cf2848fc75795eb2d4
diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs
index 70601144d..30ce03665 100644
--- a/ethcore/wasm/src/lib.rs
+++ b/ethcore/wasm/src/lib.rs
@@ -31,6 +31,7 @@ mod result;
#[cfg(test)]
mod tests;
mod env;
+mod panic_payload;
const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024;
diff --git a/ethcore/wasm/src/panic_payload.rs b/ethcore/wasm/src/panic_payload.rs
new file mode 100644
index 000000000..e79c13a24
--- /dev/null
+++ b/ethcore/wasm/src/panic_payload.rs
@@ -0,0 +1,169 @@
+// Copyright 2015-2017 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+use byteorder::{LittleEndian, ReadBytesExt};
+use std::io;
+use std::io::Read;
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct PanicPayload {
+ pub msg: Option,
+ pub file: Option,
+ pub line: Option,
+ pub col: Option,
+}
+
+fn read_string(rdr: &mut io::Cursor<&[u8]>) -> io::Result> {
+ let string_len = rdr.read_u32::()?;
+ let string = if string_len == 0 {
+ None
+ } else {
+ let mut content = vec![0; string_len as usize];
+ rdr.read_exact(&mut content)?;
+ Some(String::from_utf8_lossy(&content).into_owned())
+ };
+ Ok(string)
+}
+
+pub fn decode(raw: &[u8]) -> PanicPayload {
+ let mut rdr = io::Cursor::new(raw);
+ let msg = read_string(&mut rdr).ok().and_then(|x| x);
+ let file = read_string(&mut rdr).ok().and_then(|x| x);
+ let line = rdr.read_u32::().ok();
+ let col = rdr.read_u32::().ok();
+ PanicPayload {
+ msg: msg,
+ file: file,
+ line: line,
+ col: col,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use byteorder::WriteBytesExt;
+
+ fn write_u32(payload: &mut Vec, val: u32) {
+ payload.write_u32::(val).unwrap();
+ }
+
+ fn write_bytes(payload: &mut Vec, bytes: &[u8]) {
+ write_u32(payload, bytes.len() as u32);
+ payload.extend(bytes);
+ }
+
+ #[test]
+ fn it_works() {
+ let mut raw = Vec::new();
+ write_bytes(&mut raw, b"msg");
+ write_bytes(&mut raw, b"file");
+ write_u32(&mut raw, 1);
+ write_u32(&mut raw, 2);
+
+ let payload = decode(&raw);
+
+ assert_eq!(
+ payload,
+ PanicPayload {
+ msg: Some("msg".to_string()),
+ file: Some("file".to_string()),
+ line: Some(1),
+ col: Some(2),
+ }
+ );
+ }
+
+ #[test]
+ fn only_msg() {
+ let mut raw = Vec::new();
+ write_bytes(&mut raw, b"msg");
+
+ let payload = decode(&raw);
+
+ assert_eq!(
+ payload,
+ PanicPayload {
+ msg: Some("msg".to_string()),
+ file: None,
+ line: None,
+ col: None,
+ }
+ );
+ }
+
+ #[test]
+ fn invalid_utf8() {
+ let mut raw = Vec::new();
+ write_bytes(&mut raw, b"\xF0\x90\x80msg");
+ write_bytes(&mut raw, b"file");
+ write_u32(&mut raw, 1);
+ write_u32(&mut raw, 2);
+
+ let payload = decode(&raw);
+
+ assert_eq!(
+ payload,
+ PanicPayload {
+ msg: Some("�msg".to_string()),
+ file: Some("file".to_string()),
+ line: Some(1),
+ col: Some(2),
+ }
+ );
+ }
+
+ #[test]
+ fn trailing_data() {
+ let mut raw = Vec::new();
+ write_bytes(&mut raw, b"msg");
+ write_bytes(&mut raw, b"file");
+ write_u32(&mut raw, 1);
+ write_u32(&mut raw, 2);
+ write_u32(&mut raw, 0xdeadbeef);
+
+ let payload = decode(&raw);
+
+ assert_eq!(
+ payload,
+ PanicPayload {
+ msg: Some("msg".to_string()),
+ file: Some("file".to_string()),
+ line: Some(1),
+ col: Some(2),
+ }
+ );
+ }
+
+ #[test]
+ fn empty_str_is_none() {
+ let mut raw = Vec::new();
+ write_bytes(&mut raw, b"msg");
+ write_bytes(&mut raw, b"");
+
+ let payload = decode(&raw);
+
+ assert_eq!(
+ payload,
+ PanicPayload {
+ msg: Some("msg".to_string()),
+ file: None,
+ line: None,
+ col: None,
+ }
+ );
+ }
+}
diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs
index e5a6dc910..3dfaffad0 100644
--- a/ethcore/wasm/src/runtime.rs
+++ b/ethcore/wasm/src/runtime.rs
@@ -21,6 +21,7 @@ use std::sync::Arc;
use byteorder::{LittleEndian, ByteOrder};
use vm;
+use panic_payload;
use parity_wasm::interpreter;
use wasm_utils::rules;
use bigint::prelude::U256;
@@ -626,16 +627,30 @@ impl<'a, 'b> Runtime<'a, 'b> {
fn user_panic(&mut self, context: InterpreterCallerContext)
-> Result, InterpreterError>
{
- let msg_len = context.value_stack.pop_as::()? as u32;
- let msg_ptr = context.value_stack.pop_as::()? as u32;
+ let payload_len = context.value_stack.pop_as::()? as u32;
+ let payload_ptr = context.value_stack.pop_as::()? as u32;
- let msg = String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?)
- .map_err(|_| UserTrap::BadUtf8)?;
+ let raw_payload = self.memory.get(payload_ptr, payload_len as usize)?;
+ let payload = panic_payload::decode(&raw_payload);
+ let msg = format!(
+ "{msg}, {file}:{line}:{col}",
+ msg = payload
+ .msg
+ .as_ref()
+ .map(String::as_ref)
+ .unwrap_or(""),
+ file = payload
+ .file
+ .as_ref()
+ .map(String::as_ref)
+ .unwrap_or(""),
+ line = payload.line.unwrap_or(0),
+ col = payload.col.unwrap_or(0)
+ );
+ trace!(target: "wasm", "Contract custom panic message: {}", msg);
- trace!(target: "wasm", "Contract custom panic message: {}", msg);
-
- Err(UserTrap::Panic(msg).into())
- }
+ Err(UserTrap::Panic(msg).into())
+ }
fn block_hash(&mut self, context: InterpreterCallerContext)
-> Result, InterpreterError>
diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs
index 0a93be11c..9f8b7ddec 100644
--- a/ethcore/wasm/src/tests.rs
+++ b/ethcore/wasm/src/tests.rs
@@ -180,7 +180,7 @@ fn dispersion() {
result,
vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0]
);
- assert_eq!(gas_left, U256::from(99_469));
+ assert_eq!(gas_left, U256::from(96_961));
}
#[test]
@@ -208,7 +208,7 @@ fn suicide_not() {
result,
vec![0u8]
);
- assert_eq!(gas_left, U256::from(99_724));
+ assert_eq!(gas_left, U256::from(97_290));
}
#[test]
@@ -240,7 +240,7 @@ fn suicide() {
};
assert!(ext.suicides.contains(&refund));
- assert_eq!(gas_left, U256::from(99_663));
+ assert_eq!(gas_left, U256::from(97_249));
}
#[test]
@@ -312,7 +312,7 @@ fn call_code() {
assert!(ext.calls.contains(
&FakeCall {
call_type: FakeCallType::Call,
- gas: U256::from(98_709),
+ gas: U256::from(98_713),
sender_address: Some(sender),
receive_address: Some(receiver),
value: None,
@@ -324,7 +324,7 @@ fn call_code() {
// siphash result
let res = LittleEndian::read_u32(&result[..]);
assert_eq!(res, 4198595614);
- assert_eq!(gas_left, U256::from(93_851));
+ assert_eq!(gas_left, U256::from(93_855));
}
#[test]
@@ -357,7 +357,7 @@ fn call_static() {
assert!(ext.calls.contains(
&FakeCall {
call_type: FakeCallType::Call,
- gas: U256::from(98_709),
+ gas: U256::from(98_713),
sender_address: Some(sender),
receive_address: Some(receiver),
value: None,
@@ -370,7 +370,7 @@ fn call_static() {
let res = LittleEndian::read_u32(&result[..]);
assert_eq!(res, 317632590);
- assert_eq!(gas_left, U256::from(93_851));
+ assert_eq!(gas_left, U256::from(93_855));
}
// Realloc test
@@ -393,7 +393,7 @@ fn realloc() {
}
};
assert_eq!(result, vec![0u8; 2]);
- assert_eq!(gas_left, U256::from(99_787));
+ assert_eq!(gas_left, U256::from(97_278));
}
// Tests that contract's ability to read from a storage
@@ -419,7 +419,7 @@ fn storage_read() {
};
assert_eq!(Address::from(&result[12..32]), address);
- assert_eq!(gas_left, U256::from(99_702));
+ assert_eq!(gas_left, U256::from(99_706));
}
// Tests keccak calculation
@@ -445,7 +445,7 @@ fn keccak() {
};
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
- assert_eq!(gas_left, U256::from(84_520));
+ assert_eq!(gas_left, U256::from(82_009));
}
// memcpy test.
@@ -477,7 +477,7 @@ fn memcpy() {
};
assert_eq!(result, test_payload);
- assert_eq!(gas_left, U256::from(75_324));
+ assert_eq!(gas_left, U256::from(72_773));
}
// memmove test.
@@ -509,7 +509,7 @@ fn memmove() {
};
assert_eq!(result, test_payload);
- assert_eq!(gas_left, U256::from(75_324));
+ assert_eq!(gas_left, U256::from(72_773));
}
// memset test
@@ -534,7 +534,7 @@ fn memset() {
};
assert_eq!(result, vec![228u8; 8192]);
- assert_eq!(gas_left, U256::from(75_324));
+ assert_eq!(gas_left, U256::from(72_763));
}
macro_rules! reqrep_test {
@@ -591,7 +591,7 @@ fn math_add() {
U256::from_dec_str("1888888888888888888888888888887").unwrap(),
(&result[..]).into()
);
- assert_eq!(gas_left, U256::from(98_576));
+ assert_eq!(gas_left, U256::from(96_084));
}
// multiplication
@@ -613,7 +613,7 @@ fn math_mul() {
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
(&result[..]).into()
);
- assert_eq!(gas_left, U256::from(97_726));
+ assert_eq!(gas_left, U256::from(95_234));
}
// subtraction
@@ -635,7 +635,7 @@ fn math_sub() {
U256::from_dec_str("111111111111111111111111111111").unwrap(),
(&result[..]).into()
);
- assert_eq!(gas_left, U256::from(98_568));
+ assert_eq!(gas_left, U256::from(96_076));
}
// subtraction with overflow
@@ -677,7 +677,7 @@ fn math_div() {
U256::from_dec_str("1125000").unwrap(),
(&result[..]).into()
);
- assert_eq!(gas_left, U256::from(91_564));
+ assert_eq!(gas_left, U256::from(89_074));
}
// This test checks the ability of wasm contract to invoke
@@ -765,7 +765,7 @@ fn externs() {
"Gas limit requested and returned does not match"
);
- assert_eq!(gas_left, U256::from(97_740));
+ assert_eq!(gas_left, U256::from(95_291));
}
#[test]
@@ -791,7 +791,7 @@ fn embedded_keccak() {
};
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
- assert_eq!(gas_left, U256::from(84_520));
+ assert_eq!(gas_left, U256::from(82_009));
}
/// This test checks the correctness of log extern
@@ -826,5 +826,5 @@ fn events() {
assert_eq!(&log_entry.data, b"gnihtemos");
assert_eq!(&result, b"gnihtemos");
- assert_eq!(gas_left, U256::from(82_721));
+ assert_eq!(gas_left, U256::from(80_199));
}