Merge pull request #1678 from ethereum/assert

Implement assert()
This commit is contained in:
Alex Beregszaszi 2017-02-10 22:39:34 +00:00 committed by GitHub
commit 14ded4963d
8 changed files with 44 additions and 6 deletions

View File

@ -1,6 +1,7 @@
### 0.4.10 (unreleased) ### 0.4.10 (unreleased)
Features: Features:
* Add ``assert(condition)``, which throws if condition is false.
* Type system: Support explicit conversion of external function to address. * Type system: Support explicit conversion of external function to address.
Bugfixes: Bugfixes:

View File

@ -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 your contract receives Ether via a public getter function.
#. If you call a zero-initialized variable of internal function type. #. 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 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 (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 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

View File

@ -435,7 +435,7 @@ The following is the order of precedence for operators, listed in order of evalu
| *16* | Comma operator | ``,`` | | *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 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 - ``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`` - ``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`` - ``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`` - ``this`` (current contract's type): the current contract, explicitly convertible to ``address``
- ``super``: the contract one level higher in the inheritance hierarchy - ``super``: the contract one level higher in the inheritance hierarchy
- ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address

View File

@ -79,11 +79,13 @@ Block and Transaction Properties
You can only access the hashes of the most recent 256 blocks, all other You can only access the hashes of the most recent 256 blocks, all other
values will be zero. 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 Mathematical and Cryptographic Functions
---------------------------------------- ----------------------------------------
``assert(bool condition)``:
throws if the condition is not met.
``addmod(uint x, uint y, uint k) returns (uint)``: ``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``. 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)``: ``mulmod(uint x, uint y, uint k) returns (uint)``:

View File

@ -65,7 +65,9 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
make_shared<MagicVariableDeclaration>("ecrecover", make_shared<MagicVariableDeclaration>("ecrecover",
make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)), make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)),
make_shared<MagicVariableDeclaration>("ripemd160", 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))})
{ {
} }

View File

@ -819,8 +819,8 @@ public:
{ {
Internal, ///< stack-call using plain JUMP Internal, ///< stack-call using plain JUMP
External, ///< external call using CALL External, ///< external call using CALL
CallCode, ///< extercnal call using CALLCODE, i.e. not exchanging the storage CallCode, ///< external call using CALLCODE, i.e. not exchanging the storage
DelegateCall, ///< extercnal call using DELEGATECALL, i.e. not exchanging the storage DelegateCall, ///< external call using DELEGATECALL, i.e. not exchanging the storage
Bare, ///< CALL without function hash Bare, ///< CALL without function hash
BareCallCode, ///< CALLCODE without function hash BareCallCode, ///< CALLCODE without function hash
BareDelegateCall, ///< DELEGATECALL without function hash BareDelegateCall, ///< DELEGATECALL without function hash
@ -844,7 +844,8 @@ public:
MulMod, ///< MULMOD MulMod, ///< MULMOD
ArrayPush, ///< .push() to a dynamically sized array in storage ArrayPush, ///< .push() to a dynamically sized array in storage
ByteArrayPush, ///< .push() to a dynamically sized byte 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; } virtual Category category() const override { return Category::Function; }

View File

@ -863,6 +863,14 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::POP; m_context << Instruction::POP;
break; 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: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
} }

View File

@ -9077,6 +9077,25 @@ BOOST_AUTO_TEST_CASE(invalid_instruction)
BOOST_CHECK(callContractFunction("f()") == encodeArgs()); 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() BOOST_AUTO_TEST_SUITE_END()
} }