Skip to content

Commit

Permalink
test: Add evmmax unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet committed Jan 5, 2024
1 parent e213406 commit 67808c1
Show file tree
Hide file tree
Showing 3 changed files with 318 additions and 0 deletions.
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ target_sources(
evmmax_bn254_add_test.cpp
evmmax_bn254_mul_test.cpp
evmmax_test.cpp
evmmax_instructions_test.cpp
evmmax_secp256k1_test.cpp
evmone_test.cpp
execution_state_test.cpp
Expand Down
131 changes: 131 additions & 0 deletions test/unittests/evmmax_instructions_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2023 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include "evm_fixture.hpp"
#include <evmmax/evmmax.hpp>
#include <gtest/gtest.h>
#include <array>

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

TEST_P(evm, evmmax_32bytes_modulus_test)
{
if (is_advanced())
return;

rev = EVMC_PRAGUE; /// TODO: Use EVMC_EVMMAX
// Modulus == 7
auto code = mstore(0, 0x07);
// 3 values slots
// Modulus size in bytes
// Modulus offset in EVM memory
// Modulus ID
code += setupx(3, 32, 0, 1);
// value 3
code += mstore(32, 0x03);
// value 6
code += mstore(64, 0x06);
// num values
// values offset
// store values
code += storex(2, 32, 0);
// ADDMODX for values in slots 0 and 1 save result in slot 2
code += addmodx(2, 1, 0);
// MULMODX for values in slots 1 and 2 save result in slot 2
code += mulmodx(2, 2, 1);
// SUBMODX for values in slots 1 and 2 save result in slot 2
code += submodx(2, 2, 1);
// load values from slot 2 into EVM memory
code += loadx(1, 2, 96);
// return loaded result
code += ret(96, 32);

execute(1000, code);
EXPECT_EQ(result.status_code, EVMC_SUCCESS);
EXPECT_OUTPUT_INT(6);
}

TEST_P(evm, evmmax_1byte_modulus_test)
{
if (is_advanced())
return;

rev = EVMC_PRAGUE; /// TODO: Use EVMC_EVMMAX
// Modulus == 7
auto code = mstore8(0, 0x07);
// 3 values slots
// Modulus size in bytes
// Modulus offset in EVM memory
// Modulus ID
code += setupx(3, 1, 0, 1);
// value 3
code += mstore8(8, 0x03);
// value 6
code += mstore8(16, 0x06);
// num values
// values offset
// store values
code += storex(2, 1, 0);
// ADDMODX for values in slots 0 and 1 save result in slot 2
code += addmodx(2, 1, 0);
// MULMODX for values in slots 1 and 2 save result in slot 2
code += mulmodx(2, 2, 1);
// SUBMODX for values in slots 1 and 2 save result in slot 2
code += submodx(2, 2, 1);
// load values from slot 2 into EVM memory
code += loadx(1, 2, 17);
// return loaded result
code += ret(17, 8);

execute(1000, code);
EXPECT_EQ(result.status_code, EVMC_SUCCESS);

ASSERT_EQ(result.output_size, 8);
EXPECT_EQ(hex({result.output_data, result.output_size}), "0000000000000006");
}

TEST_P(evm, evmmax_2byte_modulus_test)
{
if (is_advanced())
return;

rev = EVMC_PRAGUE; /// TODO: Use EVMC_EVMMAX
// Modulus == 263 (0x0107)
auto code = mstore8(0, 0x01);
code += mstore8(1, 0x07);
// 3 values slots
// Modulus size in bytes
// Modulus offset in EVM memory
// Modulus ID
code += setupx(3, 2, 0, 1);
// value 258
code += mstore8(8, 0x01);
code += mstore8(9, 0x02);
// value 254
code += mstore8(16, 0x00);
code += mstore8(17, 0xfe);
// num values
// values offset
// store values
code += storex(2, 2, 0);
// ADDMODX for values in slots 0 and 1 save result in slot 2
code += addmodx(2, 1, 0); // 258 + 254 = 249 mod 263
// MULMODX for values in slots 1 and 2 save result in slot 2
code += mulmodx(2, 2, 1); // 249 * 254 = 126 mod 263
// SUBMODX for values in slots 1 and 2 save result in slot 2
code += submodx(2, 2, 1); // 126 - 254 = 135 mod 263
// load values from slot 2 into EVM memory
code += loadx(1, 2, 18);
// return loaded result
code += ret(18, 8);

execute(1000, code);
EXPECT_EQ(result.status_code, EVMC_SUCCESS);

ASSERT_EQ(result.output_size, 8);
EXPECT_EQ(hex({result.output_data, result.output_size}), "0000000000000087");
}
186 changes: 186 additions & 0 deletions test/unittests/evmmax_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
// Copyright 2023 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include "evm_fixture.hpp"
#include <evmmax/evmmax.hpp>
#include <gtest/gtest.h>
#include <array>

using namespace intx;
using namespace evmmax;
using evmone::test::evm;

// TODO(intx): Add ""_u384.
inline constexpr auto operator""_u384(const char* s)
Expand Down Expand Up @@ -140,3 +142,187 @@ TYPED_TEST(evmmax_test, mul)
}
}
}

namespace
{
template <typename UintT>
inline bytecode create_test_bytecode()
{
constexpr auto size = sizeof(UintT);
return calldatacopy(push(0), push(0), push(size * 3)) + setupx(3, size, 0, 1) +
storex(2, size, 0) + mulmodx(2, 1, 0) + loadx(1, 2, size * 3) + ret(size * 3, size);
}

} // namespace

TEST_P(evm, exec_bn254_test)
{
using namespace evmone::test;

if (evm::is_advanced())
return;

evm::rev = EVMC_PRAGUE; /// TODO: Use EVMC_EVMMAX

const ModA<uint256, BN254Mod> m;

uint8_t calldata[3 * sizeof(uint256)];
intx::be::unsafe::store(&calldata[0], BN254Mod);

const auto values = get_test_values(m);

const auto code = create_test_bytecode<uint256>();

for (const auto& x : values)
{
for (const auto& y : values)
{
const auto expected = udivrem(umul(x, y), m.mod).rem;

intx::be::unsafe::store(&calldata[32], x);
intx::be::unsafe::store(&calldata[64], y);

execute(1000, code, {calldata, 96});
EXPECT_EQ(result.status_code, EVMC_SUCCESS);
EXPECT_OUTPUT_INT(expected);
}
}
}

TEST_P(evm, exec_bls_test)
{
using namespace evmone::test;

if (evm::is_advanced())
return;

evm::rev = EVMC_PRAGUE; /// TODO: Use EVMC_EVMMAX

const ModA<uint384, BLS12384Mod> m;

constexpr auto size = sizeof(uint384);
uint8_t calldata[3 * size];
intx::be::unsafe::store(&calldata[0], BLS12384Mod);

const auto values = get_test_values(m);

const auto code = create_test_bytecode<uint384>();

for (const auto& x : values)
{
for (const auto& y : values)
{
const auto expected = udivrem(umul(x, y), m.mod).rem;

intx::be::unsafe::store(&calldata[size], x);
intx::be::unsafe::store(&calldata[size * 2], y);

execute(1000, code, {calldata, size * 3});
EXPECT_EQ(result.status_code, EVMC_SUCCESS);
ASSERT_EQ(result.output_size, size);
EXPECT_EQ(intx::be::unsafe::load<uint384>(result.output_data), expected);
}
}
}

TEST_P(evm, exec_invalid_test)
{
using namespace evmone::test;

if (evm::is_advanced())
return;

evm::rev = EVMC_PRAGUE; /// TODO: Use EVMC_EVMMAX

{
// Even modulus
constexpr auto size = sizeof(uint256);
uint8_t calldata[3 * size];

const auto code = create_test_bytecode<uint256>();
intx::be::unsafe::store(&calldata[0], BN254Mod + 1);
execute(1000, code, {calldata, size * 3});
EXPECT_EQ(result.status_code, EVMC_FAILURE);
}

{
// Modulus too big
constexpr auto size = sizeof(intx::uint<4160>);
uint8_t calldata[3 * size];

const auto code = create_test_bytecode<intx::uint<4160>>();
intx::be::unsafe::store(&calldata[0], intx::uint<4160>(7));
execute(1000, code, {calldata, size * 3});
EXPECT_EQ(result.status_code, EVMC_FAILURE);
}

{
// Too many value slots
constexpr auto size = sizeof(uint256);
uint8_t calldata[size];

const auto code = calldatacopy(push(0), push(0), push(size)) + setupx(257, size, 0, 1);
intx::be::unsafe::store(&calldata[0], BN254Mod);
execute(1000, code, {calldata, size});
EXPECT_EQ(result.status_code, EVMC_FAILURE);
}

{
// not enough gas
constexpr auto size = sizeof(uint256);
uint8_t calldata[3 * size];

const auto code = create_test_bytecode<uint256>();
intx::be::unsafe::store(&calldata[0], BN254Mod);
execute(45, code, {calldata, size * 3});
EXPECT_EQ(result.status_code, EVMC_OUT_OF_GAS);
}

{
// Too much evmmax memory used
constexpr auto size = sizeof(intx::uint<2048>);
uint8_t calldata[size * 3];

const auto code = calldatacopy(push(0), push(0), push(size)) + setupx(1, size, 0, 1) +
setupx(256, size, 0, 2);
intx::be::unsafe::store(&calldata[0], intx::uint<2048>(BN254Mod));
execute(1000, code, {calldata, size});
EXPECT_EQ(result.status_code, EVMC_FAILURE);
}

{
// Invalid instruction index
constexpr auto size = sizeof(intx::uint<256>);
uint8_t calldata[size * 3];

const auto common_code = calldatacopy(push(0), push(0), push(size)) + setupx(1, size, 0, 1);
intx::be::unsafe::store(&calldata[0], intx::uint<256>(BN254Mod));

execute(1000, common_code + addmodx(0, 0, 2), {calldata, size});
EXPECT_EQ(result.status_code, EVMC_FAILURE);

execute(1000, common_code + mulmodx(0, 0, 2), {calldata, size});
EXPECT_EQ(result.status_code, EVMC_FAILURE);

execute(1000, common_code + submodx(0, 0, 2), {calldata, size});
EXPECT_EQ(result.status_code, EVMC_FAILURE);
}

{
// No active modulus
execute(1000, addmodx(0, 0, 1));
EXPECT_EQ(result.status_code, EVMC_FAILURE);

execute(1000, mulmodx(0, 0, 2));
EXPECT_EQ(result.status_code, EVMC_FAILURE);

execute(1000, submodx(0, 0, 2));
EXPECT_EQ(result.status_code, EVMC_FAILURE);

execute(1000, loadx(1, 0, 0));
EXPECT_EQ(result.status_code, EVMC_FAILURE);

execute(1000, storex(1, 0, 0));
EXPECT_EQ(result.status_code, EVMC_FAILURE);
}
}

0 comments on commit 67808c1

Please sign in to comment.