mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Modify gas and value for external function call.
This commit is contained in:
parent
80eec8b308
commit
ec022783c4
@ -232,27 +232,41 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
case Location::EXTERNAL:
|
case Location::EXTERNAL:
|
||||||
case Location::BARE:
|
case Location::BARE:
|
||||||
|
_functionCall.getExpression().accept(*this);
|
||||||
|
appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE);
|
||||||
|
break;
|
||||||
|
case Location::SET_GAS:
|
||||||
{
|
{
|
||||||
FunctionCallOptions options;
|
// stack layout: contract_address function_id [gas] [value]
|
||||||
options.bare = function.getLocation() == Location::BARE;
|
_functionCall.getExpression().accept(*this);
|
||||||
options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
|
arguments.front()->accept(*this);
|
||||||
appendExternalFunctionCall(function, arguments, options);
|
appendTypeConversion(*arguments.front()->getType(), IntegerType(256), true);
|
||||||
|
// Note that function is not the original function, but the ".gas" function.
|
||||||
|
// Its values of gasSet and valueSet is equal to the original function's though.
|
||||||
|
unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0);
|
||||||
|
if (stackDepth > 0)
|
||||||
|
m_context << eth::swapInstruction(stackDepth);
|
||||||
|
if (function.gasSet())
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Location::SET_VALUE:
|
||||||
|
// stack layout: contract_address function_id [gas] [value]
|
||||||
|
_functionCall.getExpression().accept(*this);
|
||||||
|
// Note that function is not the original function, but the ".value" function.
|
||||||
|
// Its values of gasSet and valueSet is equal to the original function's though.
|
||||||
|
if (function.valueSet())
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
|
arguments.front()->accept(*this);
|
||||||
|
break;
|
||||||
case Location::SEND:
|
case Location::SEND:
|
||||||
{
|
// TODO set gas to min
|
||||||
FunctionCallOptions options;
|
_functionCall.getExpression().accept(*this);
|
||||||
options.bare = true;
|
arguments.front()->accept(*this);
|
||||||
options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
|
appendTypeConversion(*arguments.front()->getType(),
|
||||||
options.obtainValue = [&]()
|
*function.getParameterTypes().front(), true);
|
||||||
{
|
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, Location::EXTERNAL, false, true), {}, true);
|
||||||
arguments.front()->accept(*this);
|
|
||||||
appendTypeConversion(*arguments.front()->getType(),
|
|
||||||
*function.getParameterTypes().front(), true);
|
|
||||||
};
|
|
||||||
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, Location::EXTERNAL), {}, options);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Location::SUICIDE:
|
case Location::SUICIDE:
|
||||||
arguments.front()->accept(*this);
|
arguments.front()->accept(*this);
|
||||||
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
|
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
|
||||||
@ -289,11 +303,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
|
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
|
||||||
{Location::SHA256, 2},
|
{Location::SHA256, 2},
|
||||||
{Location::RIPEMD160, 3}};
|
{Location::RIPEMD160, 3}};
|
||||||
u256 contractAddress = contractAddresses.find(function.getLocation())->second;
|
m_context << contractAddresses.find(function.getLocation())->second;
|
||||||
FunctionCallOptions options;
|
appendExternalFunctionCall(function, arguments, true);
|
||||||
options.bare = true;
|
|
||||||
options.obtainAddress = [&]() { m_context << contractAddress; };
|
|
||||||
appendExternalFunctionCall(function, arguments, options);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -370,6 +381,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
else
|
else
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
|
||||||
break;
|
break;
|
||||||
|
case Type::Category::FUNCTION:
|
||||||
|
solAssert(!!_memberAccess.getExpression().getType()->getMemberType(member),
|
||||||
|
"Invalid member access to function.");
|
||||||
|
break;
|
||||||
case Type::Category::MAGIC:
|
case Type::Category::MAGIC:
|
||||||
// we can ignore the kind of magic and only look at the name of the member
|
// we can ignore the kind of magic and only look at the name of the member
|
||||||
if (member == "coinbase")
|
if (member == "coinbase")
|
||||||
@ -646,15 +661,25 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
|
|||||||
|
|
||||||
void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType,
|
void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType,
|
||||||
vector<ASTPointer<Expression const>> const& _arguments,
|
vector<ASTPointer<Expression const>> const& _arguments,
|
||||||
FunctionCallOptions const& _options)
|
bool bare)
|
||||||
{
|
{
|
||||||
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
|
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
|
||||||
|
|
||||||
_options.obtainAddress();
|
// Assumed stack content here:
|
||||||
if (!_options.bare)
|
// <stack top>
|
||||||
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
|
// value [if _functionType.valueSet()]
|
||||||
|
// gas [if _functionType.gasSet()]
|
||||||
|
// function identifier [unless options.bare]
|
||||||
|
// contract address
|
||||||
|
|
||||||
unsigned dataOffset = _options.bare ? 0 : CompilerUtils::dataStartOffset; // reserve 4 bytes for the function's hash identifier
|
unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0);
|
||||||
|
if (!bare)
|
||||||
|
{
|
||||||
|
m_context << eth::dupInstruction(gasValueSize + 1);
|
||||||
|
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset; // reserve 4 bytes for the function's hash identifier
|
||||||
for (unsigned i = 0; i < _arguments.size(); ++i)
|
for (unsigned i = 0; i < _arguments.size(); ++i)
|
||||||
{
|
{
|
||||||
_arguments[i]->accept(*this);
|
_arguments[i]->accept(*this);
|
||||||
@ -676,16 +701,25 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
|
|||||||
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
|
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
|
||||||
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
|
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
|
||||||
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
|
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
|
||||||
if (_options.obtainValue)
|
if (_functionType.valueSet())
|
||||||
_options.obtainValue();
|
m_context << eth::dupInstruction(5);
|
||||||
else
|
else
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
m_context << eth::dupInstruction(6); //copy contract address
|
m_context << eth::dupInstruction(6 + gasValueSize + (bare ? 0 : 1)); //copy contract address
|
||||||
|
|
||||||
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
|
if (_functionType.gasSet())
|
||||||
<< eth::Instruction::CALL
|
m_context << eth::dupInstruction(7 + (_functionType.valueSet() ? 1 : 0));
|
||||||
<< eth::Instruction::POP // @todo do not ignore failure indicator
|
else
|
||||||
<< eth::Instruction::POP; // pop contract address
|
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB;
|
||||||
|
m_context << eth::Instruction::CALL
|
||||||
|
<< eth::Instruction::POP; // @todo do not ignore failure indicator
|
||||||
|
if (_functionType.valueSet())
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
|
if (_functionType.gasSet())
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
|
if (!bare)
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
|
m_context << eth::Instruction::POP; // pop contract address
|
||||||
|
|
||||||
if (retSize > 0)
|
if (retSize > 0)
|
||||||
{
|
{
|
||||||
|
@ -87,21 +87,9 @@ private:
|
|||||||
//// Appends code that cleans higher-order bits for integer types.
|
//// Appends code that cleans higher-order bits for integer types.
|
||||||
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
|
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
|
||||||
|
|
||||||
/// Additional options used in appendExternalFunctionCall.
|
|
||||||
struct FunctionCallOptions
|
|
||||||
{
|
|
||||||
FunctionCallOptions() {}
|
|
||||||
/// Invoked to copy the address to the stack
|
|
||||||
std::function<void()> obtainAddress;
|
|
||||||
/// Invoked to copy the ethe value to the stack (if not specified, value is 0).
|
|
||||||
std::function<void()> obtainValue;
|
|
||||||
/// If true, do not prepend function index to call data
|
|
||||||
bool bare = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Appends code to call a function of the given type with the given arguments.
|
/// Appends code to call a function of the given type with the given arguments.
|
||||||
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
|
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
|
||||||
FunctionCallOptions const& _options = FunctionCallOptions());
|
bool bare = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to store and retrieve lvalues to and from various locations.
|
* Helper class to store and retrieve lvalues to and from various locations.
|
||||||
|
60
Types.cpp
60
Types.cpp
@ -561,6 +561,21 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
|
|||||||
m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL;
|
m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FunctionType::FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
|
||||||
|
FunctionType::Location _location, bool _gasSet, bool _valueSet):
|
||||||
|
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
|
||||||
|
m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet)
|
||||||
|
{
|
||||||
|
if (m_location == Location::EXTERNAL)
|
||||||
|
m_sizeOnStack = 2;
|
||||||
|
else if (m_location == Location::INTERNAL || m_location == Location::BARE)
|
||||||
|
m_sizeOnStack = 1;
|
||||||
|
if (m_gasSet)
|
||||||
|
m_sizeOnStack++;
|
||||||
|
if (m_valueSet)
|
||||||
|
m_sizeOnStack++;
|
||||||
|
}
|
||||||
|
|
||||||
bool FunctionType::operator==(Type const& _other) const
|
bool FunctionType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.getCategory() != getCategory())
|
if (_other.getCategory() != getCategory())
|
||||||
@ -580,6 +595,9 @@ bool FunctionType::operator==(Type const& _other) const
|
|||||||
if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(),
|
if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(),
|
||||||
other.m_returnParameterTypes.cbegin(), typeCompare))
|
other.m_returnParameterTypes.cbegin(), typeCompare))
|
||||||
return false;
|
return false;
|
||||||
|
//@todo this is ugly, but cannot be prevented right now
|
||||||
|
if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
|
||||||
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,17 +613,42 @@ string FunctionType::toString() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned FunctionType::getSizeOnStack() const
|
unsigned FunctionType::getSizeOnStack() const
|
||||||
|
{
|
||||||
|
unsigned size = 0;
|
||||||
|
if (m_location == Location::EXTERNAL)
|
||||||
|
size = 2;
|
||||||
|
else if (m_location == Location::INTERNAL || m_location == Location::BARE)
|
||||||
|
size = 1;
|
||||||
|
if (m_gasSet)
|
||||||
|
size++;
|
||||||
|
if (m_valueSet)
|
||||||
|
size++;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemberList const& FunctionType::getMembers() const
|
||||||
{
|
{
|
||||||
switch (m_location)
|
switch (m_location)
|
||||||
{
|
{
|
||||||
case Location::INTERNAL:
|
|
||||||
return 1;
|
|
||||||
case Location::EXTERNAL:
|
case Location::EXTERNAL:
|
||||||
return 2;
|
case Location::ECRECOVER:
|
||||||
|
case Location::SHA256:
|
||||||
|
case Location::RIPEMD160:
|
||||||
case Location::BARE:
|
case Location::BARE:
|
||||||
return 1;
|
if (!m_members)
|
||||||
|
{
|
||||||
|
map<string, TypePointer> members{
|
||||||
|
{"gas", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
|
||||||
|
TypePointers{copyAndSetGasOrValue(true, false)},
|
||||||
|
Location::SET_GAS, m_gasSet, m_valueSet)},
|
||||||
|
{"value", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
|
||||||
|
TypePointers{copyAndSetGasOrValue(false, true)},
|
||||||
|
Location::SET_VALUE, m_gasSet, m_valueSet)}};
|
||||||
|
m_members.reset(new MemberList(members));
|
||||||
|
}
|
||||||
|
return *m_members;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return EmptyMemberList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,6 +671,13 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
|
|||||||
return pointers;
|
return pointers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const
|
||||||
|
{
|
||||||
|
return make_shared<FunctionType>(m_parameterTypes, m_returnParameterTypes, m_location,
|
||||||
|
m_gasSet || _setGas, m_valueSet || _setValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool MappingType::operator==(Type const& _other) const
|
bool MappingType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.getCategory() != getCategory())
|
if (_other.getCategory() != getCategory())
|
||||||
|
26
Types.h
26
Types.h
@ -345,10 +345,15 @@ class FunctionType: public Type
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// The meaning of the value(s) on the stack referencing the function:
|
/// The meaning of the value(s) on the stack referencing the function:
|
||||||
/// INTERNAL: jump tag, EXTERNAL: contract address + function index,
|
/// INTERNAL: jump tag, EXTERNAL: contract address + function identifier,
|
||||||
/// BARE: contract address (non-abi contract call)
|
/// BARE: contract address (non-abi contract call)
|
||||||
/// OTHERS: special virtual function, nothing on the stack
|
/// OTHERS: special virtual function, nothing on the stack
|
||||||
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, LOG0, LOG1, LOG2, LOG3, LOG4, BARE };
|
enum class Location { INTERNAL, EXTERNAL, SEND,
|
||||||
|
SHA3, SUICIDE,
|
||||||
|
ECRECOVER, SHA256, RIPEMD160,
|
||||||
|
LOG0, LOG1, LOG2, LOG3, LOG4,
|
||||||
|
SET_GAS, SET_VALUE,
|
||||||
|
BARE };
|
||||||
|
|
||||||
virtual Category getCategory() const override { return Category::FUNCTION; }
|
virtual Category getCategory() const override { return Category::FUNCTION; }
|
||||||
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
|
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
|
||||||
@ -357,9 +362,8 @@ public:
|
|||||||
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
|
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
|
||||||
_location) {}
|
_location) {}
|
||||||
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
|
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
|
||||||
Location _location = Location::INTERNAL):
|
Location _location = Location::INTERNAL,
|
||||||
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
|
bool _gasSet = false, bool _valueSet = false);
|
||||||
m_location(_location) {}
|
|
||||||
|
|
||||||
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
|
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
|
||||||
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
|
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
|
||||||
@ -370,16 +374,28 @@ public:
|
|||||||
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
|
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
virtual bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned getSizeOnStack() const override;
|
virtual unsigned getSizeOnStack() const override;
|
||||||
|
virtual MemberList const& getMembers() const override;
|
||||||
|
|
||||||
Location const& getLocation() const { return m_location; }
|
Location const& getLocation() const { return m_location; }
|
||||||
std::string getCanonicalSignature() const;
|
std::string getCanonicalSignature() const;
|
||||||
|
|
||||||
|
bool gasSet() const { return m_gasSet; }
|
||||||
|
bool valueSet() const { return m_valueSet; }
|
||||||
|
|
||||||
|
/// @returns a copy of this type, where gas or value are set manually. This will never set one
|
||||||
|
/// of the parameters to fals.
|
||||||
|
TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static TypePointers parseElementaryTypeVector(strings const& _types);
|
static TypePointers parseElementaryTypeVector(strings const& _types);
|
||||||
|
|
||||||
TypePointers m_parameterTypes;
|
TypePointers m_parameterTypes;
|
||||||
TypePointers m_returnParameterTypes;
|
TypePointers m_returnParameterTypes;
|
||||||
Location m_location;
|
Location m_location;
|
||||||
|
unsigned m_sizeOnStack = 0;
|
||||||
|
bool m_gasSet = false; ///< true iff the gas value to be used is on the stack
|
||||||
|
bool m_valueSet = false; ///< true iff the value to be sent is on the stack
|
||||||
|
mutable std::unique_ptr<MemberList> m_members;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user