From 290a857f2e0352b06803e2291d1790413bc630bd Mon Sep 17 00:00:00 2001 From: Christoph Burgdorf Date: Mon, 19 Apr 2021 16:59:58 +0200 Subject: [PATCH] Add support for revert reason strings in assert statement Closes #288 --- compiler/src/yul/mappers/functions.rs | 11 +++++-- compiler/tests/features.rs | 34 ++++++++++++++++++---- compiler/tests/fixtures/features/assert.fe | 8 ++++- compiler/tests/utils.rs | 1 - newsfragments/288.feature.md | 17 +++++++++++ 5 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 newsfragments/288.feature.md diff --git a/compiler/src/yul/mappers/functions.rs b/compiler/src/yul/mappers/functions.rs index f34ea63b80..b463e510b8 100644 --- a/compiler/src/yul/mappers/functions.rs +++ b/compiler/src/yul/mappers/functions.rs @@ -188,10 +188,15 @@ fn emit(context: &Context, stmt: &Node) -> yul::Statement { } fn assert(context: &Context, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::Assert { test, msg: _ } = &stmt.kind { + if let fe::FuncStmt::Assert { test, msg } = &stmt.kind { let test = expressions::expr(context, test); - - return statement! { if (iszero([test])) { (revert(0, 0)) } }; + return match msg { + Some(val) => { + let msg = expressions::expr(context, val); + statement! { if (iszero([test])) { (revert_with_reason_string([msg])) } } + } + None => statement! { if (iszero([test])) { (revert(0, 0)) } }, + }; } unreachable!() diff --git a/compiler/tests/features.rs b/compiler/tests/features.rs index 7bc129eb7f..b1531fd905 100644 --- a/compiler/tests/features.rs +++ b/compiler/tests/features.rs @@ -75,17 +75,41 @@ fn test_assert() { let exit1 = harness.capture_call(&mut executor, "bar", &[uint_token(4)]); - assert!(matches!( - exit1, - evm::Capture::Exit((evm::ExitReason::Revert(_), _)) - )); + match exit1 { + evm::Capture::Exit((evm::ExitReason::Revert(_), output)) => assert_eq!(output.len(), 0), + _ => panic!("Did not revert correctly"), + } let exit2 = harness.capture_call(&mut executor, "bar", &[uint_token(42)]); assert!(matches!( exit2, evm::Capture::Exit((evm::ExitReason::Succeed(_), _)) - )) + )); + + let exit3 = + harness.capture_call(&mut executor, "revert_with_static_string", &[uint_token(4)]); + + match exit3 { + evm::Capture::Exit((evm::ExitReason::Revert(_), output)) => { + assert_eq!(output, encode_error_reason("Must be greater than five")) + } + _ => panic!("Did not revert correctly"), + } + + let reason = "A very looooooooooooooong reason that consumes multiple words"; + let exit4 = harness.capture_call( + &mut executor, + "revert_with", + &[uint_token(4), string_token(&reason)], + ); + + match exit4 { + evm::Capture::Exit((evm::ExitReason::Revert(_), output)) => { + assert_eq!(output, encode_error_reason(reason)) + } + _ => panic!("Did not revert correctly"), + } }) } diff --git a/compiler/tests/fixtures/features/assert.fe b/compiler/tests/fixtures/features/assert.fe index fc0c768778..92a4bca3fa 100644 --- a/compiler/tests/fixtures/features/assert.fe +++ b/compiler/tests/fixtures/features/assert.fe @@ -1,3 +1,9 @@ contract Foo: pub def bar(baz: u256): - assert baz > 5 \ No newline at end of file + assert baz > 5 + + pub def revert_with_static_string(baz: u256): + assert baz > 5, "Must be greater than five" + + pub def revert_with(baz: u256, reason: string1000): + assert baz > 5, reason \ No newline at end of file diff --git a/compiler/tests/utils.rs b/compiler/tests/utils.rs index ff12c05c1e..7c8f7b451e 100644 --- a/compiler/tests/utils.rs +++ b/compiler/tests/utils.rs @@ -350,7 +350,6 @@ pub fn test_runtime_functions_revert( } } - pub struct Runtime { functions: Vec, test_statements: Vec, diff --git a/newsfragments/288.feature.md b/newsfragments/288.feature.md new file mode 100644 index 0000000000..379208b6d6 --- /dev/null +++ b/newsfragments/288.feature.md @@ -0,0 +1,17 @@ +Support for revert messages in assert statements + +E.g + +``` +assert a == b, "my revert statement" +``` + +The provided string is abi-encoded as if it were a call +to a function `Error(string)`. For example, the revert string `"Not enough Ether provided."` returns the following hexadecimal as error return data: + +``` +0x08c379a0 // Function selector for Error(string) +0x0000000000000000000000000000000000000000000000000000000000000020 // Data offset +0x000000000000000000000000000000000000000000000000000000000000001a // String length +0x4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 // String data +``` \ No newline at end of file