Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EIP 4844 (part 2) #14759

Merged
merged 1 commit into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>`_).
r0qs marked this conversation as resolved.
Show resolved Hide resolved
- ``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>`_).
r0qs marked this conversation as resolved.
Show resolved Hide resolved

.. 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;
Comment on lines +1038 to +1044
Copy link
Collaborator

@nikola-matic nikola-matic Jan 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I really like this approach - these are separate cases for a reason - why would you combine them and then perform another check as to what the function kind is? Of course, it's easy to see what's going on once you read the code, but it's confusing at first glance - initially, I thought they may have the same opcode and use the difficulty/prevrandao mechanism based on the fork, which is obviously not the case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this one is short enough that it would have been clearer as a separate handler.

Still, when the code is longer this approach makes sense. The handler in IRGeneratorForStatements.cpp was actually the opposite case - there merging it with the other cases helps reduce duplication.

Copy link
Member Author

@r0qs r0qs Jan 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially implemented them separately, but as pointed out in #14759 (comment) there are instances where multiple built-ins are handled within the same case, both in IRGeneratorForStatements and ExpressionCompiler.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which is why I didn't complain about it myself, but @nikola-matic does have a point here :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@r0qs we seem to have wormholed ourselves into a parallel universe where Kamil doesn't complain, and I do. :D

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);
r0qs marked this conversation as resolved.
Show resolved Hide resolved
// 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
r0qs marked this conversation as resolved.
Show resolved Hide resolved
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)");
r0qs marked this conversation as resolved.
Show resolved Hide resolved

// ``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
r0qs marked this conversation as resolved.
Show resolved Hide resolved
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
r0qs marked this conversation as resolved.
Show resolved Hide resolved
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.
r0qs marked this conversation as resolved.
Show resolved Hide resolved
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