Skip to content
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
2 changes: 1 addition & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Language Features:
* Introduce global ``block.blobbasefee`` for retrieving the blob base fee of the current block.
* 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.
Copy link
Collaborator

@cameel cameel Jan 18, 2024

Choose a reason for hiding this comment

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

Oh, one more thing, let's finally stop messing with the spacing in the changelog :)

Suggested change
* Yul: Introduce builtin ``blobhash()`` for retrieving versioned hashes of blobs associated with the transaction.
* Yul: Introduce builtin ``blobhash()`` for retrieving versioned hashes of blobs associated with the transaction.

I fixed it earlier in all those PRs but it keeps reappearing again and again for some reason. Don't worry about the wrong spacing of the Compiler Features: section. We'll fix it when we sort the changelog. In PRs just add an entry for your PR and don't touch other lines.


Compiler Features:
* EVM: Support for the EVM Version "Cancun".
Expand Down
4 changes: 2 additions & 2 deletions docs/grammar/SolidityLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,8 @@ YulEVMBuiltin:
| 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode'
| 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid'
| 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice'
| 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'prevrandao'
| 'gaslimit' | 'basefee' | 'blobbasefee';
| 'blockhash' | 'blobhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty'
| 'prevrandao' | 'gaslimit' | 'basefee' | 'blobbasefee';

YulLBrace: '{' -> pushMode(YulMode);
YulRBrace: '}' -> popMode;
Expand Down
2 changes: 2 additions & 0 deletions docs/yul.rst
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,8 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a
+-------------------------+-----+---+-----------------------------------------------------------------+
| blockhash(b) | | F | hash of block nr b - only for last 256 blocks excluding current |
+-------------------------+-----+---+-----------------------------------------------------------------+
| blobhash(i) | | N | versioned hash of transaction's i-th blob |
+-------------------------+-----+---+-----------------------------------------------------------------+
| coinbase() | | F | current mining beneficiary |
+-------------------------+-----+---+-----------------------------------------------------------------+
| timestamp() | | F | timestamp of the current block in seconds since the epoch |
Expand Down
2 changes: 2 additions & 0 deletions libevmasm/Instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "RETURNDATACOPY", Instruction::RETURNDATACOPY },
{ "EXTCODEHASH", Instruction::EXTCODEHASH },
{ "BLOCKHASH", Instruction::BLOCKHASH },
{ "BLOBHASH", Instruction::BLOBHASH },
{ "COINBASE", Instruction::COINBASE },
{ "TIMESTAMP", Instruction::TIMESTAMP },
{ "NUMBER", Instruction::NUMBER },
Expand Down Expand Up @@ -223,6 +224,7 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } },
{ Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Balance } },
{ Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } },
{ Instruction::BLOBHASH, { "BLOBHASH", 0, 1, 1, false, Tier::VeryLow } },
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } },
{ Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } },
{ Instruction::NUMBER, { "NUMBER", 0, 0, 1, false, Tier::Base } },
Expand Down
1 change: 1 addition & 0 deletions libevmasm/Instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ enum class Instruction: uint8_t
CHAINID, ///< get the config's chainid param
SELFBALANCE, ///< get balance of the current account
BASEFEE, ///< get the block's basefee
BLOBHASH = 0x49, ///< get a versioned hash of one of the blobs associated with the transaction
BLOBBASEFEE = 0x4a, ///< get the block's blob basefee

POP = 0x50, ///< remove item from stack
Expand Down
1 change: 1 addition & 0 deletions libevmasm/SemanticInformation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction)
case Instruction::EXTCODECOPY:
case Instruction::EXTCODEHASH:
case Instruction::BLOCKHASH:
case Instruction::BLOBHASH:
case Instruction::COINBASE:
case Instruction::TIMESTAMP:
case Instruction::NUMBER:
Expand Down
1 change: 1 addition & 0 deletions libevmasm/SimplificationRule.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ struct EVMBuiltins
static auto constexpr RETURNDATACOPY = PatternGenerator<Instruction::RETURNDATACOPY>{};
static auto constexpr EXTCODEHASH = PatternGenerator<Instruction::EXTCODEHASH>{};
static auto constexpr BLOCKHASH = PatternGenerator<Instruction::BLOCKHASH>{};
static auto constexpr BLOBHASH = PatternGenerator<Instruction::BLOBHASH>{};
static auto constexpr COINBASE = PatternGenerator<Instruction::COINBASE>{};
static auto constexpr TIMESTAMP = PatternGenerator<Instruction::TIMESTAMP>{};
static auto constexpr NUMBER = PatternGenerator<Instruction::NUMBER>{};
Expand Down
2 changes: 2 additions & 0 deletions liblangutil/EVMVersion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ bool EVMVersion::hasOpcode(Instruction _opcode) const
return hasSelfBalance();
case Instruction::BASEFEE:
return hasBaseFee();
case Instruction::BLOBHASH:
return hasBlobHash();
case Instruction::BLOBBASEFEE:
return hasBlobBaseFee();
default:
Expand Down
1 change: 1 addition & 0 deletions liblangutil/EVMVersion.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class EVMVersion:
bool hasBlobBaseFee() const { return *this >= cancun(); }
bool hasPrevRandao() const { return *this >= paris(); }
bool hasPush0() const { return *this >= shanghai(); }
bool hasBlobHash() const { return *this >= cancun(); }

bool hasOpcode(evmasm::Instruction _opcode) const;

Expand Down
7 changes: 7 additions & 0 deletions libyul/AsmAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ void AsmAnalyzer::expectType(YulString _expectedType, YulString _givenType, Sour

bool AsmAnalyzer::validateInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location)
{
// NOTE: This function uses the default EVM version instead of the currently selected one.
auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulString(_instructionIdentifier));
if (builtin && builtin->instruction.has_value())
return validateInstructions(builtin->instruction.value(), _location);
Expand Down Expand Up @@ -705,6 +706,9 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
);
};

// The errors below are meant to be issued when processing an undeclared identifier matching a builtin name
// present on the default EVM version but not on the currently selected one,
// since the other `validateInstructions()` overload uses the default EVM version.
if (_instr == evmasm::Instruction::RETURNDATACOPY && !m_evmVersion.supportsReturndata())
errorForVM(7756_error, "only available for Byzantium-compatible");
else if (_instr == evmasm::Instruction::RETURNDATASIZE && !m_evmVersion.supportsReturndata())
Expand All @@ -729,6 +733,9 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
errorForVM(5430_error, "only available for London-compatible");
else if (_instr == evmasm::Instruction::BLOBBASEFEE && !m_evmVersion.hasBlobBaseFee())
errorForVM(6679_error, "only available for Cancun-compatible");
else if (_instr == evmasm::Instruction::BLOBHASH && !m_evmVersion.hasBlobHash())
// TODO: Change this assertion to an error, similar to the ones above, when Cancun becomes the default EVM version.
yulAssert(false);
else if (_instr == evmasm::Instruction::PC)
m_errorReporter.error(
2450_error,
Expand Down
12 changes: 10 additions & 2 deletions libyul/backends/evm/EVMDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,22 @@ std::set<YulString> createReservedIdentifiers(langutil::EVMVersion _evmVersion)
return _instrName == "prevrandao" && _evmVersion < langutil::EVMVersion::paris();
};

// TODO remove this in 0.9.0. We allow creating functions or identifiers in Yul with the name
// blobhash for VMs before cancun.
auto blobHashException = [&](evmasm::Instruction _instr) -> bool
{
return _instr == evmasm::Instruction::BLOBHASH && _evmVersion < langutil::EVMVersion::cancun();
};

std::set<YulString> reserved;
for (auto const& instr: evmasm::c_instructions)
{
std::string name = toLower(instr.first);
if (
!baseFeeException(instr.second) &&
!blobBaseFeeException(instr.second) &&
!prevRandaoException(name)
!prevRandaoException(name) &&
!blobHashException(instr.second) &&
!blobBaseFeeException(instr.second)
)
reserved.emplace(name);
}
Expand Down
2 changes: 2 additions & 0 deletions scripts/test_antlr_grammar.sh
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ done < <(
# Skipping tests with "let prevrandao := ..."
grep -v -E 'inlineAssembly/prevrandao_allowed_function_pre_paris.sol' |
grep -v -E 'inlineAssembly/prevrandao_disallowed_function_post_paris.sol' |
# Skipping a test with "let blobhash := ..."
grep -v -E 'inlineAssembly/blobhash_pre_cancun.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
7 changes: 7 additions & 0 deletions test/EVMHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):
// The minimum value of blobbasefee
tx_context.blob_base_fee = evmc::bytes32{1};

static evmc_bytes32 const blob_hashes_array[] = {
0x0100000000000000000000000000000000000000000000000000000000000001_bytes32,
0x0100000000000000000000000000000000000000000000000000000000000002_bytes32
};
tx_context.blob_hashes = blob_hashes_array;
tx_context.blob_hashes_count = sizeof(blob_hashes_array) / sizeof(blob_hashes_array[0]);

// Reserve space for recording calls.
if (!recorded_calls.capacity())
recorded_calls.reserve(max_recorded_calls);
Expand Down
11 changes: 11 additions & 0 deletions test/libsolidity/semanticTests/inlineAssembly/blobhash.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
contract C {
function f() public view returns (bytes32 ret) {
assembly {
ret := blobhash(0)
}
}
}
// ====
// EVMVersion: >=cancun
// ----
// f() -> 0x0100000000000000000000000000000000000000000000000000000000000001
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
contract C {
function f() public view returns (bytes32 ret) {
assembly {
// EIP-4844 specifies that if `index < len(tx.blob_versioned_hashes)`, `blobhash(index)` should return 0.
// Thus, as we injected only two blob hashes in the transaction context in EVMHost,
// the return value of the function below MUST be zero.
ret := blobhash(2)
}
}
}
// ====
// EVMVersion: >=cancun
// ----
// f() -> 0x00
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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
// ----
// f() -> 1
// g() -> 1000
10 changes: 10 additions & 0 deletions test/libsolidity/syntaxTests/inlineAssembly/blobhash.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract C {
function f() public view returns (bytes32 ret) {
assembly {
ret := blobhash(1)
}
}
}
// ====
// EVMVersion: >=cancun
// ----
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
contract C {
function f() pure external returns (bytes32 ret) {
assembly {
ret := blobhash()
}
}
}
// ====
// EVMVersion: <=shanghai
// ----
// DeclarationError 4619: (106-114): Function "blobhash" not found.
// DeclarationError 8678: (99-116): Variable count for assignment to "ret" does not match number of values (1 vs. 0)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
contract C {
function f() public pure returns (uint ret) {
assembly {
function blobhash() -> r {
r := 1000
}
ret := blobhash()
}
}
}
// ====
// EVMVersion: >=cancun
// ----
// ParserError 5568: (103-111): Cannot use builtin function name "blobhash" as identifier name.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
contract C {
function f() public pure {
assembly { pop(blobhash(0)) }
}
}
// ====
// EVMVersion: >=cancun
// ----
// TypeError 2527: (67-78): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
contract C {
function f() public view {
assembly {
pop(blobhash(0))
pop(blobbasefee())
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
contract C {
function f() public pure {
assembly {
pop(blobhash(0))
pop(blobbasefee())
}
}
}
// ====
// EVMVersion: >=cancun
// ----
// TypeError 2527: (79-92): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (79-90): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError 2527: (108-121): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
13 changes: 13 additions & 0 deletions test/libyul/yulInterpreterTests/blobhash.yul
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
sstore(0, blobhash(0))
sstore(1, blobhash(1))
sstore(2, blobhash(2)) // should store 0 since EVMHost has only two blob hashes injected in the block the transaction is being executed.
}
// ====
// EVMVersion: >=cancun
// ----
// Trace:
// Memory dump:
// Storage dump:
// 0000000000000000000000000000000000000000000000000000000000000000: 014916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5
// 0000000000000000000000000000000000000000000000000000000000000001: 0167d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2
15 changes: 15 additions & 0 deletions test/libyul/yulSyntaxTests/blobhash.yul
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
{
let blobhash := 1
}

{
function blobhash() {}
blobhash()
}
}

// ====
// EVMVersion: >=cancun
// ----
// ParserError 5568: (20-28): Cannot use builtin function name "blobhash" as identifier name.
13 changes: 13 additions & 0 deletions test/libyul/yulSyntaxTests/blobhash_pre_cancun.yul
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
{
let blobhash := 1
}

{
function blobhash() {}
blobhash()
}
}
// ====
// EVMVersion: <=shanghai
// ----
16 changes: 16 additions & 0 deletions test/tools/yulInterpreter/EVMInstructionInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

#include <libsolutil/Keccak256.h>
#include <libsolutil/Numeric.h>
#include <libsolutil/picosha2.h>

#include <limits>

Expand Down Expand Up @@ -233,6 +234,8 @@ u256 EVMInstructionInterpreter::eval(
return m_state.chainid;
case Instruction::BASEFEE:
return m_state.basefee;
case Instruction::BLOBHASH:
return blobHash(arg[0]);
case Instruction::BLOBBASEFEE:
return m_state.blobbasefee;
case Instruction::EXTCODESIZE:
Expand Down Expand Up @@ -649,3 +652,16 @@ std::pair<bool, size_t> EVMInstructionInterpreter::isInputMemoryPtrModified(
else
return {false, 0};
}

h256 EVMInstructionInterpreter::blobHash(u256 const& _index)
{
yulAssert(m_evmVersion.hasBlobHash());
if (_index >= m_state.blobCommitments.size())
return util::FixedHash<32>{};

h256 hashedCommitment = h256(picosha2::hash256(toBigEndian(m_state.blobCommitments[static_cast<size_t>(_index)])));
yulAssert(m_state.blobHashVersion.size == 1);
hashedCommitment[0] = *m_state.blobHashVersion.data();
yulAssert(hashedCommitment.size == 32);
return hashedCommitment;
}
4 changes: 4 additions & 0 deletions test/tools/yulInterpreter/EVMInstructionInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <libyul/ASTForward.h>

#include <libsolutil/CommonData.h>
#include <libsolutil/FixedHash.h>
#include <libsolutil/Numeric.h>

#include <liblangutil/EVMVersion.h>
Expand Down Expand Up @@ -90,6 +91,9 @@ class EVMInstructionInterpreter
std::vector<u256> const& _evaluatedArguments
);

/// @returns the blob versioned hash
util::h256 blobHash(u256 const& _index);

private:
/// Checks if the memory access is valid and adjusts msize accordingly.
/// @returns true if memory access is valid, false otherwise
Expand Down
5 changes: 5 additions & 0 deletions test/tools/yulInterpreter/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ struct InterpreterState
/// Number of the current state instance, used for recursion protection
size_t numInstance = 0;

// Blob commitment hash version
util::FixedHash<1> const blobHashVersion = util::FixedHash<1>(1);
// Blob commitments
std::array<u256, 2> const blobCommitments = {0x01, 0x02};

/// Prints execution trace and non-zero storage to @param _out.
/// Flag @param _disableMemoryTrace, if set, does not produce a memory dump. This
/// avoids false positives reports by the fuzzer when certain optimizer steps are
Expand Down