Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement assert() #1678

Merged
merged 6 commits into from
Feb 10, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)`` to abort execution.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit too sloppy (people might stop reading here). What about:

Add ``assert(condition)``, which throws if condition is false.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change these late today. Can you approve it? Would like to merge it before the weekend.

* 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 not met
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if 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