Support address().codehash

This commit is contained in:
Alex Beregszaszi 2020-09-24 18:45:40 +01:00
parent fca8026250
commit ad6739d0f6
12 changed files with 83 additions and 4 deletions

View File

@ -21,6 +21,7 @@ Breaking Changes:
* 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.
* Type System: Support ``address(...).codehash`` to retrieve the codehash of an account.
* Type System: Unary negation can only be used on signed integers, not on unsigned integers.
Language Features:
@ -157,7 +158,6 @@ Bugfixes:
Important Bugfixes:
* Type Checker: Disallow two or more free functions with identical name (potentially imported and aliased) and parameter types.
Compiler Features:
* Export compiler-generated utility sources via standard-json or combined-json.
* Optimizer: Optimize ``exp`` when base is 0, 1 or 2.

View File

@ -67,7 +67,7 @@ The following is the order of precedence for operators, listed in order of evalu
| *15* | Comma operator | ``,`` |
+------------+-------------------------------------+--------------------------------------------+
.. index:: assert, block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send
.. index:: assert, block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, codehash, send
Global Variables
================
@ -114,6 +114,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>.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
- ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure

View File

@ -211,7 +211,7 @@ Mathematical and Cryptographic Functions
When running ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*, you might encounter Out-of-Gas. This is because these functions are implemented as "precompiled contracts" and only really exist after they receive the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution might run into an Out-of-Gas error. A workaround for this problem is to first send Wei (1 for example) to each of the contracts before you use them in your actual contracts. This is not an issue on the main or test net.
.. index:: balance, send, transfer, call, callcode, delegatecall, staticcall
.. index:: balance, codehash, send, transfer, call, callcode, delegatecall, staticcall
.. _address_related:
@ -221,6 +221,9 @@ Members of Address Types
``<address>.balance`` (``uint256``)
balance of the :ref:`address` in Wei
``<address>.codehash`` (``bytes32``)
the codehash of the :ref:`address`
``<address payable>.transfer(uint256 amount)``
send given amount of Wei to :ref:`address`, reverts on failure, forwards 2300 gas stipend, not adjustable

View File

@ -2924,6 +2924,17 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
annotation.isPure = true;
}
if (
_memberAccess.expression().annotation().type->category() == Type::Category::Address &&
memberName == "codehash" &&
!m_evmVersion.hasExtCodeHash()
)
m_errorReporter.typeError(
7598_error,
_memberAccess.location(),
"\"codehash\" is not supported by the VM version."
);
if (!annotation.isPure.set())
annotation.isPure = false;

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")
if (member == "balance" || 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()},
{"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)},
{"delegatecall", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, false, StateMutability::NonPayable)},

View File

@ -1508,6 +1508,15 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
);
m_context << Instruction::BALANCE;
}
else if (member == "codehash")
{
utils().convertType(
*_memberAccess.expression().annotation().type,
*TypeProvider::address(),
true
);
m_context << Instruction::EXTCODEHASH;
}
else if ((set<string>{"send", "transfer"}).count(member))
{
solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, "");

View File

@ -1622,6 +1622,11 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
"balance(" <<
expressionAsType(_memberAccess.expression(), *TypeProvider::address()) <<
")\n";
else if (member == "codehash")
define(_memberAccess) <<
"extcodehash(" <<
expressionAsType(_memberAccess.expression(), *TypeProvider::address()) <<
")\n";
else if (set<string>{"send", "transfer"}.count(member))
{
solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, "");

View File

@ -0,0 +1,21 @@
contract C {
function f() public returns (bytes32) {
// non-existent in tests
return address(0).codehash;
}
function g() public returns (bytes32) {
// precompile
return address(0x1).codehash;
}
function h() public returns (bool) {
return address(this).codehash != 0;
}
}
// ====
// EVMVersion: >=constantinople
// compileViaYul: also
// ----
// f() -> 0x0
// g() -> 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
// h() -> true

View File

@ -0,0 +1,8 @@
contract C {
function f() public view returns (bytes32) {
return address(this).codehash;
}
}
// ====
// EVMVersion: >=constantinople
// ----

View File

@ -0,0 +1,9 @@
contract C {
function f() public view returns (bytes32) {
return address(this).codehash;
}
}
// ====
// EVMVersion: <constantinople
// ----
// TypeError 7598: (77-99): "codehash" is not supported by the VM version.

View File

@ -0,0 +1,11 @@
contract C {
function f() public view returns (bytes32) {
return address(this).codehash;
}
function g() public view returns (bytes32) {
return address(0).codehash;
}
}
// ====
// EVMVersion: >=constantinople
// ----