Skip to content

Commit

Permalink
Merge pull request #14759 from ethereum/eip-4844-add-blobhash-2
Browse files Browse the repository at this point in the history
EIP 4844 (part 2)
  • Loading branch information
cameel committed Jan 22, 2024
2 parents f05d0a9 + 81268e3 commit 01cb85f
Show file tree
Hide file tree
Showing 30 changed files with 234 additions and 12 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

Language Features:
* Introduce global ``block.blobbasefee`` for retrieving the blob base fee of the current block.
* Introduce global function ``blobhash(uint)`` for retrieving versioned hashes of blobs, akin to the homonymous Yul builtin.
* Yul: Introduce builtin ``blobbasefee()`` for retrieving the blob base fee of the current block.
* Yul: Introduce builtin ``blobhash()`` for retrieving versioned hashes of blobs associated with the transaction.

Expand Down
5 changes: 4 additions & 1 deletion docs/cheatsheet.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,16 @@ Members of ``address``
returns ``false`` on failure
- ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure

.. index:: blockhash, block, block;basefee, block;blobbasefee, block;chainid, block;coinbase, block;difficulty, block;gaslimit, block;number, block;prevrandao, block;timestamp
.. index:: blockhash, blobhash, block, block;basefee, block;blobbasefee, block;chainid, block;coinbase, block;difficulty, block;gaslimit, block;number, block;prevrandao, block;timestamp
.. index:: gasleft, msg;data, msg;sender, msg;sig, msg;value, tx;gasprice, tx;origin

Block and Transaction Properties
================================

- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks
- ``blobhash(uint index) returns (bytes32)``: versioned hash of the ``index``-th blob associated with the current transaction.
A versioned hash consists of a single byte representing the version (currently ``0x01``), followed by the last 31 bytes
of the SHA256 hash of the KZG commitment (`EIP-4844 <https://eips.ethereum.org/EIPS/eip-4844>`_).
- ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 <https://eips.ethereum.org/EIPS/eip-3198>`_ and `EIP-1559 <https://eips.ethereum.org/EIPS/eip-1559>`_)
- ``block.blobbasefee`` (``uint``): current block's blob base fee (`EIP-7516 <https://eips.ethereum.org/EIPS/eip-7516>`_ and `EIP-4844 <https://eips.ethereum.org/EIPS/eip-4844>`_)
- ``block.chainid`` (``uint``): current chain id
Expand Down
3 changes: 3 additions & 0 deletions docs/units-and-global-variables.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ Block and Transaction Properties
--------------------------------

- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block when ``blocknumber`` is one of the 256 most recent blocks; otherwise returns zero
- ``blobhash(uint index) returns (bytes32)``: versioned hash of the ``index``-th blob associated with the current transaction.
A versioned hash consists of a single byte representing the version (currently ``0x01``), followed by the last 31 bytes
of the SHA256 hash of the KZG commitment (`EIP-4844 <https://eips.ethereum.org/EIPS/eip-4844>`_).
- ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 <https://eips.ethereum.org/EIPS/eip-3198>`_ and `EIP-1559 <https://eips.ethereum.org/EIPS/eip-1559>`_)
- ``block.blobbasefee`` (``uint``): current block's blob base fee (`EIP-7516 <https://eips.ethereum.org/EIPS/eip-7516>`_ and `EIP-4844 <https://eips.ethereum.org/EIPS/eip-4844>`_)
- ``block.chainid`` (``uint``): current chain id
Expand Down
1 change: 1 addition & 0 deletions docs/using-the-compiler.rst
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ at each version. Backward compatibility is not guaranteed between each version.
- Smaller code size and gas savings due to the introduction of ``push0`` (see `EIP-3855 <https://eips.ethereum.org/EIPS/eip-3855>`_).
- ``cancun``
- The block's blob base fee (`EIP-7516 <https://eips.ethereum.org/EIPS/eip-7516>`_ and `EIP-4844 <https://eips.ethereum.org/EIPS/eip-4844>`_) can be accessed via the global ``block.blobbasefee`` or ``blobbasefee()`` in inline assembly.
- Introduces ``blobhash()`` in inline assembly and a corresponding global function to retrieve versioned hashes of blobs associated with the transaction (see `EIP-4844 <https://eips.ethereum.org/EIPS/eip-4844>`_).

.. index:: ! standard JSON, ! --standard-json
.. _compiler-api:
Expand Down
15 changes: 12 additions & 3 deletions libsolidity/analysis/GlobalContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,18 @@ int magicVariableToID(std::string const& _name)
else if (_name == "tx") return -26;
else if (_name == "type") return -27;
else if (_name == "this") return -28;
else if (_name == "blobhash") return -29;
else
solAssert(false, "Unknown magic variable: \"" + _name + "\".");
}

inline std::vector<std::shared_ptr<MagicVariableDeclaration const>> constructMagicVariables()
inline std::vector<std::shared_ptr<MagicVariableDeclaration const>> constructMagicVariables(langutil::EVMVersion _evmVersion)
{
static auto const magicVarDecl = [](std::string const& _name, Type const* _type) {
return std::make_shared<MagicVariableDeclaration>(magicVariableToID(_name), _name, _type);
};

return {
std::vector<std::shared_ptr<MagicVariableDeclaration const>> magicVariableDeclarations = {
magicVarDecl("abi", TypeProvider::magic(MagicType::Kind::ABI)),
magicVarDecl("addmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, StateMutability::Pure)),
magicVarDecl("assert", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Assert, StateMutability::Pure)),
Expand Down Expand Up @@ -101,11 +102,19 @@ inline std::vector<std::shared_ptr<MagicVariableDeclaration const>> constructMag
FunctionType::Options::withArbitraryParameters()
)),
};

if (_evmVersion >= langutil::EVMVersion::cancun())
magicVariableDeclarations.push_back(
magicVarDecl("blobhash", TypeProvider::function(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlobHash, StateMutability::View))
);

return magicVariableDeclarations;
}

}

GlobalContext::GlobalContext(): m_magicVariables{constructMagicVariables()}
GlobalContext::GlobalContext(langutil::EVMVersion _evmVersion):
m_magicVariables{constructMagicVariables(_evmVersion)}
{
}

Expand Down
3 changes: 2 additions & 1 deletion libsolidity/analysis/GlobalContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#pragma once

#include <liblangutil/EVMVersion.h>
#include <libsolidity/ast/ASTForward.h>
#include <map>
#include <memory>
Expand All @@ -47,7 +48,7 @@ class GlobalContext
GlobalContext(GlobalContext const&) = delete;
GlobalContext& operator=(GlobalContext const&) = delete;

GlobalContext();
GlobalContext(langutil::EVMVersion _evmVersion);
void setCurrentContract(ContractDefinition const& _contract);
void resetCurrentContract() { m_currentContract = nullptr; }
MagicVariableDeclaration const* currentThis() const;
Expand Down
1 change: 1 addition & 0 deletions libsolidity/ast/Types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3075,6 +3075,7 @@ std::string FunctionType::richIdentifier() const
case Kind::ABIEncodeCall: id += "abiencodecall"; break;
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
case Kind::ABIDecode: id += "abidecode"; break;
case Kind::BlobHash: id += "blobhash"; break;
case Kind::MetaType: id += "metatype"; break;
}
id += "_" + stateMutabilityToString(m_stateMutability);
Expand Down
1 change: 1 addition & 0 deletions libsolidity/ast/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,7 @@ class FunctionType: public Type
SetGas, ///< modify the default gas value for the function call
SetValue, ///< modify the default value transfer for the function call
BlockHash, ///< BLOCKHASH
BlobHash, ///< BLOBHASH
AddMod, ///< ADDMOD
MulMod, ///< MULMOD
ArrayPush, ///< .push() to a dynamically sized array in storage
Expand Down
6 changes: 5 additions & 1 deletion libsolidity/codegen/ExpressionCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1035,9 +1035,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break;
}
case FunctionType::Kind::BlockHash:
case FunctionType::Kind::BlobHash:
{
acceptAndConvert(*arguments[0], *function.parameterTypes()[0], true);
m_context << Instruction::BLOCKHASH;
if (function.kind() == FunctionType::Kind::BlockHash)
m_context << Instruction::BLOCKHASH;
else
m_context << Instruction::BLOBHASH;
break;
}
case FunctionType::Kind::AddMod:
Expand Down
2 changes: 2 additions & 0 deletions libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1498,11 +1498,13 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
case FunctionType::Kind::GasLeft:
case FunctionType::Kind::Selfdestruct:
case FunctionType::Kind::BlockHash:
case FunctionType::Kind::BlobHash:
{
static std::map<FunctionType::Kind, std::string> functions = {
{FunctionType::Kind::GasLeft, "gas"},
{FunctionType::Kind::Selfdestruct, "selfdestruct"},
{FunctionType::Kind::BlockHash, "blockhash"},
{FunctionType::Kind::BlobHash, "blobhash"},
};
solAssert(functions.find(functionType->kind()) != functions.end());

Expand Down
5 changes: 4 additions & 1 deletion libsolidity/interface/CompilerStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ void CompilerStack::setEVMVersion(langutil::EVMVersion _version)
if (m_stackState >= ParsedAndImported)
solThrow(CompilerError, "Must set EVM version before parsing.");
m_evmVersion = _version;
// GlobalContext depends on evmVersion since the Cancun hardfork.
// Therefore, we reset it whenever we set a new EVM version, ensuring that the context is never reused with a mismatched version.
m_globalContext.reset();
}

void CompilerStack::setEOFVersion(std::optional<uint8_t> _version)
Expand Down Expand Up @@ -469,7 +472,7 @@ bool CompilerStack::analyze()
if (source->ast && !syntaxChecker.checkSyntax(*source->ast))
noErrors = false;

m_globalContext = std::make_shared<GlobalContext>();
m_globalContext = std::make_shared<GlobalContext>(m_evmVersion);
// We need to keep the same resolver during the whole process.
NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter, experimentalSolidity);
for (Source const* source: m_sourceOrder)
Expand Down
1 change: 1 addition & 0 deletions scripts/test_antlr_grammar.sh
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ done < <(
grep -v -E 'inlineAssembly/prevrandao_disallowed_function_post_paris.sol' |
# Skipping a test with "let blobhash := ..."
grep -v -E 'inlineAssembly/blobhash_pre_cancun.sol' |
grep -v -E 'inlineAssembly/blobhash_pre_cancun_not_reserved.sol' |
# Skipping license error, unrelated to the grammar
grep -v -E 'license/license_double5.sol' |
grep -v -E 'license/license_hidden_unicode.sol' |
Expand Down
2 changes: 1 addition & 1 deletion test/libsolidity/Assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)

Scoper::assignScopes(*sourceUnit);
BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit));
GlobalContext globalContext;
GlobalContext globalContext(solidity::test::CommonOptions::get().evmVersion());
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false);
DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion());
solAssert(!Error::containsErrors(errorReporter.errors()), "");
Expand Down
25 changes: 24 additions & 1 deletion test/libsolidity/SolidityExpressionCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

using namespace solidity::evmasm;
using namespace solidity::langutil;
using namespace solidity::test;

namespace solidity::frontend::test
{
Expand Down Expand Up @@ -124,7 +125,7 @@ bytes compileFirstExpression(

ErrorList errors;
ErrorReporter errorReporter(errors);
GlobalContext globalContext;
GlobalContext globalContext(solidity::test::CommonOptions::get().evmVersion());
Scoper::assignScopes(*sourceUnit);
BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit));
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false);
Expand Down Expand Up @@ -656,6 +657,28 @@ BOOST_AUTO_TEST_CASE(blockhash)
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}

BOOST_AUTO_TEST_CASE(
blobhash,
*boost::unit_test::precondition(minEVMVersionCheck(EVMVersion::cancun()))
)
{
char const* sourceCode = R"(
contract test {
function f() public {
blobhash(3);
}
}
)";

bytes code = compileFirstExpression(sourceCode, {}, {});

bytes expectation({
uint8_t(Instruction::PUSH1), 0x03,
uint8_t(Instruction::BLOBHASH)
});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}

BOOST_AUTO_TEST_CASE(gas_left)
{
char const* sourceCode = R"(
Expand Down
2 changes: 2 additions & 0 deletions test/libsolidity/ViewPureChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ BOOST_AUTO_TEST_CASE(environment_access)
};
if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall())
view.emplace_back("address(0x4242).staticcall(\"\")");
if (solidity::test::CommonOptions::get().evmVersion().hasBlobHash())
view.emplace_back("blobhash(7)");

// ``block.blockhash`` and ``blockhash`` are tested separately below because their usage will
// produce warnings that can't be handled in a generic way.
Expand Down
18 changes: 18 additions & 0 deletions test/libsolidity/semanticTests/builtinFunctions/blobhash.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
contract C {
function f() public view returns(bytes32) {
return blobhash(0);
}
function g() public view returns(bytes32) {
return blobhash(1);
}
function h() public view returns(bytes32) {
// NOTE: blobhash(2) should return 0 since EVMHost has only two blob hashes injected in the block the transaction is being executed.
return blobhash(2);
}
}
// ====
// EVMVersion: >=cancun
// ----
// f() -> 0x0100000000000000000000000000000000000000000000000000000000000001
// g() -> 0x0100000000000000000000000000000000000000000000000000000000000002
// h() -> 0x00
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
contract C {
function blobhash(uint256 index) public pure returns(bytes32) {
return bytes32(index);
}
function f() public pure returns(bytes32) {
return blobhash(3);
}
}
// ====
// EVMVersion: >=cancun
// ----
// f() -> 0x03
14 changes: 14 additions & 0 deletions test/libsolidity/semanticTests/state/blobhash.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
contract C {
function f(uint _index) public returns (bytes32) {
return blobhash(_index);
}
}
// ====
// EVMVersion: >=cancun
// ----
// f(uint256): 0 -> 0x0100000000000000000000000000000000000000000000000000000000000001
// f(uint256): 1 -> 0x0100000000000000000000000000000000000000000000000000000000000002
// f(uint256): 2 -> 0x00
// f(uint256): 255 -> 0x00
// f(uint256): 256 -> 0x00
// f(uint256): 257 -> 0x00
12 changes: 12 additions & 0 deletions test/libsolidity/semanticTests/state/uncalled_blobhash.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
contract C {
function f() public returns (bytes32) {
// NOTE: The `tx_context.blob_hashes` is injected into EVMHost with the following hashes, indexed accordingly:
// 0 -> 0x0100000000000000000000000000000000000000000000000000000000000001
// 1 -> 0x0100000000000000000000000000000000000000000000000000000000000002
return (blobhash)(0);
}
}
// ====
// EVMVersion: >=cancun
// ----
// f() -> 0x0100000000000000000000000000000000000000000000000000000000000001
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
contract C {
function blobhash(uint256 index) public pure returns(bytes32) {
return bytes32(index);
}
function f() public pure returns(bytes32) {
return blobhash(2);
}
}
// ====
// EVMVersion: <=shanghai
// ----
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
contract C {
function blobhash(uint256 index) public pure returns(bytes32) {
return bytes32(index);
}
function f() public pure returns(bytes32) {
return blobhash(2);
}
}
// ====
// EVMVersion: >=cancun
// ----
// Warning 2319: (17-117): This declaration shadows a builtin symbol.
10 changes: 10 additions & 0 deletions test/libsolidity/syntaxTests/globalFunctions/blobhash_no_call.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract C
{
function f() public pure {
blobhash;
}
}
// ====
// EVMVersion: >=cancun
// ----
// Warning 6133: (52-60): Statement has no effect.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract C
{
function f() public pure {
blobhash;
}
}
// ====
// EVMVersion: <=shanghai
// ----
// DeclarationError 7576: (52-60): Undeclared identifier. Did you mean "blockhash"?
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
contract C {
function f() public pure returns (bool) {
bool blobhash = true;
return blobhash;
}
}
// ====
// EVMVersion: <=shanghai
// ----
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
contract C
{
function f() public pure returns (bool) {
bool blobhash = true;
return blobhash;
}
}
// ====
// EVMVersion: >=cancun
// ----
// Warning 2319: (67-80): This declaration shadows a builtin symbol.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
contract C {
function f() public pure returns (uint ret) {
assembly {
let blobhash := 1
ret := blobhash
}
}
function g() public pure returns (uint ret) {
assembly {
function blobhash() -> r {
r := 1000
}
ret := blobhash()
}
}
}
// ====
// EVMVersion: <=shanghai
// ----
Loading

0 comments on commit 01cb85f

Please sign in to comment.