diff options
-rw-r--r-- | libsolidity/ast/Types.cpp | 47 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 4 | ||||
-rw-r--r-- | libsolidity/codegen/Compiler.cpp | 10 | ||||
-rw-r--r-- | libsolidity/codegen/Compiler.h | 2 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 29 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.h | 2 | ||||
-rw-r--r-- | solc/jsonCompiler.cpp | 2 | ||||
-rw-r--r-- | test/libsolidity/GasMeter.cpp | 2 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 56 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 13 |
10 files changed, 122 insertions, 45 deletions
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index bca83d59..0696b908 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -333,6 +333,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons {"balance", make_shared<IntegerType >(256)}, {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)}, {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)}, + {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)}, {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)} }; else @@ -1561,9 +1562,9 @@ unsigned FunctionType::sizeOnStack() const } unsigned size = 0; - if (location == Location::External || location == Location::CallCode) + if (location == Location::External || location == Location::CallCode || location == Location::DelegateCall) size = 2; - else if (location == Location::Bare || location == Location::BareCallCode) + else if (location == Location::Bare || location == Location::BareCallCode || location == Location::BareDelegateCall) size = 1; else if (location == Location::Internal) size = 1; @@ -1619,9 +1620,11 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con case Location::RIPEMD160: case Location::Bare: case Location::BareCallCode: + case Location::BareDelegateCall: { - MemberList::MemberMap members{ - { + MemberList::MemberMap members; + if (m_location != Location::BareDelegateCall) + members.push_back(MemberList::Member( "value", make_shared<FunctionType>( parseElementaryTypeVector({"uint"}), @@ -1634,25 +1637,22 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con m_gasSet, m_valueSet ) - } - }; + )); if (m_location != Location::Creation) - members.push_back( - MemberList::Member( - "gas", - make_shared<FunctionType>( - parseElementaryTypeVector({"uint"}), - TypePointers{copyAndSetGasOrValue(true, false)}, - strings(), - strings(), - Location::SetGas, - false, - nullptr, - m_gasSet, - m_valueSet - ) + members.push_back(MemberList::Member( + "gas", + make_shared<FunctionType>( + parseElementaryTypeVector({"uint"}), + TypePointers{copyAndSetGasOrValue(true, false)}, + strings(), + strings(), + Location::SetGas, + false, + nullptr, + m_gasSet, + m_valueSet ) - ); + )); return members; } default: @@ -1700,6 +1700,7 @@ bool FunctionType::isBareCall() const { case Location::Bare: case Location::BareCallCode: + case Location::BareDelegateCall: case Location::ECRecover: case Location::SHA256: case Location::RIPEMD160: @@ -1785,7 +1786,7 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) returnParameterTypes, m_parameterNames, returnParameterNames, - _inLibrary ? Location::CallCode : m_location, + _inLibrary ? Location::DelegateCall : m_location, m_arbitraryParameters, m_declaration, m_gasSet, @@ -1884,7 +1885,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current for (auto const& it: contract.interfaceFunctions()) members.push_back(MemberList::Member( it.second->declaration().name(), - it.second->asMemberFunction(true), // use callcode + it.second->asMemberFunction(true), &it.second->declaration() )); if (isBase) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index b4a2d573..189dd10a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -732,8 +732,10 @@ public: 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 Bare, ///< CALL without function hash BareCallCode, ///< CALLCODE without function hash + BareDelegateCall, ///< DELEGATECALL without function hash Creation, ///< external call using CREATE Send, ///< CALL, but without data and gas SHA3, ///< SHA3 @@ -869,7 +871,7 @@ public: /// removed and the location of reference types is changed from CallData to Memory. /// This is needed if external functions are called on other contracts, as they cannot return /// dynamic values. - /// @param _inLibrary if true, uses CallCode as location. + /// @param _inLibrary if true, uses DelegateCall as location. /// @param _bound if true, the argumenst are placed as `arg1.functionName(arg2, ..., argn)`. FunctionTypePointer asMemberFunction(bool _inLibrary, bool _bound = false) const; diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index 18803b71..c7eb71a8 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -773,15 +773,13 @@ eth::Assembly Compiler::cloneRuntime() a << u256(0) << eth::Instruction::DUP1 << eth::Instruction::CALLDATACOPY; //@todo adjust for larger return values, make this dynamic. a << u256(0x20) << u256(0) << eth::Instruction::CALLDATASIZE; - // unfortunately, we have to send the value again, so that CALLVALUE returns the correct value - // in the callcoded contract. - a << u256(0) << eth::Instruction::CALLVALUE; + a << u256(0); // this is the address which has to be substituted by the linker. //@todo implement as special "marker" AssemblyItem. a << u256("0xcafecafecafecafecafecafecafecafecafecafe"); - a << u256(schedule.callGas + schedule.callValueTransferGas + 10) << eth::Instruction::GAS << eth::Instruction::SUB; - a << eth::Instruction::CALLCODE; - //Propagate error condition (if CALLCODE pushes 0 on stack). + a << u256(schedule.callGas + 10) << eth::Instruction::GAS << eth::Instruction::SUB; + a << eth::Instruction::DELEGATECALL; + //Propagate error condition (if DELEGATECALL pushes 0 on stack). a << eth::Instruction::ISZERO; a.appendJumpI(a.errorTag()); //@todo adjust for larger return values, make this dynamic. diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 9d069f7c..fa33bd30 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -44,7 +44,7 @@ public: ContractDefinition const& _contract, std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts ); - /// Compiles a contract that uses CALLCODE to call into a pre-deployed version of the given + /// Compiles a contract that uses DELEGATECALL to call into a pre-deployed version of the given /// contract at runtime, but contains the full creation-time code. void compileClone( ContractDefinition const& _contract, diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 58db07b1..e0b2b5f6 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -465,8 +465,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { FunctionType const& function = *functionType; if (function.bound()) - // Only callcode functions can be bound, this might be lifted later. - solAssert(function.location() == Location::CallCode, ""); + // Only delegatecall functions can be bound, this might be lifted later. + solAssert(function.location() == Location::DelegateCall, ""); switch (function.location()) { case Location::Internal: @@ -492,8 +492,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } case Location::External: case Location::CallCode: + case Location::DelegateCall: case Location::Bare: case Location::BareCallCode: + case Location::BareDelegateCall: _functionCall.expression().accept(*this); appendExternalFunctionCall(function, arguments); break; @@ -875,7 +877,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) ); m_context << eth::Instruction::BALANCE; } - else if ((set<string>{"send", "call", "callcode"}).count(member)) + else if ((set<string>{"send", "call", "callcode", "delegatecall"}).count(member)) utils().convertType( *_memberAccess.expression().annotation().type, IntegerType(0, IntegerType::Modifier::Address), @@ -1356,6 +1358,7 @@ void ExpressionCompiler::appendExternalFunctionCall( FunctionKind funKind = _functionType.location(); bool returnSuccessCondition = funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode; bool isCallCode = funKind == FunctionKind::BareCallCode || funKind == FunctionKind::CallCode; + bool isDelegateCall = funKind == FunctionKind::BareDelegateCall || funKind == FunctionKind::DelegateCall; unsigned retSize = 0; if (returnSuccessCondition) @@ -1371,13 +1374,13 @@ void ExpressionCompiler::appendExternalFunctionCall( TypePointers argumentTypes; TypePointers parameterTypes = _functionType.parameterTypes(); bool manualFunctionId = - (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) && + (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode || funKind == FunctionKind::BareDelegateCall) && !_arguments.empty() && _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) == CompilerUtils::dataStartOffset; if (manualFunctionId) { - // If we have a BareCall or BareCallCode and the first type has exactly 4 bytes, use it as + // If we have a Bare* and the first type has exactly 4 bytes, use it as // function identifier. _arguments.front()->accept(*this); utils().convertType( @@ -1416,7 +1419,7 @@ void ExpressionCompiler::appendExternalFunctionCall( parameterTypes, _functionType.padArguments(), _functionType.takesArbitraryParameters(), - isCallCode + isCallCode || isDelegateCall ); // Stack now: @@ -1435,8 +1438,10 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context << eth::Instruction::DUP2; // CALL arguments: outSize, outOff, inSize, inOff (already present up to here) - // value, addr, gas (stack top) - if (_functionType.valueSet()) + // [value,] addr, gas (stack top) + if (isDelegateCall) + solAssert(!_functionType.valueSet(), "Value set for delegatecall"); + else if (_functionType.valueSet()) m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos)); else m_context << u256(0); @@ -1446,20 +1451,22 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); else { - eth::EVMSchedule schedule;// TODO: Make relevant to current suppose context. + eth::EVMSchedule schedule; // send all gas except the amount needed to execute "SUB" and "CALL" // @todo this retains too much gas for now, needs to be fine-tuned. u256 gasNeededByCaller = schedule.callGas + 10; if (_functionType.valueSet()) gasNeededByCaller += schedule.callValueTransferGas; - if (!isCallCode) + if (!isCallCode && !isDelegateCall) gasNeededByCaller += schedule.callNewAccountGas; // we never know m_context << gasNeededByCaller << eth::Instruction::GAS << eth::Instruction::SUB; } - if (isCallCode) + if (isDelegateCall) + m_context << eth::Instruction::DELEGATECALL; + else if (isCallCode) m_context << eth::Instruction::CALLCODE; else m_context << eth::Instruction::CALL; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 517d0055..1d13a23c 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -124,7 +124,7 @@ public: eth::LinkerObject const& object(std::string const& _contractName = "") const; /// @returns the runtime object for the contract. eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const; - /// @returns the bytecode of a contract that uses an already deployed contract via CALLCODE. + /// @returns the bytecode of a contract that uses an already deployed contract via DELEGATECALL. /// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to /// substituted by the actual address. Note that this sequence starts end ends in three X /// characters but can contain anything in between. diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index 987f8dff..b5efa94d 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -72,7 +72,7 @@ Json::Value gasToJson(GasEstimator::GasConsumption const& _gas) Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract) { - eth::EVMSchedule schedule;// TODO: make relevant to supposed context. + eth::EVMSchedule schedule; Json::Value gasEstimates(Json::objectValue); using Gas = GasEstimator::GasConsumption; if (!_compiler.assemblyItems(_contract) && !_compiler.runtimeAssemblyItems(_contract)) diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 25df9e4d..9f947af3 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -59,7 +59,7 @@ public: void testCreationTimeGas(string const& _sourceCode) { - EVMSchedule schedule;// TODO: make relevant to supposed context. + EVMSchedule schedule; compileAndRun(_sourceCode); auto state = make_shared<KnownState>(); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index b54393e4..388f5a69 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2890,6 +2890,62 @@ BOOST_AUTO_TEST_CASE(generic_callcode) BOOST_CHECK_EQUAL(m_state.balance(c_senderAddress), 50); } +BOOST_AUTO_TEST_CASE(generic_delegatecall) +{ + char const* sourceCode = R"**( + contract receiver { + uint public received; + address public sender; + uint public value; + function receive(uint256 x) { received = x; sender = msg.sender; value = msg.value; } + } + contract sender { + uint public received; + address public sender; + uint public value; + function doSend(address rec) + { + bytes4 signature = bytes4(bytes32(sha3("receive(uint256)"))); + rec.delegatecall(signature, 23); + } + } + )**"; + compileAndRun(sourceCode, 0, "receiver"); + u160 const c_receiverAddress = m_contractAddress; + compileAndRun(sourceCode, 50, "sender"); + u160 const c_senderAddress = m_contractAddress; + BOOST_CHECK(m_sender != c_senderAddress); // just for sanity + BOOST_CHECK(callContractFunctionWithValue("doSend(address)", 11, c_receiverAddress) == encodeArgs()); + BOOST_CHECK(callContractFunction("received()") == encodeArgs(u256(23))); + BOOST_CHECK(callContractFunction("sender()") == encodeArgs(u160(m_sender))); + BOOST_CHECK(callContractFunction("value()") == encodeArgs(u256(11))); + m_contractAddress = c_receiverAddress; + BOOST_CHECK(callContractFunction("received()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("sender()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("value()") == encodeArgs(u256(0))); + BOOST_CHECK(m_state.storage(c_receiverAddress).empty()); + BOOST_CHECK(!m_state.storage(c_senderAddress).empty()); + BOOST_CHECK_EQUAL(m_state.balance(c_receiverAddress), 0); + BOOST_CHECK_EQUAL(m_state.balance(c_senderAddress), 50 + 11); +} + +BOOST_AUTO_TEST_CASE(library_call_in_homestead) +{ + char const* sourceCode = R"( + library Lib { function m() returns (address) { return msg.sender; } } + contract Test { + address public sender; + function f() { + sender = Lib.m(); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); + BOOST_CHECK(callContractFunction("f()") == encodeArgs()); + BOOST_CHECK(callContractFunction("sender()") == encodeArgs(u160(m_sender))); +} + BOOST_AUTO_TEST_CASE(store_bytes) { // this test just checks that the copy loop does not mess up the stack diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 5f6c0535..b2e46b7d 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3232,6 +3232,19 @@ BOOST_AUTO_TEST_CASE(int10abc_is_identifier) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value) +{ + char const* text = R"( + library L { function l() {} } + contract test { + function f() { + L.l.value; + } + } + )"; + BOOST_CHECK(!success(text)); +} + BOOST_AUTO_TEST_SUITE_END() } |