diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 89f268b33..3fb251d95 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -233,23 +233,20 @@ private: }; /** - * Pseudo AST node that is used as declaration for "this", "msg", "tx" and "block" when the - * identifier is encountered. Will never have a valid location in the source code. + * Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global + * functions when such an identifier is encountered. Will never have a valid location in the source code. */ class MagicVariableDeclaration: public Declaration { public: - enum class VariableKind { THIS, MSG, TX, BLOCK }; - MagicVariableDeclaration(VariableKind _kind, ASTString const& _name, std::shared_ptr const& _type): - Declaration(Location(), std::make_shared(_name)), m_kind(_kind), m_type(_type) {} + MagicVariableDeclaration(ASTString const& _name, std::shared_ptr const& _type): + Declaration(Location(), std::make_shared(_name)), m_type(_type) {} virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST.")); } std::shared_ptr const& getType() const { return m_type; } - VariableKind getKind() const { return m_kind; } private: - VariableKind m_kind; std::shared_ptr m_type; }; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index f5ab829c3..4dc377791 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -160,6 +160,7 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation) bool ExpressionCompiler::visit(FunctionCall& _functionCall) { + using Location = FunctionType::Location; if (_functionCall.isTypeConversion()) { //@todo struct construction @@ -184,7 +185,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) if (asserts(arguments.size() == function.getParameterTypes().size())) BOOST_THROW_EXCEPTION(InternalCompilerError()); - if (function.getLocation() == FunctionType::Location::INTERNAL) + if (function.getLocation() == Location::INTERNAL) { // Calling convention: Caller pushes return address and arguments // Callee removes them and pushes return values @@ -208,29 +209,57 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i) m_context << eth::Instruction::POP; } - else if (function.getLocation() == FunctionType::Location::EXTERNAL) - { + else if (function.getLocation() == Location::EXTERNAL) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet.")); - } else { switch (function.getLocation()) { - case FunctionType::Location::SEND: + case Location::SEND: m_context << u256(0) << u256(0) << u256(0) << u256(0); arguments.front()->accept(*this); //@todo might not be necessary - appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front()); + appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); _functionCall.getExpression().accept(*this); m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB << eth::Instruction::CALL << eth::Instruction::POP; break; - case FunctionType::Location::SUICIDE: + case Location::SUICIDE: arguments.front()->accept(*this); //@todo might not be necessary - appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front()); + appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); m_context << eth::Instruction::SUICIDE; + break; + case Location::SHA3: + arguments.front()->accept(*this); + appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); + // @todo move this once we actually use memory + m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3; + break; + case Location::ECRECOVER: + case Location::SHA256: + case Location::RIPEMD160: + { + static const map contractAddresses{{Location::ECRECOVER, 1}, + {Location::SHA256, 2}, + {Location::RIPEMD160, 3}}; + u256 contractAddress = contractAddresses.find(function.getLocation())->second; + // @todo later, combine this code with external function call + for (unsigned i = 0; i < arguments.size(); ++i) + { + arguments[i]->accept(*this); + appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true); + // @todo move this once we actually use memory + m_context << u256(i * 32) << eth::Instruction::MSTORE; + } + m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0) + << contractAddress << u256(500) //@todo determine actual gas requirement + << eth::Instruction::CALL + << eth::Instruction::POP + << u256(0) << eth::Instruction::MLOAD; + break; + } default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented.")); } @@ -246,9 +275,15 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) { case Type::Category::INTEGER: if (member == "balance") + { + appendTypeConversion(*_memberAccess.getExpression().getType(), + IntegerType(0, IntegerType::Modifier::ADDRESS), true); m_context << eth::Instruction::BALANCE; + } else if (member == "send") - { // no modification + { + appendTypeConversion(*_memberAccess.getExpression().getType(), + IntegerType(0, IntegerType::Modifier::ADDRESS), true); } else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); @@ -319,7 +354,7 @@ void ExpressionCompiler::endVisit(Identifier& _identifier) Declaration* declaration = _identifier.getReferencedDeclaration(); if (MagicVariableDeclaration* magicVar = dynamic_cast(declaration)) { - if (magicVar->getKind() == MagicVariableDeclaration::VariableKind::THIS) + if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this" m_context << eth::Instruction::ADDRESS; return; } diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index 4120dae5e..e958352fd 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -33,15 +33,33 @@ namespace solidity { GlobalContext::GlobalContext(): - m_magicVariables{make_shared(MagicVariableDeclaration::VariableKind::BLOCK, - "block", - make_shared(MagicType::Kind::BLOCK)), - make_shared(MagicVariableDeclaration::VariableKind::MSG, - "msg", - make_shared(MagicType::Kind::MSG)), - make_shared(MagicVariableDeclaration::VariableKind::TX, - "tx", - make_shared(MagicType::Kind::TX))} + m_magicVariables{make_shared("block", make_shared(MagicType::Kind::BLOCK)), + make_shared("msg", make_shared(MagicType::Kind::MSG)), + make_shared("tx", make_shared(MagicType::Kind::TX)), + make_shared("suicide", + make_shared(TypePointers({std::make_shared(0, + IntegerType::Modifier::ADDRESS)}), + TypePointers(), + FunctionType::Location::SUICIDE)), + make_shared("sha3", + make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), + TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), + FunctionType::Location::SHA3)), + make_shared("sha256", + make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), + TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), + FunctionType::Location::SHA256)), + make_shared("ecrecover", + make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH), + std::make_shared(8, IntegerType::Modifier::HASH), + std::make_shared(256, IntegerType::Modifier::HASH), + std::make_shared(256, IntegerType::Modifier::HASH)}), + TypePointers({std::make_shared(0, IntegerType::Modifier::ADDRESS)}), + FunctionType::Location::ECRECOVER)), + make_shared("ripemd160", + make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), + TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}), + FunctionType::Location::RIPEMD160))} { } @@ -64,7 +82,6 @@ MagicVariableDeclaration*GlobalContext::getCurrentThis() const { if (!m_thisPointer[m_currentContract]) m_thisPointer[m_currentContract] = make_shared( - MagicVariableDeclaration::VariableKind::THIS, "this", make_shared(*m_currentContract)); return m_thisPointer[m_currentContract].get(); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 630c9c213..b655f9e0d 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -356,7 +356,7 @@ private: class MagicType: public Type { public: - enum class Kind { BLOCK, MSG, TX }; //@todo should be unified with MagicVariableDeclaration::VariableKind; + enum class Kind { BLOCK, MSG, TX }; virtual Category getCategory() const override { return Category::MAGIC; } MagicType(Kind _kind); diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 80a435d1e..8b25d4031 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -27,12 +27,14 @@ #include #include #include +#include using namespace std; namespace dev { -/// Provider another overload for toBigEndian to encode arguments and return values. +/// Provides additional overloads for toBigEndian to encode arguments and return values. +inline bytes toBigEndian(byte _value) { return bytes({_value}); } inline bytes toBigEndian(bool _value) { return bytes({byte(_value)}); } namespace solidity @@ -809,6 +811,93 @@ BOOST_AUTO_TEST_CASE(send_ether) BOOST_CHECK_EQUAL(m_state.balance(address), amount); } +BOOST_AUTO_TEST_CASE(suicide) +{ + char const* sourceCode = "contract test {\n" + " function a(address receiver) returns (uint ret) {\n" + " suicide(receiver);\n" + " return 10;\n" + " }\n" + "}\n"; + u256 amount(130); + compileAndRun(sourceCode, amount); + u160 address(23); + BOOST_CHECK(callContractFunction(0, address) == bytes()); + BOOST_CHECK(!m_state.addressHasCode(m_contractAddress)); + BOOST_CHECK_EQUAL(m_state.balance(address), amount); +} + +BOOST_AUTO_TEST_CASE(sha3) +{ + char const* sourceCode = "contract test {\n" + " function a(hash input) returns (hash sha3hash) {\n" + " return sha3(input);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + auto f = [&](u256 const& _x) -> u256 + { + return dev::sha3(toBigEndian(_x)); + }; + testSolidityAgainstCpp(0, f, u256(4)); + testSolidityAgainstCpp(0, f, u256(5)); + testSolidityAgainstCpp(0, f, u256(-1)); +} + +BOOST_AUTO_TEST_CASE(sha256) +{ + char const* sourceCode = "contract test {\n" + " function a(hash input) returns (hash sha256hash) {\n" + " return sha256(input);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + auto f = [&](u256 const& _input) -> u256 + { + h256 ret; + dev::sha256(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32)); + return ret; + }; + testSolidityAgainstCpp(0, f, u256(4)); + testSolidityAgainstCpp(0, f, u256(5)); + testSolidityAgainstCpp(0, f, u256(-1)); +} + +BOOST_AUTO_TEST_CASE(ripemd) +{ + char const* sourceCode = "contract test {\n" + " function a(hash input) returns (hash sha256hash) {\n" + " return ripemd160(input);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + auto f = [&](u256 const& _input) -> u256 + { + h256 ret; + dev::ripemd160(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32)); + return u256(ret) >> (256 - 160); + }; + testSolidityAgainstCpp(0, f, u256(4)); + testSolidityAgainstCpp(0, f, u256(5)); + testSolidityAgainstCpp(0, f, u256(-1)); +} + +BOOST_AUTO_TEST_CASE(ecrecover) +{ + char const* sourceCode = "contract test {\n" + " function a(hash h, uint8 v, hash r, hash s) returns (address addr) {\n" + " return ecrecover(h, v, r, s);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + u256 h("0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c"); + byte v = 28; + u256 r("0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f"); + u256 s("0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549"); + u160 addr("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"); + BOOST_CHECK(callContractFunction(0, h, v, r, s) == toBigEndian(addr)); +} + BOOST_AUTO_TEST_SUITE_END() }