diff options
author | Alex Beregszaszi <alex@rtfs.hu> | 2017-06-26 07:06:20 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-26 07:06:20 +0800 |
commit | 29b8cdb594044525b398994fe9855fbbdb76981b (patch) | |
tree | 4d3b3a3706c4a89194f728cca37a6c457c5857fb | |
parent | b83f77e0e567b58c2ada831e526ad5bacfed8b40 (diff) | |
parent | 784b72bb72631f134fbbc0cc3b7acc109dd3f785 (diff) | |
download | dexon-solidity-29b8cdb594044525b398994fe9855fbbdb76981b.tar.gz dexon-solidity-29b8cdb594044525b398994fe9855fbbdb76981b.tar.zst dexon-solidity-29b8cdb594044525b398994fe9855fbbdb76981b.zip |
Merge pull request #2298 from ethereum/fixThrowRevert
Change invalid opcode to revert for input validation.
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | docs/control-structures.rst | 65 | ||||
-rw-r--r-- | docs/miscellaneous.rst | 2 | ||||
-rw-r--r-- | docs/units-and-global-variables.rst | 19 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.cpp | 14 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.h | 10 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 15 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 10 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 9 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 16 | ||||
-rw-r--r-- | test/libsolidity/Assembly.cpp | 4 | ||||
-rw-r--r-- | test/libsolidity/JSONCompiler.cpp | 26 | ||||
-rw-r--r-- | test/libsolidity/StandardCompiler.cpp | 30 |
13 files changed, 147 insertions, 74 deletions
diff --git a/Changelog.md b/Changelog.md index ee952a37..6e15c9db 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ Features: * Code Generator: Added the Whiskers template system. Bugfixes: + * Code generator: Use ``REVERT`` instead of ``INVALID`` for generated input validation routines. * Type Checker: Fix address literals not being treated as compile-time constants. * Type Checker: Make UTF8-validation a bit more sloppy to include more valid sequences. * Fixed crash concerning non-callable types. diff --git a/docs/control-structures.rst b/docs/control-structures.rst index a2d34274..03787c20 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -361,55 +361,72 @@ As a result, the following code is legal, despite being poorly written:: return bar;// returns 5 } -.. index:: ! exception, ! throw +.. index:: ! exception, ! throw, ! assert, ! require, ! revert -Exceptions -========== +Error handling: Assert, Require, Revert and Exceptions +====================================================== + +Solidity uses state-reverting exceptions to handle errors. Such an exception will undo all changes made to the +state in the current call (and all its sub-calls) and also flag an error to the caller. +The convenience functions ``assert`` and ``require`` can be used to check for conditions and throw an exception +if the condition is not met. The difference between the two is that ``assert`` should only be used for internal errors +and ``require`` should be used to check external conditions (invalid inputs or errors in external components). +The idea behind that is that analysis tools can check your contract and try to come up with situations and +series of function calls that will reach a failing assertion. If this is possible, this means there is a bug +in your contract you should fix. -There are some cases where exceptions are thrown automatically (see below). You can use the ``throw`` instruction to throw an exception manually. The effect of an exception is that the currently executing call is stopped and reverted (i.e. all changes to the state and balances are undone) and the exception is also "bubbled up" through Solidity function calls (exceptions are ``send`` and the low-level functions ``call``, ``delegatecall`` and ``callcode``, those return ``false`` in case of an exception). +There are two other ways to trigger execptions: The ``revert`` function can be used to flag an error and +revert the current call. In the future it might be possible to also include details about the error +in a call to ``revert``. The ``throw`` keyword can also be used as an alternative to ``revert()``. + +When exceptions happen in a sub-call, they "bubble up" (i.e. exceptions are rethrown) automatically. Exceptions to this rule are ``send`` +and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those return ``false`` in case +of an exception instead of "bubbling up". Catching exceptions is not yet possible. -In the following example, we show how ``throw`` can be used to easily revert an Ether transfer and also how to check the return value of ``send``:: +In the following example, you can see how ``require`` can be used to easily check conditions on inputs +and how ``assert`` can be used for internal error checking:: pragma solidity ^0.4.0; contract Sharer { function sendHalf(address addr) payable returns (uint balance) { - if (!addr.send(msg.value / 2)) - throw; // also reverts the transfer to Sharer + require(msg.value % 2 == 0); // Only allow even numbers + uint balanceBeforeTransfer = this.balance; + addr.transfer(msg.value / 2); + // Since transfer throws an exception on failure and + // cannot call back here, there should be no way for us to + // still have half of the money. + assert(this.balance == balanceBeforeTransfer - msg.value / 2); return this.balance; } } -Currently, Solidity automatically generates a runtime exception in the following situations: +An ``assert``-style exception is generated in the following situations: #. If you access an array at a too large or negative index (i.e. ``x[i]`` where ``i >= x.length`` or ``i < 0``). #. If you access a fixed-length ``bytesN`` at a too large or negative index. -#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``. -#. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly"). #. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). #. If you shift by a negative amount. #. If you convert a value too big or negative into an enum type. -#. If you perform an external function call targeting a contract that contains no code. -#. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). -#. If your contract receives Ether via a public getter function. #. If you call a zero-initialized variable of internal function type. -#. 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: +A ``require``-style exception is generated in the following situations: #. Calling ``throw``. #. Calling ``require`` with an argument that evaluates to ``false``. +#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``. +#. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly"). +#. If you perform an external function call targeting a contract that contains no code. +#. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). +#. If your contract receives Ether via a public getter function. +#. If a ``.transfer()`` fails. -Internally, Solidity performs a revert operation (instruction ``0xfd``) when a user-provided exception is thrown or the condition of -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 +Internally, Solidity performs a revert operation (instruction ``0xfd``) for a ``require``-style exception and executes an invalid operation +(instruction ``0xfe``) to throw an ``assert``-style exception. In both cases, this causes +the EVM to revert all changes made to the state. The reason for reverting 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 -(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. +(or at least call) without effect. Note that ``assert``-style exceptions consume all gas available to the call, while +``revert``-style exceptions will not consume any gas starting from the Metropolis release.
\ No newline at end of file diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 17f2dcf9..182de33a 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -464,7 +464,7 @@ Global Variables - ``tx.gasprice`` (``uint``): gas price of the transaction - ``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) +- ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component) - ``revert()``: abort execution and revert state changes - ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments - ``sha3(...) returns (bytes32)``: an alias to `keccak256()` diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 779e3819..7d21f065 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -79,13 +79,23 @@ Block and Transaction Properties You can only access the hashes of the most recent 256 blocks, all other values will be zero. -.. index:: assert, revert, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send +.. index:: assert, revert, require + +Error Handling +-------------- + +``assert(bool condition)``: + throws if the condition is not met - to be used for internal errors. +``require(bool condition)``: + throws if the condition is not met - to be used for errors in inputs or external components. +``revert()``: + abort execution and revert state changes + +.. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, 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)``: @@ -101,8 +111,6 @@ Mathematical and Cryptographic Functions ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover the address associated with the public key from elliptic curve signature or return zero on error (`example usage <https://ethereum.stackexchange.com/q/1777/222>`_) -``revert()``: - abort execution and revert state changes In the above, "tightly packed" means that the arguments are concatenated without padding. This means that the following are all identical:: @@ -122,6 +130,7 @@ This means that, for example, ``keccak256(0) == keccak256(uint8(0))`` and It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net. +.. index:: balance, send, transfer, call, callcode, delegatecall .. _address_related: Address Related diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 9d0d6d37..1937b529 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -244,6 +244,20 @@ CompilerContext& CompilerContext::appendConditionalInvalid() return *this; } +CompilerContext& CompilerContext::appendRevert() +{ + return *this << u256(0) << u256(0) << Instruction::REVERT; +} + +CompilerContext& CompilerContext::appendConditionalRevert() +{ + *this << Instruction::ISZERO; + eth::AssemblyItem afterTag = appendConditionalJump(); + appendRevert(); + *this << afterTag; + return *this; +} + void CompilerContext::resetVisitedNodes(ASTNode const* _node) { stack<ASTNode const*> newStack; diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 030b35a6..1968c1e1 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -136,11 +136,15 @@ public: /// Appends a JUMP to a new tag and @returns the tag eth::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); } /// Appends a JUMP to a tag already on the stack - CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary); + CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary); /// Appends an INVALID instruction - CompilerContext& appendInvalid(); + CompilerContext& appendInvalid(); /// Appends a conditional INVALID instruction - CompilerContext& appendConditionalInvalid(); + CompilerContext& appendConditionalInvalid(); + /// Appends a REVERT(0, 0) call + CompilerContext& appendRevert(); + /// Appends a conditional REVERT(0, 0) call + CompilerContext& appendConditionalRevert(); /// Appends a JUMP to a specific tag CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; } /// Appends pushing of a new tag and @returns the new tag. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index bfe72961..7fed1975 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -392,7 +392,13 @@ void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function) Instruction::OR; } -void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded, bool _chopSignBits) +void CompilerUtils::convertType( + Type const& _typeOnStack, + Type const& _targetType, + bool _cleanupNeeded, + bool _chopSignBits, + bool _asPartOfArgumentDecoding +) { // For a type extension, we need to remove all higher-order bits that we might have ignored in // previous operations. @@ -450,7 +456,10 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack); solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; - m_context.appendConditionalInvalid(); + if (_asPartOfArgumentDecoding) + m_context.appendConditionalRevert(); + else + m_context.appendConditionalInvalid(); enumOverflowCheckPending = false; } break; @@ -985,7 +994,7 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda m_context << shiftFactor << Instruction::MUL; } if (_fromCalldata) - convertType(_type, _type, true); + convertType(_type, _type, true, false, true); return numBytes; } diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index a88951bc..caf2cdc2 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -135,7 +135,15 @@ public: /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be /// necessary. /// If @a _chopSignBits, the function resets the signed bits out of the width of the signed integer. - void convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false, bool _chopSignBits = false); + /// If @a _asPartOfArgumentDecoding is true, failed conversions are flagged via REVERT, + /// otherwise they are flagged with INVALID. + void convertType( + Type const& _typeOnStack, + Type const& _targetType, + bool _cleanupNeeded = false, + bool _chopSignBits = false, + bool _asPartOfArgumentDecoding = false + ); /// Creates a zero-value for the given type and puts it onto the stack. This might allocate /// memory for memory references. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index dc090634..ace82ad4 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -111,7 +111,7 @@ void ContractCompiler::appendCallValueCheck() { // Throw if function is not payable but call contained ether. m_context << Instruction::CALLVALUE; - m_context.appendConditionalInvalid(); + m_context.appendConditionalRevert(); } void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract) @@ -276,7 +276,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary()); } else - m_context.appendInvalid(); + m_context.appendRevert(); for (auto const& it: interfaceFunctions) { @@ -368,7 +368,7 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter // copy to memory // move calldata type up again CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack()); - CompilerUtils(m_context).convertType(*calldataType, arrayType); + CompilerUtils(m_context).convertType(*calldataType, arrayType, false, false, true); // fetch next pointer again CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack()); } @@ -805,8 +805,7 @@ bool ContractCompiler::visit(Throw const& _throw) { CompilerContext::LocationSetter locationSetter(m_context, _throw); // Do not send back an error detail. - m_context << u256(0) << u256(0); - m_context << Instruction::REVERT; + m_context.appendRevert(); return false; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 03bba80c..a7cfe4dc 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -587,7 +587,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::CREATE; // Check if zero (out of stack or not enough balance). m_context << Instruction::DUP1 << Instruction::ISZERO; - m_context.appendConditionalInvalid(); + m_context.appendConditionalRevert(); if (function.valueSet()) m_context << swapInstruction(1) << Instruction::POP; break; @@ -651,7 +651,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { // Check if zero (out of stack or not enough balance). m_context << Instruction::ISZERO; - m_context.appendConditionalInvalid(); + m_context.appendConditionalRevert(); } break; case FunctionType::Kind::Selfdestruct: @@ -660,9 +660,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::SELFDESTRUCT; break; case FunctionType::Kind::Revert: - // memory offset returned - zero length - m_context << u256(0) << u256(0); - m_context << Instruction::REVERT; + m_context.appendRevert(); break; case FunctionType::Kind::SHA3: { @@ -888,9 +886,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) auto success = m_context.appendConditionalJump(); if (function.kind() == FunctionType::Kind::Assert) // condition was not met, flag an error - m_context << Instruction::INVALID; + m_context.appendInvalid(); else - m_context << u256(0) << u256(0) << Instruction::REVERT; + m_context.appendRevert(); // the success branch m_context << success; break; @@ -1695,7 +1693,7 @@ void ExpressionCompiler::appendExternalFunctionCall( if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall) { m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; - m_context.appendConditionalInvalid(); + m_context.appendConditionalRevert(); existenceChecked = true; } @@ -1731,7 +1729,7 @@ void ExpressionCompiler::appendExternalFunctionCall( { //Propagate error condition (if CALL pushes 0 on stack). m_context << Instruction::ISZERO; - m_context.appendConditionalInvalid(); + m_context.appendConditionalRevert(); } utils().popStackSlots(remainsSize); diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index e52f4d50..99a2996e 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -119,8 +119,8 @@ BOOST_AUTO_TEST_CASE(location_test) shared_ptr<string const> n = make_shared<string>(""); AssemblyItems items = compileContract(sourceCode); vector<SourceLocation> locations = - vector<SourceLocation>(17, SourceLocation(2, 75, n)) + - vector<SourceLocation>(30, SourceLocation(20, 72, n)) + + vector<SourceLocation>(19, SourceLocation(2, 75, n)) + + vector<SourceLocation>(32, SourceLocation(20, 72, n)) + vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + vector<SourceLocation>(2, SourceLocation(58, 67, n)) + vector<SourceLocation>(3, SourceLocation(20, 72, n)); diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index f5154395..aa690f0b 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -73,28 +73,36 @@ BOOST_AUTO_TEST_CASE(basic_compilation) Json::Value contract = result["contracts"]["fileA:A"]; BOOST_CHECK(contract.isObject()); BOOST_CHECK(contract["interface"].isString()); - BOOST_CHECK(contract["interface"].asString() == "[]"); + BOOST_CHECK_EQUAL(contract["interface"].asString(), "[]"); BOOST_CHECK(contract["bytecode"].isString()); - BOOST_CHECK(dev::test::bytecodeSansMetadata(contract["bytecode"].asString()) == - "60606040523415600b57fe5b5b60338060196000396000f30060606040525bfe00"); + BOOST_CHECK_EQUAL( + dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), + "60606040523415600e57600080fd5b5b603680601c6000396000f30060606040525b600080fd00" + ); BOOST_CHECK(contract["runtimeBytecode"].isString()); - BOOST_CHECK(dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()) == - "60606040525bfe00"); + BOOST_CHECK_EQUAL( + dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), + "60606040525b600080fd00" + ); BOOST_CHECK(contract["functionHashes"].isObject()); BOOST_CHECK(contract["gasEstimates"].isObject()); - BOOST_CHECK(dev::jsonCompactPrint(contract["gasEstimates"]) == - "{\"creation\":[62,10200],\"external\":{},\"internal\":{}}"); + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(contract["gasEstimates"]), + "{\"creation\":[62,10800],\"external\":{},\"internal\":{}}" + ); BOOST_CHECK(contract["metadata"].isString()); BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); BOOST_CHECK(result["sources"].isObject()); BOOST_CHECK(result["sources"]["fileA"].isObject()); BOOST_CHECK(result["sources"]["fileA"]["AST"].isObject()); - BOOST_CHECK(dev::jsonCompactPrint(result["sources"]["fileA"]["AST"]) == + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(result["sources"]["fileA"]["AST"]), "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}}," "\"children\":[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null]," "\"contractKind\":\"contract\",\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1]," "\"name\":\"A\",\"nodes\":[null],\"scope\":2},\"id\":1,\"name\":\"ContractDefinition\"," - "\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}"); + "\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}" + ); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 35644a4d..be13d46b 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -189,37 +189,43 @@ BOOST_AUTO_TEST_CASE(basic_compilation) Json::Value contract = getContractResult(result, "fileA", "A"); BOOST_CHECK(contract.isObject()); BOOST_CHECK(contract["abi"].isArray()); - BOOST_CHECK(dev::jsonCompactPrint(contract["abi"]) == "[]"); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[]"); BOOST_CHECK(contract["devdoc"].isObject()); - BOOST_CHECK(dev::jsonCompactPrint(contract["devdoc"]) == "{\"methods\":{}}"); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["devdoc"]), "{\"methods\":{}}"); BOOST_CHECK(contract["userdoc"].isObject()); - BOOST_CHECK(dev::jsonCompactPrint(contract["userdoc"]) == "{\"methods\":{}}"); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["userdoc"]), "{\"methods\":{}}"); BOOST_CHECK(contract["evm"].isObject()); /// @TODO check evm.methodIdentifiers, legacyAssembly, bytecode, deployedBytecode BOOST_CHECK(contract["evm"]["bytecode"].isObject()); BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString()); - BOOST_CHECK(dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()) == - "60606040523415600b57fe5b5b60338060196000396000f30060606040525bfe00"); + BOOST_CHECK_EQUAL( + dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()), + "60606040523415600e57600080fd5b5b603680601c6000396000f30060606040525b600080fd00" + ); BOOST_CHECK(contract["evm"]["assembly"].isString()); BOOST_CHECK(contract["evm"]["assembly"].asString().find( " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(callvalue))\n" - " invalid\ntag_1:\ntag_2:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n" + " 0x0\n dup1\n revert\ntag_1:\ntag_2:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n" " return\nstop\n\nsub_0: assembly {\n /* \"fileA\":0:14 contract A { } */\n" - " mstore(0x40, 0x60)\n tag_1:\n invalid\n\n" - " auxdata: 0xa165627a7a72305820") != std::string::npos); + " mstore(0x40, 0x60)\n tag_1:\n 0x0\n dup1\n revert\n\n" + " auxdata: 0xa165627a7a7230582") == 0); BOOST_CHECK(contract["evm"]["gasEstimates"].isObject()); - BOOST_CHECK(dev::jsonCompactPrint(contract["evm"]["gasEstimates"]) == - "{\"creation\":{\"codeDepositCost\":\"10200\",\"executionCost\":\"62\",\"totalCost\":\"10262\"}}"); + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(contract["evm"]["gasEstimates"]), + "{\"creation\":{\"codeDepositCost\":\"10800\",\"executionCost\":\"62\",\"totalCost\":\"10862\"}}" + ); BOOST_CHECK(contract["metadata"].isString()); BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); BOOST_CHECK(result["sources"].isObject()); BOOST_CHECK(result["sources"]["fileA"].isObject()); BOOST_CHECK(result["sources"]["fileA"]["legacyAST"].isObject()); - BOOST_CHECK(dev::jsonCompactPrint(result["sources"]["fileA"]["legacyAST"]) == + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(result["sources"]["fileA"]["legacyAST"]), "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}},\"children\":" "[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null],\"contractKind\":\"contract\"," "\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodes\":[null],\"scope\":2}," - "\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}"); + "\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}" + ); } BOOST_AUTO_TEST_SUITE_END() |