Skip to content
New issue

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

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

Already on GitHub? # to your account

Add BLOBHASH instruction #668

Merged
merged 2 commits into from
Sep 14, 2023
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
10 changes: 10 additions & 0 deletions lib/evmone/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,16 @@ inline void basefee(StackTop stack, ExecutionState& state) noexcept
stack.push(intx::be::load<uint256>(state.get_tx_context().block_base_fee));
}

inline void blobhash(StackTop stack, ExecutionState& state) noexcept
{
auto& index = stack.top();
const auto& tx = state.get_tx_context();

index = (index < tx.blob_hashes_count) ?
intx::be::load<uint256>(tx.blob_hashes[static_cast<size_t>(index)]) :
0;
}

inline Result extcodesize(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept
{
auto& x = stack.top();
Expand Down
1 change: 1 addition & 0 deletions lib/evmone/instructions_opcodes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ enum Opcode : uint8_t
OP_CHAINID = 0x46,
OP_SELFBALANCE = 0x47,
OP_BASEFEE = 0x48,
OP_BLOBHASH = 0x49,

OP_POP = 0x50,
OP_MLOAD = 0x51,
Expand Down
2 changes: 2 additions & 0 deletions lib/evmone/instructions_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ constexpr inline GasCostTable gas_costs = []() noexcept {
table[EVMC_SHANGHAI][OP_PUSH0] = 2;

table[EVMC_CANCUN] = table[EVMC_SHANGHAI];
table[EVMC_CANCUN][OP_BLOBHASH] = 3;
table[EVMC_CANCUN][OP_TLOAD] = warm_storage_read_cost;
table[EVMC_CANCUN][OP_TSTORE] = warm_storage_read_cost;
table[EVMC_CANCUN][OP_MCOPY] = 3;
Expand Down Expand Up @@ -287,6 +288,7 @@ constexpr inline std::array<Traits, 256> traits = []() noexcept {
table[OP_CHAINID] = {"CHAINID", 0, false, 0, 1, EVMC_ISTANBUL};
table[OP_SELFBALANCE] = {"SELFBALANCE", 0, false, 0, 1, EVMC_ISTANBUL};
table[OP_BASEFEE] = {"BASEFEE", 0, false, 0, 1, EVMC_LONDON};
table[OP_BLOBHASH] = {"BLOBHASH", 0, false, 1, 0, EVMC_CANCUN};

table[OP_POP] = {"POP", 0, false, 1, -1, EVMC_FRONTIER};
table[OP_MLOAD] = {"MLOAD", 0, false, 1, 0, EVMC_FRONTIER};
Expand Down
2 changes: 1 addition & 1 deletion lib/evmone/instructions_xmacro.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
ON_OPCODE_IDENTIFIER(OP_CHAINID, chainid) \
ON_OPCODE_IDENTIFIER(OP_SELFBALANCE, selfbalance) \
ON_OPCODE_IDENTIFIER(OP_BASEFEE, basefee) \
ON_OPCODE_UNDEFINED(0x49) \
ON_OPCODE_IDENTIFIER(OP_BLOBHASH, blobhash) \
ON_OPCODE_UNDEFINED(0x4a) \
ON_OPCODE_UNDEFINED(0x4b) \
ON_OPCODE_UNDEFINED(0x4c) \
Expand Down
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ target_sources(
evm_eip3198_basefee_test.cpp
evm_eip3855_push0_test.cpp
evm_eip3860_initcode_test.cpp
evm_eip4844_blobhash_test.cpp
evm_eof_test.cpp
evm_eof_calls_test.cpp
evm_eof_function_test.cpp
Expand Down
108 changes: 108 additions & 0 deletions test/unittests/evm_eip4844_blobhash_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2023 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

/// This file contains EVM unit tests for the BLOBHASH instruction from EIP-4844
/// https://eips.ethereum.org/EIPS/eip-4844

#include "evm_fixture.hpp"

using namespace evmc::literals;
using evmone::test::evm;

TEST_P(evm, blobhash_undefined)
{
rev = EVMC_SHANGHAI;
execute(blobhash(0));
EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION);
}

TEST_P(evm, blobhash_empty)
{
rev = EVMC_CANCUN;
execute(blobhash(0) + ret_top());
EXPECT_OUTPUT_INT(0);

execute(blobhash(1) + ret_top());
EXPECT_OUTPUT_INT(0);

execute(blobhash(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32) +
ret_top());
EXPECT_OUTPUT_INT(0);
}
Copy link
Member

Choose a reason for hiding this comment

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

You could also test it with 0xff...ff, here and in other tests

Copy link
Member Author

Choose a reason for hiding this comment

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

Added.

Copy link
Member

Choose a reason for hiding this comment

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

What about other tests?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added to all.


TEST_P(evm, blobhash_one)
{
rev = EVMC_CANCUN;

const std::array blob_hashes{
0x01feeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed_bytes32};

host.tx_context.blob_hashes = blob_hashes.data();
host.tx_context.blob_hashes_count = blob_hashes.size();

execute(blobhash(0) + ret_top());
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_EQ(output, blob_hashes[0]);

execute(blobhash(1) + ret_top());
EXPECT_OUTPUT_INT(0);

execute(blobhash(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32) +
ret_top());
EXPECT_OUTPUT_INT(0);
}

TEST_P(evm, blobhash_two)
{
rev = EVMC_CANCUN;

const std::array blob_hashes{
0x0100000000000000000000000000000000000000000000000000000000000001_bytes32,
0x0100000000000000000000000000000000000000000000000000000000000002_bytes32};

host.tx_context.blob_hashes = blob_hashes.data();
host.tx_context.blob_hashes_count = blob_hashes.size();

for (size_t i = 0; i < blob_hashes.size(); ++i)
{
execute(blobhash(i) + ret_top());
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_EQ(output, blob_hashes[i]);
}

execute(blobhash(blob_hashes.size()) + ret_top());
EXPECT_OUTPUT_INT(0);

execute(blobhash(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32) +
ret_top());
EXPECT_OUTPUT_INT(0);
}

TEST_P(evm, blobhash_invalid_hash_version)
{
rev = EVMC_CANCUN;

// The BLOBHASH instruction does not care about the hash version,
// it will return whatever is in the array.
const std::array blob_hashes{
0x0000000000000000000000000000000000000000000000000000000000000000_bytes32,
0x0200000000000000000000000000000000000000000000000000000000000000_bytes32};

host.tx_context.blob_hashes = blob_hashes.data();
host.tx_context.blob_hashes_count = blob_hashes.size();

for (size_t i = 0; i < blob_hashes.size(); ++i)
{
execute(blobhash(i) + ret_top());
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_EQ(output, blob_hashes[i]);
}

execute(blobhash(blob_hashes.size()) + ret_top());
EXPECT_OUTPUT_INT(0);

execute(blobhash(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32) +
ret_top());
EXPECT_OUTPUT_INT(0);
}
1 change: 1 addition & 0 deletions test/unittests/instructions_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ constexpr bool instruction_only_in_evmone(evmc_revision rev, Opcode op) noexcept

switch (op)
{
case OP_BLOBHASH:
case OP_RJUMP:
case OP_RJUMPI:
case OP_RJUMPV:
Expand Down
5 changes: 5 additions & 0 deletions test/utils/bytecode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,11 @@ inline bytecode blockhash(bytecode number)
return number + OP_BLOCKHASH;
}

inline bytecode blobhash(bytecode index)
{
return index + OP_BLOBHASH;
}

template <Opcode kind>
struct call_instruction
{
Expand Down