From f8461e9e31b96e6656b8dcabd73de2e5176e6fe3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 9 Feb 2017 01:37:53 +0000 Subject: [PATCH 1/6] Implement assert as a global function --- Changelog.md | 1 + libsolidity/analysis/GlobalContext.cpp | 4 +++- libsolidity/ast/Types.h | 7 ++++--- libsolidity/codegen/ExpressionCompiler.cpp | 8 ++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 0c4e83294..e8656ac87 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.10 (unreleased) Features: + * Add ``assert(condition)`` to abort execution. * Type system: Support explicit conversion of external function to address. Bugfixes: diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index e46868be2..cc418c5e0 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -65,7 +65,9 @@ m_magicVariables(vector>{make_shared< make_shared("ecrecover", make_shared(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)), make_shared("ripemd160", - make_shared(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true))}) + make_shared(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true)), + make_shared("assert", + make_shared(strings{"bool"}, strings{}, FunctionType::Location::Assert))}) { } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index e280b32c2..83d840e06 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -819,8 +819,8 @@ public: { Internal, ///< stack-call using plain JUMP External, ///< external call using CALL - CallCode, ///< extercnal call using CALLCODE, i.e. not exchanging the storage - DelegateCall, ///< extercnal call using DELEGATECALL, i.e. not exchanging the storage + CallCode, ///< external call using CALLCODE, i.e. not exchanging the storage + DelegateCall, ///< external call using DELEGATECALL, i.e. not exchanging the storage Bare, ///< CALL without function hash BareCallCode, ///< CALLCODE without function hash BareDelegateCall, ///< DELEGATECALL without function hash @@ -844,7 +844,8 @@ public: MulMod, ///< MULMOD ArrayPush, ///< .push() to a dynamically sized array in storage ByteArrayPush, ///< .push() to a dynamically sized byte array in storage - ObjectCreation ///< array creation using new + ObjectCreation, ///< array creation using new + Assert ///< assert() }; virtual Category category() const override { return Category::Function; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index d74d9dd3d..a99e3d406 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -863,6 +863,14 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::POP; break; } + case Location::Assert: + { + arguments.front()->accept(*this); + utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true); + m_context << Instruction::ISZERO; + m_context.appendConditionalJumpTo(m_context.errorTag()); + break; + } default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type.")); } From 8429c03f2ab81ad5fe077df6f69f6dbe88609779 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 9 Feb 2017 15:27:23 +0000 Subject: [PATCH 2/6] Add tests for assert() --- test/libsolidity/SolidityEndToEndTest.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 4924b55d0..e49db34e9 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9077,6 +9077,25 @@ BOOST_AUTO_TEST_CASE(invalid_instruction) BOOST_CHECK(callContractFunction("f()") == encodeArgs()); } +BOOST_AUTO_TEST_CASE(assert) +{ + char const* sourceCode = R"( + contract C { + function f() { + assert(false); + } + function g(bool val) returns (bool) { + assert(val == true); + return true; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs()); + BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs()); + BOOST_CHECK(callContractFunction("g(bool)", true) == encodeArgs(true)); +} + BOOST_AUTO_TEST_SUITE_END() } From 7f726de1cb861f4c7cb4e89e4ec56cd5712d0855 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 9 Feb 2017 15:31:03 +0000 Subject: [PATCH 3/6] Document assert() --- docs/miscellaneous.rst | 3 ++- docs/units-and-global-variables.rst | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 476500676..27289daf1 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -435,7 +435,7 @@ The following is the order of precedence for operators, listed in order of evalu | *16* | Comma operator | ``,`` | +------------+-------------------------------------+--------------------------------------------+ -.. index:: block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send +.. index:: block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, assert, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send Global Variables ================ @@ -460,6 +460,7 @@ Global Variables - ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error - ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256`` - ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256`` +- ``assert(bool condition)``: throws if the condition is not met - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` - ``super``: the contract one level higher in the inheritance hierarchy - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index dd3d4be8d..a6f6613f1 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -79,11 +79,13 @@ Block and Transaction Properties You can only access the hashes of the most recent 256 blocks, all other values will be zero. -.. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send +.. index:: assert, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send Mathematical and Cryptographic Functions ---------------------------------------- +``assert(bool condition)``: + throws if the condition is not met. ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. ``mulmod(uint x, uint y, uint k) returns (uint)``: From 39cd2214f2161d2e09ab0de21a64f6dcdb851b13 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 10 Feb 2017 13:31:40 +0000 Subject: [PATCH 4/6] Document user provided exceptions --- docs/control-structures.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 1c7d71f26..ff0a48ec1 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -396,6 +396,10 @@ Currently, Solidity automatically generates a runtime exception in the following #. If your contract receives Ether via a public getter function. #. If you call a zero-initialized variable of internal function type. +While a user-provided exception is generated in the following situations: +#. Calling ``throw``. +#. The condition of ``assert(condition)`` is not met. + Internally, Solidity performs an "invalid jump" when a user-provided exception is thrown. In contrast, it performs an invalid operation (instruction ``0xfe``) if a runtime exception is encountered. In both cases, this causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect From 8a3d4a0500e98dbca83c0217d141fcb4860986e3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 10 Feb 2017 13:32:36 +0000 Subject: [PATCH 5/6] Cleanup is not needed for assert() --- libsolidity/codegen/ExpressionCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index a99e3d406..f69d61db9 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -866,7 +866,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case Location::Assert: { arguments.front()->accept(*this); - utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true); + utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), false); m_context << Instruction::ISZERO; m_context.appendConditionalJumpTo(m_context.errorTag()); break; From fd7ffedead51f77135250dca29326c890f3632a2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 10 Feb 2017 21:41:40 +0000 Subject: [PATCH 6/6] Use different wording for assert --- Changelog.md | 2 +- docs/miscellaneous.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index e8656ac87..d383ba421 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,7 @@ ### 0.4.10 (unreleased) Features: - * Add ``assert(condition)`` to abort execution. + * Add ``assert(condition)``, which throws if condition is false. * Type system: Support explicit conversion of external function to address. Bugfixes: diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 27289daf1..a64ceeb2b 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -460,7 +460,7 @@ Global Variables - ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error - ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256`` - ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256`` -- ``assert(bool condition)``: throws if the condition is not met +- ``assert(bool condition)``: throws if the condition is false - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` - ``super``: the contract one level higher in the inheritance hierarchy - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address