Implement address.transfer()

This commit is contained in:
Alex Beregszaszi 2017-02-05 20:21:14 +00:00
parent 0177d964b1
commit 4264625c69
4 changed files with 20 additions and 5 deletions

View File

@ -2,6 +2,7 @@
Features: Features:
* Add ``assert(condition)``, which throws if condition is false. * Add ``assert(condition)``, which throws if condition is false.
* Introduce ``.transfer(value)`` for sending Ether.
* Code generator: Support ``revert()`` to abort with rolling back, but not consuming all gas. * Code generator: Support ``revert()`` to abort with rolling back, but not consuming all gas.
* Inline assembly: Support ``revert`` (EIP140) as an opcode. * Inline assembly: Support ``revert`` (EIP140) as an opcode.
* Type system: Support explicit conversion of external function to address. * Type system: Support explicit conversion of external function to address.

View File

@ -464,7 +464,8 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
{"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true, false, true)}, {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true, false, true)},
{"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true, false, true)}, {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true, false, true)},
{"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)}, {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)} {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)},
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Location::Transfer)}
}; };
else else
return MemberList::MemberMap(); return MemberList::MemberMap();
@ -2097,6 +2098,7 @@ string FunctionType::identifier() const
case Location::BareDelegateCall: id += "baredelegatecall"; break; case Location::BareDelegateCall: id += "baredelegatecall"; break;
case Location::Creation: id += "creation"; break; case Location::Creation: id += "creation"; break;
case Location::Send: id += "send"; break; case Location::Send: id += "send"; break;
case Location::Transfer: id += "transfer"; break;
case Location::SHA3: id += "sha3"; break; case Location::SHA3: id += "sha3"; break;
case Location::Selfdestruct: id += "selfdestruct"; break; case Location::Selfdestruct: id += "selfdestruct"; break;
case Location::Revert: id += "revert"; break; case Location::Revert: id += "revert"; break;

View File

@ -826,6 +826,7 @@ public:
BareDelegateCall, ///< DELEGATECALL without function hash BareDelegateCall, ///< DELEGATECALL without function hash
Creation, ///< external call using CREATE Creation, ///< external call using CREATE
Send, ///< CALL, but without data and gas Send, ///< CALL, but without data and gas
Transfer, ///< CALL, but without data and throws on error
SHA3, ///< SHA3 SHA3, ///< SHA3
Selfdestruct, ///< SELFDESTRUCT Selfdestruct, ///< SELFDESTRUCT
Revert, ///< REVERT Revert, ///< REVERT

View File

@ -616,6 +616,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments.front()->accept(*this); arguments.front()->accept(*this);
break; break;
case Location::Send: case Location::Send:
case Location::Transfer:
_functionCall.expression().accept(*this); _functionCall.expression().accept(*this);
// Provide the gas stipend manually at first because we may send zero ether. // Provide the gas stipend manually at first because we may send zero ether.
// Will be zeroed if we send more than zero ether. // Will be zeroed if we send more than zero ether.
@ -625,9 +626,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
*arguments.front()->annotation().type, *arguments.front()->annotation().type,
*function.parameterTypes().front(), true *function.parameterTypes().front(), true
); );
// gas <- gas * !value if (function.location() != Location::Transfer)
m_context << Instruction::SWAP1 << Instruction::DUP2; {
m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1; // gas <- gas * !value
m_context << Instruction::SWAP1 << Instruction::DUP2;
m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1;
}
appendExternalFunctionCall( appendExternalFunctionCall(
FunctionType( FunctionType(
TypePointers{}, TypePointers{},
@ -644,6 +648,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
), ),
{} {}
); );
if (function.location() == Location::Transfer)
{
// Check if zero (out of stack or not enough balance).
m_context << Instruction::ISZERO;
m_context.appendConditionalInvalid();
}
break; break;
case Location::Selfdestruct: case Location::Selfdestruct:
arguments.front()->accept(*this); arguments.front()->accept(*this);
@ -960,6 +970,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
case FunctionType::Location::Bare: case FunctionType::Location::Bare:
case FunctionType::Location::BareCallCode: case FunctionType::Location::BareCallCode:
case FunctionType::Location::BareDelegateCall: case FunctionType::Location::BareDelegateCall:
case FunctionType::Location::Transfer:
_memberAccess.expression().accept(*this); _memberAccess.expression().accept(*this);
m_context << funType->externalIdentifier(); m_context << funType->externalIdentifier();
break; break;
@ -1041,7 +1052,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
); );
m_context << Instruction::BALANCE; m_context << Instruction::BALANCE;
} }
else if ((set<string>{"send", "call", "callcode", "delegatecall"}).count(member)) else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member))
utils().convertType( utils().convertType(
*_memberAccess.expression().annotation().type, *_memberAccess.expression().annotation().type,
IntegerType(0, IntegerType::Modifier::Address), IntegerType(0, IntegerType::Modifier::Address),