diff --git a/test/unittests/evmmax_test.cpp b/test/unittests/evmmax_test.cpp index 2fa24f6c2f..65a1e1616a 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,1031 @@ 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); + } + + { + // 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); + } +} + +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"); +} + +namespace +{ +struct SlotsScope; + +struct ValueSlotsRegister +{ +private: + friend struct SlotsScope; + + std::vector vals; + + [[nodiscard]] uint8_t register_slot() noexcept + { + if (const auto it = std::find(vals.begin(), vals.end(), false); it != vals.end()) + { + *it = true; + return static_cast(std::distance(vals.begin(), it)); + } + else + { + assert(vals.size() < 256); + vals.push_back(true); + return static_cast(vals.size() - 1); + } + } + + void unregister_slot(uint8_t slot_idx) noexcept + { + if (slot_idx < vals.size()) + vals[slot_idx] = false; + else + assert(false); // Invalid slot idx + } + +public: + explicit ValueSlotsRegister() noexcept = default; + [[nodiscard]] uint8_t max_slots_used() const { return static_cast(vals.size()); } +}; + +struct SlotsScope +{ +private: + std::set slots; + ValueSlotsRegister& value_slots_register; + +public: + explicit SlotsScope(ValueSlotsRegister& vs_reg) noexcept : value_slots_register(vs_reg) {} + + [[nodiscard]] uint8_t register_slot() noexcept + { + const auto new_slot = value_slots_register.register_slot(); + slots.insert(new_slot); + return new_slot; + } + + virtual ~SlotsScope() noexcept + { + for (const auto& slot : slots) + value_slots_register.unregister_slot(slot); + } +}; + +bytecode bn254_ecadd(ValueSlotsRegister& vs_reg, uint8_t x3_idx, uint8_t y3_idx, uint8_t z3_idx, + uint8_t x1_idx, uint8_t y1_idx, uint8_t z1_idx, uint8_t x2_idx, uint8_t y2_idx, uint8_t z2_idx, + uint8_t b3_idx) +{ + auto code = bytecode{}; + SlotsScope scope(vs_reg); + // Point addition in projective space. + const auto t0_idx = scope.register_slot(); + const auto t1_idx = scope.register_slot(); + const auto t2_idx = scope.register_slot(); + const auto t3_idx = scope.register_slot(); + const auto t4_idx = scope.register_slot(); + + code += mulmodx(t0_idx, x1_idx, x2_idx); // 1 + code += mulmodx(t1_idx, y1_idx, y2_idx); // 2 + code += mulmodx(t2_idx, z1_idx, z2_idx); // 3 + code += addmodx(t3_idx, x1_idx, y1_idx); // 4 + code += addmodx(t4_idx, x2_idx, y2_idx); // 5 + code += mulmodx(t3_idx, t3_idx, t4_idx); // 6 + code += addmodx(t4_idx, t0_idx, t1_idx); // 7 + code += submodx(t3_idx, t3_idx, t4_idx); // 8 + code += addmodx(t4_idx, y1_idx, z1_idx); // 9 + code += addmodx(x3_idx, y2_idx, z2_idx); // 10 + code += mulmodx(t4_idx, t4_idx, x3_idx); // 11 + code += addmodx(x3_idx, t1_idx, t2_idx); // 12 + code += submodx(t4_idx, t4_idx, x3_idx); // 13 + code += addmodx(x3_idx, x1_idx, z1_idx); // 14 + code += addmodx(y3_idx, x2_idx, z2_idx); // 15 + code += mulmodx(x3_idx, x3_idx, y3_idx); // 16 + code += addmodx(y3_idx, t0_idx, t2_idx); // 17 + code += submodx(y3_idx, x3_idx, y3_idx); // 18 + code += addmodx(x3_idx, t0_idx, t0_idx); // 19 + code += addmodx(t0_idx, x3_idx, t0_idx); // 20 + code += mulmodx(t2_idx, b3_idx, t2_idx); // 21 + code += addmodx(z3_idx, t1_idx, t2_idx); // 22 + code += submodx(t1_idx, t1_idx, t2_idx); // 23 + code += mulmodx(y3_idx, b3_idx, y3_idx); // 24 + code += mulmodx(x3_idx, t4_idx, y3_idx); // 25 + code += mulmodx(t2_idx, t3_idx, t1_idx); // 26 + code += submodx(x3_idx, t2_idx, x3_idx); // 27 + code += mulmodx(y3_idx, y3_idx, t0_idx); // 28 + code += mulmodx(t1_idx, t1_idx, z3_idx); // 29 + code += addmodx(y3_idx, t1_idx, y3_idx); // 30 + code += mulmodx(t0_idx, t0_idx, t3_idx); // 31 + code += mulmodx(z3_idx, z3_idx, t4_idx); // 32 + code += addmodx(z3_idx, z3_idx, t0_idx); // 33 + + return code; +} + +bytecode field_inv_bn254(ValueSlotsRegister& vs_reg, uint8_t dst_idx, uint8_t x_idx) +{ + auto code = bytecode{}; + + SlotsScope scope(vs_reg); + // Inversion computation + // Allocate Temporaries. + const auto t0_idx = scope.register_slot(); + const auto t1_idx = scope.register_slot(); + const auto t2_idx = scope.register_slot(); + const auto t3_idx = scope.register_slot(); + const auto t4_idx = scope.register_slot(); + const auto t5_idx = scope.register_slot(); + const auto t6_idx = scope.register_slot(); + const auto t7_idx = scope.register_slot(); + const auto t8_idx = scope.register_slot(); + const auto t9_idx = scope.register_slot(); + const auto t10_idx = scope.register_slot(); + const auto t11_idx = scope.register_slot(); + const auto t12_idx = scope.register_slot(); + const auto t13_idx = scope.register_slot(); + const auto t14_idx = scope.register_slot(); + const auto t15_idx = scope.register_slot(); + const auto t16_idx = scope.register_slot(); + const auto t17_idx = scope.register_slot(); + const auto t18_idx = scope.register_slot(); + const auto t19_idx = scope.register_slot(); + const auto t20_idx = scope.register_slot(); + const auto t21_idx = scope.register_slot(); + const auto z_idx = dst_idx; + + // Step 1: t8 = x^0x2 + code += mulmodx(t8_idx, x_idx, x_idx); + + // Step 2: t15 = x^0x3 + code += mulmodx(t15_idx, x_idx, t8_idx); + + // Step 3: z = x^0x5 + code += mulmodx(z_idx, t8_idx, t15_idx); + + // Step 4: t1 = x^0x6 + code += mulmodx(t1_idx, x_idx, z_idx); + + // Step 5: t3 = x^0x8 + code += mulmodx(t3_idx, t8_idx, t1_idx); + + // Step 6: t9 = x^0xd + code += mulmodx(t9_idx, z_idx, t3_idx); + + // Step 7: t6 = x^0x12 + code += mulmodx(t6_idx, z_idx, t9_idx); + + // Step 8: t19 = x^0x13 + code += mulmodx(t19_idx, x_idx, t6_idx); + + // Step 9: t0 = x^0x14 + code += mulmodx(t0_idx, x_idx, t19_idx); + + // Step 10: t20 = x^0x17 + code += mulmodx(t20_idx, t15_idx, t0_idx); + + // Step 11: t2 = x^0x1c + code += mulmodx(t2_idx, z_idx, t20_idx); + + // Step 12: t17 = x^0x20 + code += mulmodx(t17_idx, t9_idx, t19_idx); + + // Step 13: t4 = x^0x23 + code += mulmodx(t4_idx, t15_idx, t17_idx); + + // Step 14: t14 = x^0x2b + code += mulmodx(t14_idx, t3_idx, t4_idx); + + // Step 15: t12 = x^0x2f + code += mulmodx(t12_idx, t19_idx, t2_idx); + + // Step 16: t16 = x^0x41 + code += mulmodx(t16_idx, t6_idx, t12_idx); + + // Step 17: t18 = x^0x53 + code += mulmodx(t18_idx, t6_idx, t16_idx); + + // Step 18: t3 = x^0x5b + code += mulmodx(t3_idx, t3_idx, t18_idx); + + // Step 19: t5 = x^0x61 + code += mulmodx(t5_idx, t1_idx, t3_idx); + + // Step 20: t0 = x^0x75 + code += mulmodx(t0_idx, t0_idx, t5_idx); + + // Step 21: t10 = x^0x91 + code += mulmodx(t10_idx, t2_idx, t0_idx); + + // Step 22: t7 = x^0x95 + code += mulmodx(t7_idx, t17_idx, t0_idx); + + // Step 23: t11 = x^0xb5 + code += mulmodx(t11_idx, t17_idx, t7_idx); + + // Step 24: t13 = x^0xbb + code += mulmodx(t13_idx, t1_idx, t11_idx); + + // Step 25: t21 = x^0xc1 + code += mulmodx(t21_idx, t1_idx, t13_idx); + + // Step 26: t2 = x^0xc3 + code += mulmodx(t2_idx, t8_idx, t21_idx); + + // Step 27: t6 = x^0xd3 + code += mulmodx(t6_idx, t6_idx, t21_idx); + + // Step 28: t17 = x^0xe1 + code += mulmodx(t17_idx, t17_idx, t21_idx); + + // Step 29: t8 = x^0xe3 + code += mulmodx(t8_idx, t8_idx, t17_idx); + + // Step 30: t1 = x^0xe7 + code += mulmodx(t1_idx, t1_idx, t17_idx); + + // Step 38: t21 = x^0xc100 + // for (int i = 0; i < 8; ++i) + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + + // Step 39: t21 = x^0xc191 + code += mulmodx(t21_idx, t10_idx, t21_idx); + + // Step 49: t21 = x^0x3064400 + // for (int i = 0; i < 10; ++i) + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + + // Step 50: t21 = x^0x30644e7 + code += mulmodx(t21_idx, t1_idx, t21_idx); + + // Step 57: t21 = x^0x183227380 + // for (int i = 0; i < 7; ++i) + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + code += mulmodx(t21_idx, t21_idx, t21_idx); + + // Step 58: t20 = x^0x183227397 + code += mulmodx(t20_idx, t20_idx, t21_idx); + + // Step 67: t20 = x^0x30644e72e00 + // for (int i = 0; i < 9; ++i) + code += mulmodx(t20_idx, t20_idx, t20_idx); + code += mulmodx(t20_idx, t20_idx, t20_idx); + code += mulmodx(t20_idx, t20_idx, t20_idx); + code += mulmodx(t20_idx, t20_idx, t20_idx); + code += mulmodx(t20_idx, t20_idx, t20_idx); + code += mulmodx(t20_idx, t20_idx, t20_idx); + code += mulmodx(t20_idx, t20_idx, t20_idx); + code += mulmodx(t20_idx, t20_idx, t20_idx); + code += mulmodx(t20_idx, t20_idx, t20_idx); + + // Step 68: t19 = x^0x30644e72e13 + code += mulmodx(t19_idx, t19_idx, t20_idx); + + // Step 75: t19 = x^0x1832273970980 + // for (int i = 0; i < 7; ++i) + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + + // Step 76: t19 = x^0x183227397098d + code += mulmodx(t19_idx, t9_idx, t19_idx); + + // Step 90: t19 = x^0x60c89ce5c2634000 + // for (int i = 0; i < 14; ++i) + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + code += mulmodx(t19_idx, t19_idx, t19_idx); + + // Step 91: t18 = x^0x60c89ce5c2634053 + code += mulmodx(t18_idx, t18_idx, t19_idx); + + // Step 100: t18 = x^0xc19139cb84c680a600 + // for (int i = 0; i < 9; ++i) + code += mulmodx(t18_idx, t18_idx, t18_idx); + code += mulmodx(t18_idx, t18_idx, t18_idx); + code += mulmodx(t18_idx, t18_idx, t18_idx); + code += mulmodx(t18_idx, t18_idx, t18_idx); + code += mulmodx(t18_idx, t18_idx, t18_idx); + code += mulmodx(t18_idx, t18_idx, t18_idx); + code += mulmodx(t18_idx, t18_idx, t18_idx); + code += mulmodx(t18_idx, t18_idx, t18_idx); + code += mulmodx(t18_idx, t18_idx, t18_idx); + + // Step 101: t17 = x^0xc19139cb84c680a6e1 + code += mulmodx(t17_idx, t17_idx, t18_idx); + + // Step 109: t17 = x^0xc19139cb84c680a6e100 + // for (int i = 0; i < 8; ++i) + code += mulmodx(t17_idx, t17_idx, t17_idx); + code += mulmodx(t17_idx, t17_idx, t17_idx); + code += mulmodx(t17_idx, t17_idx, t17_idx); + code += mulmodx(t17_idx, t17_idx, t17_idx); + code += mulmodx(t17_idx, t17_idx, t17_idx); + code += mulmodx(t17_idx, t17_idx, t17_idx); + code += mulmodx(t17_idx, t17_idx, t17_idx); + code += mulmodx(t17_idx, t17_idx, t17_idx); + + // Step 110: t16 = x^0xc19139cb84c680a6e141 + code += mulmodx(t16_idx, t16_idx, t17_idx); + + // Step 120: t16 = x^0x30644e72e131a029b850400 + // for (int i = 0; i < 10; ++i) + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + + // Step 121: t16 = x^0x30644e72e131a029b85045b + code += mulmodx(t16_idx, t3_idx, t16_idx); + + // Step 126: t16 = x^0x60c89ce5c263405370a08b60 + // for (int i = 0; i < 5; ++i) + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + + // Step 127: t16 = x^0x60c89ce5c263405370a08b6d + code += mulmodx(t16_idx, t9_idx, t16_idx); + + // Step 135: t16 = x^0x60c89ce5c263405370a08b6d00 + // for (int i = 0; i < 8; ++i) + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + code += mulmodx(t16_idx, t16_idx, t16_idx); + + // Step 136: t15 = x^0x60c89ce5c263405370a08b6d03 + code += mulmodx(t15_idx, t15_idx, t16_idx); + + // Step 148: t15 = x^0x60c89ce5c263405370a08b6d03000 + // for (int i = 0; i < 12; ++i) + code += mulmodx(t15_idx, t15_idx, t15_idx); + code += mulmodx(t15_idx, t15_idx, t15_idx); + code += mulmodx(t15_idx, t15_idx, t15_idx); + code += mulmodx(t15_idx, t15_idx, t15_idx); + code += mulmodx(t15_idx, t15_idx, t15_idx); + code += mulmodx(t15_idx, t15_idx, t15_idx); + code += mulmodx(t15_idx, t15_idx, t15_idx); + code += mulmodx(t15_idx, t15_idx, t15_idx); + code += mulmodx(t15_idx, t15_idx, t15_idx); + code += mulmodx(t15_idx, t15_idx, t15_idx); + code += mulmodx(t15_idx, t15_idx, t15_idx); + code += mulmodx(t15_idx, t15_idx, t15_idx); + + // Step 149: t14 = x^0x60c89ce5c263405370a08b6d0302b + code += mulmodx(t14_idx, t14_idx, t15_idx); + + // Step 161: t14 = x^0x60c89ce5c263405370a08b6d0302b000 + // for (int i = 0; i < 12; ++i) + code += mulmodx(t14_idx, t14_idx, t14_idx); + code += mulmodx(t14_idx, t14_idx, t14_idx); + code += mulmodx(t14_idx, t14_idx, t14_idx); + code += mulmodx(t14_idx, t14_idx, t14_idx); + code += mulmodx(t14_idx, t14_idx, t14_idx); + code += mulmodx(t14_idx, t14_idx, t14_idx); + code += mulmodx(t14_idx, t14_idx, t14_idx); + code += mulmodx(t14_idx, t14_idx, t14_idx); + code += mulmodx(t14_idx, t14_idx, t14_idx); + code += mulmodx(t14_idx, t14_idx, t14_idx); + code += mulmodx(t14_idx, t14_idx, t14_idx); + code += mulmodx(t14_idx, t14_idx, t14_idx); + + // Step 162: t13 = x^0x60c89ce5c263405370a08b6d0302b0bb + code += mulmodx(t13_idx, t13_idx, t14_idx); + + // Step 170: t13 = x^0x60c89ce5c263405370a08b6d0302b0bb00 + // for (int i = 0; i < 8; ++i) + code += mulmodx(t13_idx, t13_idx, t13_idx); + code += mulmodx(t13_idx, t13_idx, t13_idx); + code += mulmodx(t13_idx, t13_idx, t13_idx); + code += mulmodx(t13_idx, t13_idx, t13_idx); + code += mulmodx(t13_idx, t13_idx, t13_idx); + code += mulmodx(t13_idx, t13_idx, t13_idx); + code += mulmodx(t13_idx, t13_idx, t13_idx); + code += mulmodx(t13_idx, t13_idx, t13_idx); + + // Step 171: t12 = x^0x60c89ce5c263405370a08b6d0302b0bb2f + code += mulmodx(t12_idx, t12_idx, t13_idx); + + // Step 185: t12 = x^0x183227397098d014dc2822db40c0ac2ecbc000 + // for (int i = 0; i < 14; ++i) + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + code += mulmodx(t12_idx, t12_idx, t12_idx); + + + // Step 186: t11 = x^0x183227397098d014dc2822db40c0ac2ecbc0b5 + code += mulmodx(t11_idx, t11_idx, t12_idx); + + // Step 195: t11 = x^0x30644e72e131a029b85045b68181585d97816a00 + // for (int i = 0; i < 9; ++i) + code += mulmodx(t11_idx, t11_idx, t11_idx); + code += mulmodx(t11_idx, t11_idx, t11_idx); + code += mulmodx(t11_idx, t11_idx, t11_idx); + code += mulmodx(t11_idx, t11_idx, t11_idx); + code += mulmodx(t11_idx, t11_idx, t11_idx); + code += mulmodx(t11_idx, t11_idx, t11_idx); + code += mulmodx(t11_idx, t11_idx, t11_idx); + code += mulmodx(t11_idx, t11_idx, t11_idx); + code += mulmodx(t11_idx, t11_idx, t11_idx); + + // Step 196: t10 = x^0x30644e72e131a029b85045b68181585d97816a91 + code += mulmodx(t10_idx, t10_idx, t11_idx); + + // Step 201: t10 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d5220 + // for (int i = 0; i < 5; ++i) + code += mulmodx(t10_idx, t10_idx, t10_idx); + code += mulmodx(t10_idx, t10_idx, t10_idx); + code += mulmodx(t10_idx, t10_idx, t10_idx); + code += mulmodx(t10_idx, t10_idx, t10_idx); + code += mulmodx(t10_idx, t10_idx, t10_idx); + + // Step 202: t9 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d + code += mulmodx(t9_idx, t9_idx, t10_idx); + + // Step 214: t9 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d000 + // for (int i = 0; i < 12; ++i) + code += mulmodx(t9_idx, t9_idx, t9_idx); + code += mulmodx(t9_idx, t9_idx, t9_idx); + code += mulmodx(t9_idx, t9_idx, t9_idx); + code += mulmodx(t9_idx, t9_idx, t9_idx); + code += mulmodx(t9_idx, t9_idx, t9_idx); + code += mulmodx(t9_idx, t9_idx, t9_idx); + code += mulmodx(t9_idx, t9_idx, t9_idx); + code += mulmodx(t9_idx, t9_idx, t9_idx); + code += mulmodx(t9_idx, t9_idx, t9_idx); + code += mulmodx(t9_idx, t9_idx, t9_idx); + code += mulmodx(t9_idx, t9_idx, t9_idx); + code += mulmodx(t9_idx, t9_idx, t9_idx); + + // Step 215: t8 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e3 + code += mulmodx(t8_idx, t8_idx, t9_idx); + + // Step 223: t8 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e300 + // for (int i = 0; i < 8; ++i) + code += mulmodx(t8_idx, t8_idx, t8_idx); + code += mulmodx(t8_idx, t8_idx, t8_idx); + code += mulmodx(t8_idx, t8_idx, t8_idx); + code += mulmodx(t8_idx, t8_idx, t8_idx); + code += mulmodx(t8_idx, t8_idx, t8_idx); + code += mulmodx(t8_idx, t8_idx, t8_idx); + code += mulmodx(t8_idx, t8_idx, t8_idx); + code += mulmodx(t8_idx, t8_idx, t8_idx); + + // Step 224: t7 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e395 + code += mulmodx(t7_idx, t7_idx, t8_idx); + + // Step 235: t7 = x^0x30644e72e131a029b85045b68181585d97816a916871ca800 + // for (int i = 0; i < 11; ++i) + code += mulmodx(t7_idx, t7_idx, t7_idx); + code += mulmodx(t7_idx, t7_idx, t7_idx); + code += mulmodx(t7_idx, t7_idx, t7_idx); + code += mulmodx(t7_idx, t7_idx, t7_idx); + code += mulmodx(t7_idx, t7_idx, t7_idx); + code += mulmodx(t7_idx, t7_idx, t7_idx); + code += mulmodx(t7_idx, t7_idx, t7_idx); + code += mulmodx(t7_idx, t7_idx, t7_idx); + code += mulmodx(t7_idx, t7_idx, t7_idx); + code += mulmodx(t7_idx, t7_idx, t7_idx); + code += mulmodx(t7_idx, t7_idx, t7_idx); + + // Step 236: t6 = x^0x30644e72e131a029b85045b68181585d97816a916871ca8d3 + code += mulmodx(t6_idx, t6_idx, t7_idx); + + // Step 243: t6 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e546980 + // for (int i = 0; i < 7; ++i) + code += mulmodx(t6_idx, t6_idx, t6_idx); + code += mulmodx(t6_idx, t6_idx, t6_idx); + code += mulmodx(t6_idx, t6_idx, t6_idx); + code += mulmodx(t6_idx, t6_idx, t6_idx); + code += mulmodx(t6_idx, t6_idx, t6_idx); + code += mulmodx(t6_idx, t6_idx, t6_idx); + code += mulmodx(t6_idx, t6_idx, t6_idx); + + // Step 244: t5 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e1 + code += mulmodx(t5_idx, t5_idx, t6_idx); + + // Step 255: t5 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f0800 + // for (int i = 0; i < 11; ++i) + code += mulmodx(t5_idx, t5_idx, t5_idx); + code += mulmodx(t5_idx, t5_idx, t5_idx); + code += mulmodx(t5_idx, t5_idx, t5_idx); + code += mulmodx(t5_idx, t5_idx, t5_idx); + code += mulmodx(t5_idx, t5_idx, t5_idx); + code += mulmodx(t5_idx, t5_idx, t5_idx); + code += mulmodx(t5_idx, t5_idx, t5_idx); + code += mulmodx(t5_idx, t5_idx, t5_idx); + code += mulmodx(t5_idx, t5_idx, t5_idx); + code += mulmodx(t5_idx, t5_idx, t5_idx); + code += mulmodx(t5_idx, t5_idx, t5_idx); + + // Step 256: t4 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f0823 + code += mulmodx(t4_idx, t4_idx, t5_idx); + + // Step 268: t4 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f0823000 + // for (int i = 0; i < 12; ++i) + code += mulmodx(t4_idx, t4_idx, t4_idx); + code += mulmodx(t4_idx, t4_idx, t4_idx); + code += mulmodx(t4_idx, t4_idx, t4_idx); + code += mulmodx(t4_idx, t4_idx, t4_idx); + code += mulmodx(t4_idx, t4_idx, t4_idx); + code += mulmodx(t4_idx, t4_idx, t4_idx); + code += mulmodx(t4_idx, t4_idx, t4_idx); + code += mulmodx(t4_idx, t4_idx, t4_idx); + code += mulmodx(t4_idx, t4_idx, t4_idx); + code += mulmodx(t4_idx, t4_idx, t4_idx); + code += mulmodx(t4_idx, t4_idx, t4_idx); + code += mulmodx(t4_idx, t4_idx, t4_idx); + + // Step 269: t3 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b + code += mulmodx(t3_idx, t3_idx, t4_idx); + + // Step 278: t3 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b600 + // for (int i = 0; i < 9; ++i) + code += mulmodx(t3_idx, t3_idx, t3_idx); + code += mulmodx(t3_idx, t3_idx, t3_idx); + code += mulmodx(t3_idx, t3_idx, t3_idx); + code += mulmodx(t3_idx, t3_idx, t3_idx); + code += mulmodx(t3_idx, t3_idx, t3_idx); + code += mulmodx(t3_idx, t3_idx, t3_idx); + code += mulmodx(t3_idx, t3_idx, t3_idx); + code += mulmodx(t3_idx, t3_idx, t3_idx); + code += mulmodx(t3_idx, t3_idx, t3_idx); + + // Step 279: t2 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3 + code += mulmodx(t2_idx, t2_idx, t3_idx); + + // Step 287: t2 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c300 + // for (int i = 0; i < 8; ++i) + code += mulmodx(t2_idx, t2_idx, t2_idx); + code += mulmodx(t2_idx, t2_idx, t2_idx); + code += mulmodx(t2_idx, t2_idx, t2_idx); + code += mulmodx(t2_idx, t2_idx, t2_idx); + code += mulmodx(t2_idx, t2_idx, t2_idx); + code += mulmodx(t2_idx, t2_idx, t2_idx); + code += mulmodx(t2_idx, t2_idx, t2_idx); + code += mulmodx(t2_idx, t2_idx, t2_idx); + + // Step 288: t1 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7 + code += mulmodx(t1_idx, t1_idx, t2_idx); + + // Step 295: t1 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f380 + // for (int i = 0; i < 7; ++i) + code += mulmodx(t1_idx, t1_idx, t1_idx); + code += mulmodx(t1_idx, t1_idx, t1_idx); + code += mulmodx(t1_idx, t1_idx, t1_idx); + code += mulmodx(t1_idx, t1_idx, t1_idx); + code += mulmodx(t1_idx, t1_idx, t1_idx); + code += mulmodx(t1_idx, t1_idx, t1_idx); + code += mulmodx(t1_idx, t1_idx, t1_idx); + + // Step 296: t0 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f5 + code += mulmodx(t0_idx, t0_idx, t1_idx); + + // Step 302: t0 = x^0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd40 + /// for (int i = 0; i < 6; ++i) + code += mulmodx(t0_idx, t0_idx, t0_idx); + code += mulmodx(t0_idx, t0_idx, t0_idx); + code += mulmodx(t0_idx, t0_idx, t0_idx); + code += mulmodx(t0_idx, t0_idx, t0_idx); + code += mulmodx(t0_idx, t0_idx, t0_idx); + code += mulmodx(t0_idx, t0_idx, t0_idx); + + // Step 303: z = x^0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45 + code += mulmodx(z_idx, z_idx, t0_idx); + + return code; +} + +} // namespace + +TEST_P(evm, exec_bn254_ecadd_test) +{ + using namespace evmone::test; + + if (evm::is_advanced()) + return; + + evm::rev = EVMC_PRAGUE; /// TODO: Use EVMC_EVMMAX + + constexpr auto size = sizeof(uint256); + + const auto x1 = 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2_u256; + const auto y1 = 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba_u256; + const auto x2 = 0x1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc286_u256; + const auto y2 = 0x0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4_u256; + + uint8_t calldata[6 * size]; // mod, x1, y1, x2, y2, b3 + intx::be::unsafe::store(&calldata[0], BN254Mod); + intx::be::unsafe::store(&calldata[size], x1); + intx::be::unsafe::store(&calldata[2 * size], y1); + intx::be::unsafe::store(&calldata[3 * size], x2); + intx::be::unsafe::store(&calldata[4 * size], y2); + intx::be::unsafe::store(&calldata[5 * size], 9_u256); + + ValueSlotsRegister vs_reg; + SlotsScope scope(vs_reg); + + const auto x3_idx = scope.register_slot(); + const auto y3_idx = scope.register_slot(); + const auto z3_idx = scope.register_slot(); + + // Stores inputs in evm memory and `1` to load it as `z` coordinates of the inputs in projective + // space + auto code_init = calldatacopy(push(0), push(0), push(size * 6)) + mstore(size * 6, push(1)); + + auto code = bytecode{}; + { + SlotsScope scope1(vs_reg); + const auto x1_idx = scope1.register_slot(); + const auto y1_idx = scope1.register_slot(); + const auto z1_idx = scope1.register_slot(); + const auto x2_idx = scope1.register_slot(); + const auto y2_idx = scope1.register_slot(); + const auto z2_idx = scope1.register_slot(); + const auto b3_idx = scope1.register_slot(); + + code += storex(2, size, x1_idx) + storex(1, size * 6, z1_idx); // [x1, y1, 1] + code += + storex(2, size * 3, x2_idx) + storex(1, size * 6, z2_idx); // [x1, y1, 1, x2, y2, 1] + code += storex(1, size * 5, b3_idx); + + code += bn254_ecadd( + vs_reg, x3_idx, y3_idx, z3_idx, x1_idx, y1_idx, z1_idx, x2_idx, y2_idx, z2_idx, b3_idx); + } + { + SlotsScope scope2(vs_reg); + const auto z_inv_idx = scope2.register_slot(); + + code += field_inv_bn254(vs_reg, z_inv_idx, z3_idx); + + code += mulmodx(x3_idx, x3_idx, z_inv_idx); + code += mulmodx(y3_idx, y3_idx, z_inv_idx); + } + //////////////////////////////////////////////////////// + + code += loadx(2, x3_idx, size * 6); + // return loaded result + code += ret(size * 6, size * 2); + + execute( + 1000, code_init + setupx(vs_reg.max_slots_used(), size, 0, 1) + code, {calldata, size * 6}); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + + ASSERT_EQ(result.output_size, size * 2); + EXPECT_EQ(hex({result.output_data, result.output_size}), + "1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49018683193ae021a2f8920fed18" + "6cde5d9b1365116865281ccf884c1f28b1df8f"); +}