mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Require and Assert.
This commit is contained in:
parent
9aab3b8639
commit
47cd8964b8
@ -1,8 +1,9 @@
|
|||||||
### 0.4.10 (unreleased)
|
### 0.4.10 (unreleased)
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
* Add ``assert(condition)``, which throws if condition is false (meant for internal errors).
|
||||||
|
* Add ``require(condition)``, which throws if condition is false (meant for invalid input).
|
||||||
* Commandline interface: Do not overwrite files unless forced.
|
* Commandline interface: Do not overwrite files unless forced.
|
||||||
* Add ``assert(condition)``, which throws if condition is false.
|
|
||||||
* Introduce ``.transfer(value)`` for sending Ether.
|
* Introduce ``.transfer(value)`` for sending Ether.
|
||||||
* Code generator: Support ``revert()`` to abort with rolling back, but not consuming all gas.
|
* Code generator: Support ``revert()`` to abort with rolling back, but not consuming all gas.
|
||||||
* Inline assembly: Support ``revert`` (EIP140) as an opcode.
|
* Inline assembly: Support ``revert`` (EIP140) as an opcode.
|
||||||
|
@ -396,12 +396,19 @@ 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.
|
||||||
#. If a ``.transfer()`` fails.
|
#. If a ``.transfer()`` fails.
|
||||||
|
#. If you call ``assert`` with an argument that evaluates to false.
|
||||||
|
|
||||||
While a user-provided exception is generated in the following situations:
|
While a user-provided exception is generated in the following situations:
|
||||||
#. Calling ``throw``.
|
#. Calling ``throw``.
|
||||||
|
#. Calling ``require`` with an argument that evaluates to ``false``.
|
||||||
|
|
||||||
Internally, Solidity performs a revert operation (instruction ``0xfd``) when a user-provided exception is thrown. In contrast, it performs an invalid operation
|
Internally, Solidity performs a revert operation (instruction ``0xfd``) when a user-provided exception is thrown or the condition of
|
||||||
(instruction ``0xfe``) if a runtime exception is encountered. In both cases, this causes
|
a ``require`` call is not met. In contrast, it performs an invalid operation
|
||||||
|
(instruction ``0xfe``) if a runtime exception is encountered or the condition of an ``assert`` call is not met. 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
|
||||||
did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction
|
did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction
|
||||||
(or at least call) without effect.
|
(or at least call) without effect.
|
||||||
|
|
||||||
|
If contracts are written so that ``assert`` is only used to test internal conditions and ``require``
|
||||||
|
is used in case of malformed input, a formal analysis tool that verifies that the invalid
|
||||||
|
opcode can never be reached can be used to check for the absence of errors assuming valid inputs.
|
@ -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, revert, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send
|
.. index:: assert, block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send
|
||||||
|
|
||||||
Global Variables
|
Global Variables
|
||||||
================
|
================
|
||||||
@ -453,6 +453,8 @@ Global Variables
|
|||||||
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
|
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
|
||||||
- ``tx.gasprice`` (``uint``): gas price of the transaction
|
- ``tx.gasprice`` (``uint``): gas price of the transaction
|
||||||
- ``tx.origin`` (``address``): sender of the transaction (full call chain)
|
- ``tx.origin`` (``address``): sender of the transaction (full call chain)
|
||||||
|
- ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error)
|
||||||
|
- ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input)
|
||||||
- ``revert()``: abort execution and revert state changes
|
- ``revert()``: abort execution and revert state changes
|
||||||
- ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments
|
- ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments
|
||||||
- ``sha3(...) returns (bytes32)``: an alias to `keccak256()`
|
- ``sha3(...) returns (bytes32)``: an alias to `keccak256()`
|
||||||
|
@ -66,9 +66,10 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
|
|||||||
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)),
|
||||||
// Disabled until decision about semantics of assert is made.
|
make_shared<MagicVariableDeclaration>("assert",
|
||||||
// make_shared<MagicVariableDeclaration>("assert",
|
make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Location::Assert)),
|
||||||
// make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Location::Assert)),
|
make_shared<MagicVariableDeclaration>("require",
|
||||||
|
make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Location::Require)),
|
||||||
make_shared<MagicVariableDeclaration>("revert",
|
make_shared<MagicVariableDeclaration>("revert",
|
||||||
make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Revert))})
|
make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Revert))})
|
||||||
{
|
{
|
||||||
|
@ -847,7 +847,8 @@ public:
|
|||||||
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()
|
Assert, ///< assert()
|
||||||
|
Require ///< require()
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual Category category() const override { return Category::Function; }
|
virtual Category category() const override { return Category::Function; }
|
||||||
|
@ -879,14 +879,18 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Location::Assert:
|
case Location::Assert:
|
||||||
|
case Location::Require:
|
||||||
{
|
{
|
||||||
arguments.front()->accept(*this);
|
arguments.front()->accept(*this);
|
||||||
utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), false);
|
utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), false);
|
||||||
// jump if condition was met
|
// jump if condition was met
|
||||||
m_context << Instruction::ISZERO << Instruction::ISZERO;
|
m_context << Instruction::ISZERO << Instruction::ISZERO;
|
||||||
auto success = m_context.appendConditionalJump();
|
auto success = m_context.appendConditionalJump();
|
||||||
|
if (function.location() == Location::Assert)
|
||||||
// condition was not met, flag an error
|
// condition was not met, flag an error
|
||||||
m_context << Instruction::INVALID;
|
m_context << Instruction::INVALID;
|
||||||
|
else
|
||||||
|
m_context << u256(0) << u256(0) << Instruction::REVERT;
|
||||||
// the success branch
|
// the success branch
|
||||||
m_context << success;
|
m_context << success;
|
||||||
break;
|
break;
|
||||||
|
@ -9133,24 +9133,30 @@ BOOST_AUTO_TEST_CASE(invalid_instruction)
|
|||||||
BOOST_CHECK(callContractFunction("f()") == encodeArgs());
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
//BOOST_AUTO_TEST_CASE(assert)
|
BOOST_AUTO_TEST_CASE(assert_require)
|
||||||
//{
|
{
|
||||||
// char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
// contract C {
|
contract C {
|
||||||
// function f() {
|
function f() {
|
||||||
// assert(false);
|
assert(false);
|
||||||
// }
|
}
|
||||||
// function g(bool val) returns (bool) {
|
function g(bool val) returns (bool) {
|
||||||
// assert(val == true);
|
assert(val == true);
|
||||||
// return true;
|
return true;
|
||||||
// }
|
}
|
||||||
// }
|
function h(bool val) returns (bool) {
|
||||||
// )";
|
require(val);
|
||||||
// compileAndRun(sourceCode, 0, "C");
|
return true;
|
||||||
// BOOST_CHECK(callContractFunction("f()") == encodeArgs());
|
}
|
||||||
// BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs());
|
}
|
||||||
// BOOST_CHECK(callContractFunction("g(bool)", true) == encodeArgs(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_CHECK(callContractFunction("h(bool)", false) == encodeArgs());
|
||||||
|
BOOST_CHECK(callContractFunction("h(bool)", true) == encodeArgs(true));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(revert)
|
BOOST_AUTO_TEST_CASE(revert)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user