Skip to content

Commit

Permalink
Merge pull request #1678 from ethereum/assert
Browse files Browse the repository at this point in the history
Implement assert()
  • Loading branch information
axic authored Feb 10, 2017
2 parents 885b6ed + fd7ffed commit 14ded49
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 6 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
### 0.4.10 (unreleased)

Features:
* Add ``assert(condition)``, which throws if condition is false.
* Type system: Support explicit conversion of external function to address.

Bugfixes:
Expand Down
4 changes: 4 additions & 0 deletions docs/control-structures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion docs/miscellaneous.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
================
Expand All @@ -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 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
Expand Down
4 changes: 3 additions & 1 deletion docs/units-and-global-variables.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)``:
Expand Down
4 changes: 3 additions & 1 deletion libsolidity/analysis/GlobalContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
make_shared<MagicVariableDeclaration>("ecrecover",
make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)),
make_shared<MagicVariableDeclaration>("ripemd160",
make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true))})
make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true)),
make_shared<MagicVariableDeclaration>("assert",
make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Location::Assert))})
{
}

Expand Down
7 changes: 4 additions & 3 deletions libsolidity/ast/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -819,8 +819,8 @@ class FunctionType: public Type
{
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
Expand All @@ -844,7 +844,8 @@ class FunctionType: public Type
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; }
Expand Down
8 changes: 8 additions & 0 deletions libsolidity/codegen/ExpressionCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(), false);
m_context << Instruction::ISZERO;
m_context.appendConditionalJumpTo(m_context.errorTag());
break;
}
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
}
Expand Down
19 changes: 19 additions & 0 deletions test/libsolidity/SolidityEndToEndTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()

}
Expand Down

0 comments on commit 14ded49

Please sign in to comment.