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
4 changes: 2 additions & 2 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -470,13 +470,13 @@ jobs:
working_directory: ~/build
command: >
bin/evmone-statetest ~/spec-tests/fixtures/state_tests/
--gtest_filter='-cancun/eip4844_blobs/blob_txs.invalid_tx_blob_count:frontier/precompiles/precompiles.precompiles:osaka/eip7594_peerdas/max_blob_per_tx.invalid_max_blobs_per_tx:osaka/eip7825_transaction_gas_limit_cap/tx_gas_limit.transaction_gas_limit_cap:osaka/eip7939_count_leading_zeros/*.*:osaka/eip7951_p256verify_precompiles/*.*:prague/eip7702_set_code_tx/set_code_txs.set_code_to_precompile'
--gtest_filter='-cancun/eip4844_blobs/blob_txs.invalid_tx_blob_count:frontier/precompiles/precompiles.precompiles:osaka/eip7594_peerdas/max_blob_per_tx.invalid_max_blobs_per_tx:osaka/eip7825_transaction_gas_limit_cap/tx_gas_limit.transaction_gas_limit_cap:osaka/eip7951_p256verify_precompiles/*.*:prague/eip7702_set_code_tx/set_code_txs.set_code_to_precompile'
- run:
name: "Fusaka pre-release execution spec tests (blockchain_tests)"
working_directory: ~/build
command: >
bin/evmone-blockchaintest ~/spec-tests/fixtures/blockchain_tests/
--gtest_filter='-cancun/eip4844_blobs/blob_txs.valid_blob_tx_combinations:frontier/precompiles/precompiles.precompiles:osaka/eip7594_peerdas/max_blob_per_tx.max_blobs_per_tx_fork_transition:osaka/eip7825_transaction_gas_limit_cap/tx_gas_limit.transaction_gas_limit_cap_at_transition:osaka/eip7918_blob_reserve_price/*.*:osaka/eip7934_block_rlp_limit/max_block_rlp_size.block_at_rlp_size_limit_boundary:osaka/eip7939_count_leading_zeros/*.*:osaka/eip7951_p256verify_precompiles/*.*:prague/eip7702_set_code_tx/set_code_txs.set_code_to_precompile'
--gtest_filter='-cancun/eip4844_blobs/blob_txs.valid_blob_tx_combinations:frontier/precompiles/precompiles.precompiles:osaka/eip7594_peerdas/max_blob_per_tx.max_blobs_per_tx_fork_transition:osaka/eip7825_transaction_gas_limit_cap/tx_gas_limit.transaction_gas_limit_cap_at_transition:osaka/eip7918_blob_reserve_price/*.*:osaka/eip7934_block_rlp_limit/max_block_rlp_size.block_at_rlp_size_limit_boundary:osaka/eip7951_p256verify_precompiles/*.*:prague/eip7702_set_code_tx/set_code_txs.set_code_to_precompile'
- collect_coverage_clang
- upload_coverage:
flags: eest-fusaka
Expand Down
5 changes: 5 additions & 0 deletions lib/evmone/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,11 @@ inline void sar(StackTop stack) noexcept
x = (x >> y) | (sign_mask << mask_shift);
}

inline void clz(StackTop stack) noexcept
{
stack.top() = clz(stack.top());
}

inline Result keccak256(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept
{
const auto& index = stack.pop();
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 @@ -41,6 +41,7 @@ enum Opcode : uint8_t
OP_SHL = 0x1b,
OP_SHR = 0x1c,
OP_SAR = 0x1d,
OP_CLZ = 0x1e,

OP_KECCAK256 = 0x20,

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 @@ -173,6 +173,7 @@ constexpr inline GasCostTable gas_costs = []() noexcept {
table[EVMC_PRAGUE] = table[EVMC_CANCUN];

table[EVMC_OSAKA] = table[EVMC_PRAGUE];
table[EVMC_OSAKA][OP_CLZ] = 5;

table[EVMC_EXPERIMENTAL] = table[EVMC_OSAKA];

Expand Down Expand Up @@ -281,6 +282,7 @@ constexpr inline std::array<Traits, 256> traits = []() noexcept {
table[OP_SHL] = {"SHL", 0, false, 2, -1, EVMC_CONSTANTINOPLE, REV_EOF1};
table[OP_SHR] = {"SHR", 0, false, 2, -1, EVMC_CONSTANTINOPLE, REV_EOF1};
table[OP_SAR] = {"SAR", 0, false, 2, -1, EVMC_CONSTANTINOPLE, REV_EOF1};
table[OP_CLZ] = {"CLZ", 0, false, 1, 0, EVMC_OSAKA, REV_EOF1};

table[OP_KECCAK256] = {"KECCAK256", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1};

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 @@ -63,7 +63,7 @@
ON_OPCODE_IDENTIFIER(OP_SHL, shl) \
ON_OPCODE_IDENTIFIER(OP_SHR, shr) \
ON_OPCODE_IDENTIFIER(OP_SAR, sar) \
ON_OPCODE_UNDEFINED(0x1e) \
ON_OPCODE_IDENTIFIER(OP_CLZ, clz) \
ON_OPCODE_UNDEFINED(0x1f) \
\
ON_OPCODE_IDENTIFIER(OP_KECCAK256, keccak256) \
Expand Down
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ target_sources(
evm_eip3860_initcode_test.cpp
evm_eip4844_blobhash_test.cpp
evm_eip7516_blobbasefee_test.cpp
evm_eip7939_clz_test.cpp
evm_eof_test.cpp
evm_eof_calls_test.cpp
evm_eof_function_test.cpp
Expand Down
56 changes: 56 additions & 0 deletions test/unittests/evm_eip7939_clz_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2025 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

/// This file contains EVM unit tests for EIP-7939 "Count leading zeros (CLZ) opcode"
/// https://eips.ethereum.org/EIPS/eip-7939

#include "evm_fixture.hpp"

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

TEST_P(evm, clz_pre_osaka)
{
rev = EVMC_PRAGUE;
const auto code = bytecode{OP_CLZ};

execute(code);
EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION);
}

TEST_P(evm, clz_gas)
{
rev = EVMC_OSAKA;
execute(bytecode{} + OP_PUSH0 + OP_CLZ);
EXPECT_GAS_USED(EVMC_SUCCESS, 2 + 5);
}

TEST_P(evm, clz_osaka)
{
rev = EVMC_OSAKA;
const std::vector<std::pair<bytecode, intx::uint256>> cases{
{0, 256},
{1, 255},
{0x8000000000000000000000000000000000000000000000000000000000000000_bytes32, 0},
{0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32, 0},
{0x4000000000000000000000000000000000000000000000000000000000000000_bytes32, 1},
{0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_bytes32, 1},
{0x0000000000000000000000000000000100000000000000000000000000000000_bytes32, 127},
{0x0000000000000000000000000000000000000000000000010000000000000000_bytes32, 191},
{0x0000000000000000000000000000000000000000000000008000000000000000_bytes32, 192},
};
for (const auto& [input, expected_output] : cases)
{
execute(clz(input) + ret_top());
EXPECT_GAS_USED(EVMC_SUCCESS, 23);
EXPECT_OUTPUT_INT(expected_output);
}
}

TEST_P(evm, clz_stack_underflow)
{
rev = EVMC_OSAKA;
execute(OP_CLZ);
EXPECT_STATUS(EVMC_STACK_UNDERFLOW);
}
1 change: 1 addition & 0 deletions test/unittests/instructions_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ constexpr bool instruction_only_in_evmone(evmc_revision rev, Opcode op) noexcept

switch (op)
{
case OP_CLZ:
case OP_BLOBHASH:
case OP_BLOBBASEFEE:
case OP_RJUMP:
Expand Down
5 changes: 5 additions & 0 deletions test/utils/bytecode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@ inline bytecode eq(bytecode a, bytecode b)
return b + a + OP_EQ;
}

inline bytecode clz(bytecode a)
{
return a + OP_CLZ;
}

inline bytecode byte(bytecode a, bytecode n)
{
return a + n + OP_BYTE;
Expand Down
2 changes: 2 additions & 0 deletions test/utils/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ RevisionSchedule to_rev_schedule(std::string_view s)
return {EVMC_SHANGHAI, EVMC_CANCUN, 15'000};
if (s == "CancunToPragueAtTime15k")
return {EVMC_CANCUN, EVMC_PRAGUE, 15'000};
if (s == "PragueToOsakaAtTime15k")
return {EVMC_PRAGUE, EVMC_OSAKA, 15'000};

const auto single_rev = to_rev(s);
return {single_rev, single_rev, 0};
Expand Down