diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 68860b329a..84ce6b8f6c 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -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 diff --git a/test/unittests/evmmax_instructions_test.cpp b/test/unittests/evmmax_instructions_test.cpp new file mode 100644 index 0000000000..fcd500f4b6 --- /dev/null +++ b/test/unittests/evmmax_instructions_test.cpp @@ -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 +#include +#include + +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"); +} diff --git a/test/unittests/evmmax_test.cpp b/test/unittests/evmmax_test.cpp index 2fa24f6c2f..b0cf9793c9 100644 --- a/test/unittests/evmmax_test.cpp +++ b/test/unittests/evmmax_test.cpp @@ -2,12 +2,14 @@ // Copyright 2023 The evmone Authors. // SPDX-License-Identifier: Apache-2.0 +#include "evm_fixture.hpp" #include #include #include using namespace intx; using namespace evmmax; +using evmone::test::evm; // TODO(intx): Add ""_u384. inline constexpr auto operator""_u384(const char* s) @@ -140,3 +142,169 @@ TYPED_TEST(evmmax_test, mul) } } } + +namespace +{ +template +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 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(); + + 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 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(); + + 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(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(); + 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::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(); + 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); + } + + { + // 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); + } +}