diff --git a/Changelog.md b/Changelog.md index 296c49570..cb5060ea3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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. diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 29b338e93..8e2a95af5 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -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 - ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei +- ``
.codehash`` (``bytes32``): the codehash of the :ref:`address` - ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index e8c41c480..a6152d7f9 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -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 ``
.balance`` (``uint256``) balance of the :ref:`address` in Wei +``
.codehash`` (``bytes32``) + the codehash of the :ref:`address` + ``
.transfer(uint256 amount)`` send given amount of Wei to :ref:`address`, reverts on failure, forwards 2300 gas stipend, not adjustable diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index dffcc9252..31952f4ff 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -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; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 5dba65f2c..898c33e5f 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -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: diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c0b631022..4206513fc 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -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)}, diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 1b5da2c7a..2a0e512f5 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -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{"send", "transfer"}).count(member)) { solAssert(dynamic_cast(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, ""); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 688ab6166..00969faf5 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -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{"send", "transfer"}.count(member)) { solAssert(dynamic_cast(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, ""); diff --git a/test/libsolidity/semanticTests/various/codehash.sol b/test/libsolidity/semanticTests/various/codehash.sol new file mode 100644 index 000000000..c8730cca6 --- /dev/null +++ b/test/libsolidity/semanticTests/various/codehash.sol @@ -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 diff --git a/test/libsolidity/syntaxTests/types/address/codehash.sol b/test/libsolidity/syntaxTests/types/address/codehash.sol new file mode 100644 index 000000000..a7c418ca7 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/codehash.sol @@ -0,0 +1,8 @@ +contract C { + function f() public view returns (bytes32) { + return address(this).codehash; + } +} +// ==== +// EVMVersion: >=constantinople +// ---- \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address/codehash_before_constantinople.sol b/test/libsolidity/syntaxTests/types/address/codehash_before_constantinople.sol new file mode 100644 index 000000000..2e65e0ba3 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/codehash_before_constantinople.sol @@ -0,0 +1,9 @@ +contract C { + function f() public view returns (bytes32) { + return address(this).codehash; + } +} +// ==== +// EVMVersion: =constantinople +// ----