Introduce address(...).code

This commit is contained in:
Alex Beregszaszi 2020-09-24 17:28:14 +01:00
parent 4b410b8731
commit 7b347b9ec2
10 changed files with 90 additions and 2 deletions

View File

@ -19,6 +19,7 @@ Breaking Changes:
* Type System: Disallow ``type(super)``.
* Type System: Disallow enums with more than 256 members.
* Type System: Disallow explicit conversions from negative literals and literals larger than ``type(uint160).max`` to ``address`` type.
* Type System: Introduce ``address(...).code`` to retrieve the code as ``bytes memory``. The size can be obtained via ``address(...).code.length``, but it will currently always include copying the code.
* Type System: Explicit conversions between two types are disallowed if it changes more than one of sign, width or kind at the same time.
* Type System: Explicit conversions from literals to enums are only allowed if the value fits in the enum.
* Type System: Explicit conversions from literals to integer type is as strict as implicit conversions.

View File

@ -115,6 +115,7 @@ Global Variables
- ``super``: the contract one level higher in the inheritance hierarchy
- ``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given address
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
- ``<address>.code`` (``bytes memory``): code at the :ref:`address` (can be empty)
- ``<address>.codehash`` (``bytes32``): the codehash of the :ref:`address`
- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`,
returns ``false`` on failure

View File

@ -222,6 +222,9 @@ Members of Address Types
``<address>.balance`` (``uint256``)
balance of the :ref:`address` in Wei
``<address>.code`` (``bytes memory``)
code at the :ref:`address` (can be empty)
``<address>.codehash`` (``bytes32``)
the codehash of the :ref:`address`

View File

@ -356,7 +356,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
switch (_memberAccess.expression().annotation().type->category())
{
case Type::Category::Address:
if (member == "balance" || member == "codehash")
if (member == "balance" || member == "code" || member == "codehash")
mutability = StateMutability::View;
break;
case Type::Category::Magic:

View File

@ -456,6 +456,7 @@ MemberList::MemberMap AddressType::nativeMembers(ASTNode const*) const
{
MemberList::MemberMap members = {
{"balance", TypeProvider::uint256()},
{"code", TypeProvider::array(DataLocation::Memory)},
{"codehash", TypeProvider::fixedBytes(32)},
{"call", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)},
{"callcode", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},

View File

@ -1508,6 +1508,39 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
);
m_context << Instruction::BALANCE;
}
else if (member == "code")
{
// Stack: <address>
utils().convertType(
*_memberAccess.expression().annotation().type,
*TypeProvider::address(),
true
);
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE;
// Stack post: <address> <size>
m_context << Instruction::DUP1;
// Account for the size field of `bytes memory`
m_context << u256(32) << Instruction::ADD;
utils().allocateMemory();
// Stack post: <address> <size> <mem_offset>
// Store size at mem_offset
m_context << Instruction::DUP2 << Instruction::DUP2 << Instruction::MSTORE;
m_context << u256(0) << Instruction::SWAP1 << Instruction::DUP1;
// Stack post: <address> <size> 0 <mem_offset> <mem_offset>
m_context << u256(32) << Instruction::ADD << Instruction::SWAP1;
// Stack post: <address> <size> 0 <mem_offset_adjusted> <mem_offset>
m_context << Instruction::SWAP4;
// Stack post: <mem_offset> <size> 0 <mem_offset_adjusted> <address>
m_context << Instruction::EXTCODECOPY;
// Stack post: <mem_offset>
}
else if (member == "codehash")
{
utils().convertType(

View File

@ -0,0 +1,22 @@
contract C {
bytes public initCode;
constructor() {
// This should catch problems, but lets also test the case the optimiser is buggy.
assert(address(this).code.length == 0);
initCode = address(this).code;
}
// To avoid dependency on exact length.
function f() public view returns (bool) { return address(this).code.length > 400; }
function g() public view returns (uint) { return address(0).code.length; }
function h() public view returns (uint) { return address(1).code.length; }
}
// ====
// compileViaYul: false
// ----
// constructor() ->
// initCode() -> 0x20, 0
// f() -> true
// g() -> 0
// h() -> 0

View File

@ -0,0 +1,19 @@
contract A {
constructor() {
assembly {
// This is only 7 bytes here.
mstore(0, 0x48aa5566000000)
return(0, 32)
}
}
}
contract C {
function f() public returns (bytes memory) { return address(new A()).code; }
function g() public returns (uint) { return address(new A()).code.length; }
}
// ====
// compileViaYul: false
// ----
// f() -> 0x20, 0x20, 0x48aa5566000000
// g() -> 0x20

View File

@ -0,0 +1,8 @@
contract C {
function f() public view returns (address) { return address(this); }
function g() public view returns (uint) { return f().balance; }
function h() public view returns (bytes memory) { return f().code; }
function i() public view returns (uint) { return f().code.length; }
function j() public view returns (uint) { return h().length; }
}
// ----

View File

@ -1,6 +1,6 @@
contract C {
function f() public returns (C) { return this; }
function g() public returns (uint) { return f().balance(); }
function g() public returns (uint) { return f().balance; }
}
// ----
// TypeError 3125: (114-125): Member "balance" not found or not visible after argument-dependent lookup in contract C. Use "address(...).balance" to access this address member.