diff options
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 14 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 13 |
2 files changed, 25 insertions, 2 deletions
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 9d77ccdc..33571bc0 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -242,6 +242,12 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac m_context << notFound; if (fallback) { + if (!fallback->isPayable()) + { + // Throw if function is not payable but call contained ether. + m_context << Instruction::CALLVALUE; + m_context.appendConditionalJumpTo(m_context.errorTag()); + } eth::AssemblyItem returnTag = m_context.pushNewTag(); fallback->accept(*this); m_context << returnTag; @@ -255,7 +261,15 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac FunctionTypePointer const& functionType = it.second; solAssert(functionType->hasDeclaration(), ""); CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration()); + m_context << callDataUnpackerEntryPoints.at(it.first); + if (!functionType->isPayable()) + { + // Throw if function is not payable but call contained ether. + m_context << Instruction::CALLVALUE; + m_context.appendConditionalJumpTo(m_context.errorTag()); + } + eth::AssemblyItem returnTag = m_context.pushNewTag(); m_context << CompilerUtils::dataStartOffset; appendCalldataUnpacker(functionType->parameterTypes()); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 4a81e27d..96ca4296 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -568,12 +568,17 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) break; case Location::Send: _functionCall.expression().accept(*this); - m_context << u256(0); // do not send gas (there still is the stipend) + // Provide the gas stipend manually at first because we may send zero ether. + // Will be zeroed if we send more than zero ether. + m_context << u256(eth::GasCosts::callStipend); arguments.front()->accept(*this); utils().convertType( *arguments.front()->annotation().type, *function.parameterTypes().front(), true ); + // gas <- gas * !value + m_context << Instruction::SWAP1 << Instruction::DUP2; + m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1; appendExternalFunctionCall( FunctionType( TypePointers{}, @@ -583,6 +588,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) Location::Bare, false, nullptr, + false, + false, true, true ), @@ -1524,11 +1531,13 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context << u256(0); m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos)); + bool existenceChecked = false; // Check the the target contract exists (has code) for non-low-level calls. if (funKind == FunctionKind::External || funKind == FunctionKind::CallCode || funKind == FunctionKind::DelegateCall) { m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; m_context.appendConditionalJumpTo(m_context.errorTag()); + existenceChecked = true; } if (_functionType.gasSet()) @@ -1540,7 +1549,7 @@ void ExpressionCompiler::appendExternalFunctionCall( u256 gasNeededByCaller = eth::GasCosts::callGas + 10; if (_functionType.valueSet()) gasNeededByCaller += eth::GasCosts::callValueTransferGas; - if (!isCallCode && !isDelegateCall) + if (!isCallCode && !isDelegateCall && !existenceChecked) gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know m_context << gasNeededByCaller << |