diff --git a/lib/assigner/evmone/instructions.hpp b/lib/assigner/evmone/instructions.hpp index 47e21f5..77a1c7b 100644 --- a/lib/assigner/evmone/instructions.hpp +++ b/lib/assigner/evmone/instructions.hpp @@ -336,9 +336,9 @@ struct instructions { state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-3, state.rw_trace.size(), false, stack[2])); state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); - const auto& x = stack[0]; - const auto& y = stack[1]; - auto& m = stack[2]; + const auto& x = stack.pop(); + const auto& y = stack.pop(); + auto& m = stack.top(); m = x.mulmod(y, m); state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } @@ -966,8 +966,8 @@ struct instructions { if (!check_memory(gas_left, state.memory, index, 1)) return {EVMC_OUT_OF_GAS, gas_left}; - const auto addr = (int)index.to_uint64(); - state.memory[addr] = value.to_uint64(); + const auto addr = index.to_uint64(); + value.template store(&state.memory[addr]); for(uint64_t j = 0; j < 8; j++){ state.rw_trace.push_back(nil::evm_assigner::memory_operation(state.call_id, addr + j, state.rw_trace.size(), true, state.memory[addr + j])); } diff --git a/lib/assigner/include/bytecode.hpp b/lib/assigner/include/bytecode.hpp index 500ce22..0f4ac38 100644 --- a/lib/assigner/include/bytecode.hpp +++ b/lib/assigner/include/bytecode.hpp @@ -87,7 +87,7 @@ namespace nil { } else { // BYTE bytecode_table.witness(TAG, start_row_index + cur) = 1; - bytecode_table.witness(INDEX, start_row_index + cur) = j-1; + bytecode_table.witness(INDEX, start_row_index + cur) = j-1; // TODO: why j-1? Index is 0 for both j == 0 and j == 1 bytecode_table.witness(LENGTH_LEFT, start_row_index + cur) = prev_length - 1; prev_length = prev_length - 1; if (push_size == 0) { diff --git a/lib/assigner/include/zkevm_word.hpp b/lib/assigner/include/zkevm_word.hpp index c8fc0d7..8533064 100644 --- a/lib/assigner/include/zkevm_word.hpp +++ b/lib/assigner/include/zkevm_word.hpp @@ -38,6 +38,10 @@ namespace nil { value = v; } + zkevm_word(uint32_t v) { + value = v; + } + zkevm_word(int64_t v) { value = v; } @@ -50,6 +54,10 @@ namespace nil { value = v; } + zkevm_word(uint64_t v0,uint64_t v1,uint64_t v2,uint64_t v3) { + value = {v0, v1, v2, v3}; + } + zkevm_word(const evmc::uint256be& v) { value = intx::be::load(v); } diff --git a/lib/assigner/test/assigner_test.cpp b/lib/assigner/test/assigner_test.cpp index d0d3e0e..ccac0c7 100644 --- a/lib/assigner/test/assigner_test.cpp +++ b/lib/assigner/test/assigner_test.cpp @@ -10,6 +10,8 @@ #include +const uint8_t global_input_variable_for_test[13] = "Hello World!"; + class AssignerTest : public testing::Test { public: @@ -32,7 +34,6 @@ class AssignerTest : public testing::Test assigner_ptr = std::make_shared>(assignments); - const uint8_t input[] = "Hello World!"; const evmc_uint256be value = {{1, 0}}; const evmc_address sender_addr = {{0, 1, 2}}; const evmc_address recipient_addr = {{1, 1, 2}}; @@ -51,8 +52,8 @@ class AssignerTest : public testing::Test .gas = gas, .recipient = recipient_addr, .sender = sender_addr, - .input_data = input, - .input_size = sizeof(input), + .input_data = global_input_variable_for_test, + .input_size = sizeof(global_input_variable_for_test), .value = value, .create2_salt = {0}, .code_address = code_addr @@ -77,6 +78,35 @@ class AssignerTest : public testing::Test static struct evmc_message msg; }; +template +struct TestRWCircuitStruct { + std::vector code; + const evmone::bytes_view container; + const evmone::baseline::CodeAnalysis code_analysis; + const evmone::bytes_view data; + evmone::ExecutionState state; + evmone::baseline::Position position; + + TestRWCircuitStruct( + const uint8_t opcode, + AssignerTest* testInstance + ) : + code{opcode}, + container(code.data(), code.size()), + code_analysis(evmone::baseline::analyze(testInstance->rev, container)), + data(code_analysis.eof_header.get_data(container)), + state( + testInstance->msg, + testInstance->rev, + *(testInstance->host_interface), + testInstance->ctx, + container, + data, + 0, + testInstance->assigner_ptr), + position(code.data(), state.stack_space.bottom()) {} +}; + std::shared_ptr> AssignerTest::assigner_ptr; std::vector> @@ -92,7 +122,7 @@ inline void check_eq(const uint8_t* l, const uint8_t* r, size_t len) { } } -inline void rw_circuit_check(const std::vector> assignments, +inline void rw_circuit_check(const std::vector>& assignments, uint32_t start_row_index, uint8_t operation_type, uint32_t call_id, @@ -105,6 +135,7 @@ inline void rw_circuit_check(const std::vector::RW_TABLE_INDEX]; + std::cout << "check values on row " << start_row_index << " \n"; // OP_TYPE EXPECT_EQ(rw_table.witness(0, start_row_index), operation_type); // CALL ID @@ -141,6 +172,75 @@ inline std::string bytes_to_string(const uint8_t *data, int len) return res; } +inline void stack_rw_circuit_check(const std::vector>& assignments, + uint32_t start_row_index, + const typename AssignerTest::BlueprintFieldType::value_type& address, + uint32_t rw_id, + bool is_write, + const typename AssignerTest::BlueprintFieldType::value_type& value_hi, + const typename AssignerTest::BlueprintFieldType::value_type& value_lo) { + rw_circuit_check(assignments, start_row_index, 1/*STACK_OP*/, 0/*CALL_ID*/, address/*address in stack*/, 0/*storage key hi*/, 0/*storage key lo*/, rw_id/*trace size*/, is_write/*is_write*/, value_hi/*value_hi*/, value_lo/*value_lo*/); +} + +inline void memory_rw_circuit_check(const std::vector>& assignments, + uint32_t start_row_index, + const typename AssignerTest::BlueprintFieldType::value_type& address, + uint32_t rw_id, + bool is_write, + const typename AssignerTest::BlueprintFieldType::value_type& value_hi, + const typename AssignerTest::BlueprintFieldType::value_type& value_lo) { + rw_circuit_check(assignments, start_row_index, 2/*MEMORY_OP*/, 0/*CALL_ID*/, address/*address in memory*/, 0/*storage key hi*/, 0/*storage key lo*/, rw_id/*trace size*/, is_write/*is_write*/, value_hi/*value_hi*/, value_lo/*value_lo*/); +} + +inline void bytecode_circuit_check(const std::vector>& assignments, + uint32_t start_row_index, + const typename AssignerTest::BlueprintFieldType::value_type& tag, + const typename AssignerTest::BlueprintFieldType::value_type& index, + const typename AssignerTest::BlueprintFieldType::value_type& value, + const typename AssignerTest::BlueprintFieldType::value_type& is_opcode, + const typename AssignerTest::BlueprintFieldType::value_type& push_size, + const typename AssignerTest::BlueprintFieldType::value_type& length_left, + const typename AssignerTest::BlueprintFieldType::value_type& hash_hi, + const typename AssignerTest::BlueprintFieldType::value_type& hash_lo, + const typename AssignerTest::BlueprintFieldType::value_type& value_rlc, + const typename AssignerTest::BlueprintFieldType::value_type& rlc_challenge +) { + EXPECT_EQ(assignments[0].witness(0, start_row_index), tag); + EXPECT_EQ(assignments[0].witness(1, start_row_index), index); + EXPECT_EQ(assignments[0].witness(2, start_row_index), value); + EXPECT_EQ(assignments[0].witness(3, start_row_index), is_opcode); + EXPECT_EQ(assignments[0].witness(4, start_row_index), push_size); + EXPECT_EQ(assignments[0].witness(5, start_row_index), length_left); + EXPECT_EQ(assignments[0].witness(6, start_row_index), hash_hi); + EXPECT_EQ(assignments[0].witness(7, start_row_index), hash_lo); + EXPECT_EQ(assignments[0].witness(8, start_row_index), value_rlc); + EXPECT_EQ(assignments[0].witness(9, start_row_index), rlc_challenge); +} + +TEST_F(AssignerTest, mul_bytecode) { + + std::vector code = { + evmone::OP_PUSH1, + 4, + evmone::OP_PUSH1, + 8, + evmone::OP_MUL, + }; + + assigner_ptr->handle_bytecode(code.size(), code.data()); + + const typename AssignerTest::BlueprintFieldType::value_type hash_hi = 0xa496800ccbfa34512f79b5fc4faea679_cppui_modular255; + const typename AssignerTest::BlueprintFieldType::value_type hash_lo = 0x544dfa344b6d9f3923362f5615959272_cppui_modular255; + + uint32_t start_row_index = 0; + + bytecode_circuit_check(assignments, start_row_index++, 0/*TAG*/, 0/*INDEX*/, 96/*VALUE*/, 0/*IS_OPCODE*/, 0/*PUSH_SIZE*/, 96/*LENGTH_LEFT*/, hash_hi/*HASH_HI*/, hash_lo/*HASH_LO*/, 0 /*VALUE_RLC*/, 15/*RLC_CHALLENGE*/); + bytecode_circuit_check(assignments, start_row_index++, 1/*TAG*/, 0/*INDEX*/, 4 /*VALUE*/, 1/*IS_OPCODE*/, 0/*PUSH_SIZE*/, 95/*LENGTH_LEFT*/, hash_hi/*HASH_HI*/, hash_lo/*HASH_LO*/, 4 /*VALUE_RLC*/, 15/*RLC_CHALLENGE*/); + bytecode_circuit_check(assignments, start_row_index++, 1/*TAG*/, 1/*INDEX*/, 96/*VALUE*/, 1/*IS_OPCODE*/, 1/*PUSH_SIZE*/, 94/*LENGTH_LEFT*/, hash_hi/*HASH_HI*/, hash_lo/*HASH_LO*/, 156 /*VALUE_RLC*/, 15/*RLC_CHALLENGE*/); + bytecode_circuit_check(assignments, start_row_index++, 1/*TAG*/, 2/*INDEX*/, 8 /*VALUE*/, 0/*IS_OPCODE*/, 0/*PUSH_SIZE*/, 93/*LENGTH_LEFT*/, hash_hi/*HASH_HI*/, hash_lo/*HASH_LO*/, 2348 /*VALUE_RLC*/, 15/*RLC_CHALLENGE*/); + bytecode_circuit_check(assignments, start_row_index++, 1/*TAG*/, 3/*INDEX*/, 2 /*VALUE*/, 1/*IS_OPCODE*/, 0/*PUSH_SIZE*/, 92/*LENGTH_LEFT*/, hash_hi/*HASH_HI*/, hash_lo/*HASH_LO*/, 35222/*VALUE_RLC*/, 15/*RLC_CHALLENGE*/); +} + TEST_F(AssignerTest, conversions_uint256be_to_zkevm_word) { evmc::uint256be uint256be_number; @@ -315,37 +415,661 @@ TEST_F(AssignerTest, set_val) ASSERT_EQ(tmp.get_value(), expected_val); } -TEST_F(AssignerTest, mul) { +template +inline void test_stack_rw_circuit_1( + void (*instr_fn)(evmone::StackTop, evmone::ExecutionState&) noexcept, + uint8_t opcode, + typename nil::evm_assigner::zkevm_word inp, + typename nil::evm_assigner::zkevm_word expected, + AssignerTest* testInstance +) { - std::vector code = { - evmone::OP_PUSH1, - 4, - evmone::OP_PUSH1, - 8, - evmone::OP_MUL, - }; + TestRWCircuitStruct wrapper(opcode, testInstance); + *(++wrapper.position.stack_top) = inp; + instr_fn(wrapper.position.stack_top, wrapper.state); + testInstance->assigner_ptr->handle_rw(wrapper.state.rw_trace); - nil::evm_assigner::evaluate(host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); + stack_rw_circuit_check(testInstance->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, false/*is_write*/, inp.w_hi() /*value_hi*/, inp.w_lo() /*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, 1/*row*/, 0/*address in stack*/, 1/*trace size*/, true /*is_write*/, expected.w_hi()/*value_hi*/, expected.w_lo()/*value_lo*/); +} - uint32_t start_row_index = 0; - uint32_t call_id = 0; - //PUSH - rw_circuit_check(assignments, start_row_index, 1/*STACK_OP*/, call_id, 0/*address in stack*/, 0/*storage key hi*/, 0/*storage key lo*/, - 0/*trace size*/, true/*is_write*/, 0/*value_hi*/, 4/*value_lo*/); - //MUL - rw_circuit_check(assignments, start_row_index + 1, 1/*STACK_OP*/, call_id, 0/*address in stack*/, 0/*storage key hi*/, 0/*storage key lo*/, - 2/*trace size*/, false/*is_write*/, 0/*value_hi*/, 4/*value_lo*/); - //MUL - rw_circuit_check(assignments, start_row_index + 2, 1/*STACK_OP*/, call_id, 0/*address in stack*/, 0/*storage key hi*/, 0/*storage key lo*/, - 4/*trace size*/, true/*is_write*/, 0/*value_hi*/, 32/*value_lo*/); - //PUSH - rw_circuit_check(assignments, start_row_index + 3, 1/*STACK_OP*/, call_id, 1/*address in stack*/, 0/*storage key hi*/, 0/*storage key lo*/, - 1/*trace size*/, true/*is_write*/, 0/*value_hi*/, 8/*value_lo*/); - //MUL - rw_circuit_check(assignments, start_row_index + 4, 1/*STACK_OP*/, call_id, 1/*address in stack*/, 0/*storage key hi*/, 0/*storage key lo*/, - 3/*trace size*/, false/*is_write*/, 0/*value_hi*/, 8/*value_lo*/); +template +inline void test_stack_rw_circuit_2( + void (*instr_fn)(evmone::StackTop, evmone::ExecutionState&) noexcept, + uint8_t opcode, + typename nil::evm_assigner::zkevm_word inp1, + typename nil::evm_assigner::zkevm_word inp2, + typename nil::evm_assigner::zkevm_word expected, + AssignerTest* testInstance +) { + + TestRWCircuitStruct wrapper(opcode, testInstance); + *(++wrapper.position.stack_top) = inp1; + *(++wrapper.position.stack_top) = inp2; + instr_fn(wrapper.position.stack_top, wrapper.state); + testInstance->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(testInstance->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, false/*is_write*/, inp1.w_hi() /*value_hi*/, inp1.w_lo() /*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, 1/*row*/, 0/*address in stack*/, 2/*trace size*/, true /*is_write*/, expected.w_hi()/*value_hi*/, expected.w_lo()/*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, 2/*row*/, 1/*address in stack*/, 1/*trace size*/, false/*is_write*/, inp2.w_hi() /*value_hi*/, inp2.w_lo() /*value_lo*/); +} + +template +inline void test_stack_rw_circuit_exp( + evmone::Result (*instr_fn)(evmone::StackTop, int64_t, evmone::ExecutionState&) noexcept, + uint8_t opcode, + typename nil::evm_assigner::zkevm_word inp1, + typename nil::evm_assigner::zkevm_word inp2, + typename nil::evm_assigner::zkevm_word expected, + AssignerTest* testInstance +) { + + TestRWCircuitStruct wrapper(opcode, testInstance); + *(++wrapper.position.stack_top) = inp1; + *(++wrapper.position.stack_top) = inp2; + instr_fn(wrapper.position.stack_top, testInstance->msg.gas, wrapper.state); + testInstance->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(testInstance->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, false/*is_write*/, inp1.w_hi() /*value_hi*/, inp1.w_lo() /*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, 1/*row*/, 0/*address in stack*/, 2/*trace size*/, true /*is_write*/, expected.w_hi()/*value_hi*/, expected.w_lo()/*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, 2/*row*/, 1/*address in stack*/, 1/*trace size*/, false/*is_write*/, inp2.w_hi() /*value_hi*/, inp2.w_lo() /*value_lo*/); +} + +template +inline void test_stack_rw_circuit_keccak( + evmone::Result (*instr_fn)(evmone::StackTop, int64_t, evmone::ExecutionState&) noexcept, + uint8_t opcode, + typename nil::evm_assigner::zkevm_word inp1, + typename nil::evm_assigner::zkevm_word inp2, + AssignerTest* testInstance +) { + std::vector code = {opcode}; + + const evmone::bytes_view container{code.data(), code.size()}; + const auto code_analysis = evmone::baseline::analyze(testInstance->rev, container); + const auto data = code_analysis.eof_header.get_data(container); + evmone::ExecutionState state( + testInstance->msg, + testInstance->rev, + *(testInstance->host_interface), + testInstance->ctx, + container, + data, + 0, + testInstance->assigner_ptr); + evmone::baseline::Position position{code.data(), state.stack_space.bottom()}; + + position.stack_top++; + *position.stack_top = inp1; + position.stack_top++; + *position.stack_top = inp2; + + const auto i = inp2.to_uint64(); + const auto s = inp1.to_uint64(); + + assert(s != 0); + + uint8_t keccak_input[32]; + for (std::size_t j = 0; j < 32; j++) { keccak_input[j] = 255; } + + (&state.memory)->grow(32); + + uint8_t* mem_data = &state.memory[i]; + for (std::size_t j = 0; j < 32; j++) { mem_data[j] = 255; } + + nil::evm_assigner::zkevm_word expected = nil::evm_assigner::zkevm_word(ethash::keccak256(keccak_input, s)); + + instr_fn(position.stack_top, testInstance->msg.gas, state); + + testInstance->assigner_ptr->handle_rw(state.rw_trace); + + uint32_t row = 0; + stack_rw_circuit_check(testInstance->assignments, row++/*row*/, 0/*address in stack*/, 0/*trace size*/, false/*is_write*/, inp1.w_hi() /*value_hi*/, inp1.w_lo() /*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, row++/*row*/, 0/*address in stack*/, s == 0 ? 2 : 2+32/*trace size*/, true /*is_write*/, expected.w_hi()/*value_hi*/, expected.w_lo()/*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, row++/*row*/, 1/*address in stack*/, 1/*trace size*/, false/*is_write*/, inp2.w_hi() /*value_hi*/, inp2.w_lo() /*value_lo*/); + if (s != 0 ) { + for(uint32_t j = 0; j < 32; j++){ + memory_rw_circuit_check(testInstance->assignments, row++, (inp2 + j).w_lo(), 2 + j, false, 0, mem_data[j]); + } + } + // TODO: what if s == 0 +} + + +template +inline void test_stack_rw_circuit_3( + void (*instr_fn)(evmone::StackTop, evmone::ExecutionState&) noexcept, + uint8_t opcode, + typename nil::evm_assigner::zkevm_word inp1, + typename nil::evm_assigner::zkevm_word inp2, + typename nil::evm_assigner::zkevm_word inp3, + typename nil::evm_assigner::zkevm_word expected, + AssignerTest* testInstance +) { + + TestRWCircuitStruct wrapper(opcode, testInstance); + *(++wrapper.position.stack_top) = inp1; + *(++wrapper.position.stack_top) = inp2; + *(++wrapper.position.stack_top) = inp3; + instr_fn(wrapper.position.stack_top, wrapper.state); + testInstance->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(testInstance->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, false/*is_write*/, inp1.w_hi() /*value_hi*/, inp1.w_lo() /*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, 1/*row*/, 0/*address in stack*/, 3/*trace size*/, true /*is_write*/, expected.w_hi()/*value_hi*/, expected.w_lo()/*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, 2/*row*/, 1/*address in stack*/, 1/*trace size*/, false/*is_write*/, inp2.w_hi() /*value_hi*/, inp2.w_lo() /*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, 3/*row*/, 2/*address in stack*/, 2/*trace size*/, false/*is_write*/, inp3.w_hi() /*value_hi*/, inp3.w_lo() /*value_lo*/); + +} + +template +typename nil::evm_assigner::zkevm_word expected_result_1 ( + uint8_t opcode, + typename nil::evm_assigner::zkevm_word inp +) { + switch(opcode) { + case evmone::OP_NOT: return ~inp; + case evmone::OP_ISZERO: return inp == 0; + default: std::abort(); + } +} + +template +typename nil::evm_assigner::zkevm_word expected_result_2 ( + uint8_t opcode, + typename nil::evm_assigner::zkevm_word inp1, + typename nil::evm_assigner::zkevm_word inp2 +) { + switch(opcode) { + case evmone::OP_ADD: return inp2 + inp1; + case evmone::OP_MUL: return inp2 * inp1; + case evmone::OP_SUB: return inp2 - inp1; + case evmone::OP_DIV: return inp1 != 0 ? inp2 / inp1 : 0; + case evmone::OP_SDIV: return inp2.sdiv(inp1); + case evmone::OP_MOD: return inp1 != 0 ? inp2 % inp1 : 0; + case evmone::OP_SMOD: return inp2.smod(inp1); + case evmone::OP_SIGNEXTEND: { + + nil::evm_assigner::zkevm_word res(inp1); + if (inp2<31) + { + const auto e = inp2.to_uint64(); // uint256 -> uint64. + const auto sign_word_index = + static_cast(e / sizeof(e)); // Index of the word with the sign bit. + const auto sign_byte_index = e % sizeof(e); // Index of the sign byte in the sign word. + auto sign_word = inp1.to_uint64(sign_word_index); + + const auto sign_byte_offset = sign_byte_index * 8; + const auto sign_byte = sign_word >> sign_byte_offset; // Move sign byte to position 0. + + // Sign-extend the "sign" byte and move it to the right position. Value bits are zeros. + const auto sext_byte = static_cast(int64_t{static_cast(sign_byte)}); + const auto sext = sext_byte << sign_byte_offset; + + const auto sign_mask = ~uint64_t{0} << sign_byte_offset; + const auto value = sign_word & ~sign_mask; // Reset extended bytes. + sign_word = sext | value; // Combine the result word. + + // Produce bits (all zeros or ones) for extended words. This is done by SAR of + // the sign-extended byte. Shift by any value 7-63 would work. + const auto sign_ex = static_cast(static_cast(sext_byte) >> 8); + + for (size_t i = 3; i > sign_word_index; --i) + res.set_val(sign_ex, i); // Clear extended words. + } + return res; + } + case evmone::OP_LT: return inp2 < inp1; + case evmone::OP_GT: return inp1 < inp2; + case evmone::OP_SLT: return inp2.slt(inp1); + case evmone::OP_SGT: return inp1.slt(inp2); + case evmone::OP_EQ: return inp1 == inp2; + case evmone::OP_AND: return inp1 & inp2; + case evmone::OP_OR: return inp1 | inp2; + case evmone::OP_XOR: return inp1 ^ inp2; + case evmone::OP_BYTE: { + const bool n_valid = inp2 < 32; + const uint64_t byte_mask = (n_valid ? 0xff : 0); + const auto index = 31 - static_cast(inp2.to_uint64() % 32); + const auto word = inp1.to_uint64(index / 8); + const auto byte_index = index % 8; + const auto byte = (word >> (byte_index * 8)) & byte_mask; + return nil::evm_assigner::zkevm_word(byte); + } + case evmone::OP_SHL: return inp1 << inp2; + case evmone::OP_SHR: return inp1 >> inp2; + case evmone::OP_SAR: { + const bool is_neg = inp1.to_uint64(3) < 0; // Inspect the top bit (words are LE). + const nil::evm_assigner::zkevm_word sign_mask = is_neg ? 1 : 0; + const auto mask_shift = (inp2< 256) ? (256 - inp2.to_uint64(0)) : 0; + return (inp1 >> inp2) | (sign_mask << mask_shift); + } + default: std::abort(); + } } +template +typename nil::evm_assigner::zkevm_word expected_result_3 ( + uint8_t opcode, + typename nil::evm_assigner::zkevm_word inp1, + typename nil::evm_assigner::zkevm_word inp2, + typename nil::evm_assigner::zkevm_word inp3 +) { + switch(opcode) { + case evmone::OP_ADDMOD: return inp3.addmod(inp2, inp1); + case evmone::OP_MULMOD: return inp3.mulmod(inp2, inp1); + default: std::abort(); + } +} + + +#define TEST_STACK_1_INPUTS(INSTR, OP_CODE, INP) \ +TEST_F(AssignerTest, INSTR ## _ ## INP) { \ + test_stack_rw_circuit_1( \ + evmone::instr::core::instructions::INSTR, \ + evmone::OP_CODE, \ + typename nil::evm_assigner::zkevm_word(INP), \ + expected_result_1(evmone::OP_CODE, typename nil::evm_assigner::zkevm_word(INP)), \ + this); \ +} + +#define TEST_STACK_2_INPUTS(INSTR, OP_CODE, INP1, INP2) \ +TEST_F(AssignerTest, INSTR ## _ ## INP1 ## _ ## INP2) { \ + test_stack_rw_circuit_2( \ + evmone::instr::core::instructions::INSTR, \ + evmone::OP_CODE, \ + typename nil::evm_assigner::zkevm_word(INP1), \ + typename nil::evm_assigner::zkevm_word(INP2), \ + expected_result_2(evmone::OP_CODE, typename nil::evm_assigner::zkevm_word(INP1), typename nil::evm_assigner::zkevm_word(INP2)), \ + this); \ +} + +#define TEST_STACK_3_INPUTS(INSTR, OP_CODE, INP1, INP2, INP3) \ +TEST_F(AssignerTest, INSTR ## _ ## INP1 ## _ ## INP2 ## _ ## INP3) { \ + test_stack_rw_circuit_3( \ + evmone::instr::core::instructions::INSTR, \ + evmone::OP_CODE, \ + typename nil::evm_assigner::zkevm_word(INP1), \ + typename nil::evm_assigner::zkevm_word(INP2), \ + typename nil::evm_assigner::zkevm_word(INP3), \ + expected_result_3(evmone::OP_CODE, typename nil::evm_assigner::zkevm_word(INP1), typename nil::evm_assigner::zkevm_word(INP2), typename nil::evm_assigner::zkevm_word(INP3)), \ + this); \ +} + +#define TEST_STACK_1_INPUTS_ALL_CASES(INSTR, OP_CODE) \ + TEST_STACK_1_INPUTS(INSTR, OP_CODE, 0) \ + TEST_STACK_1_INPUTS(INSTR, OP_CODE, 1) \ + TEST_STACK_1_INPUTS(INSTR, OP_CODE, 0xffffffffffffffff) + +#define TEST_STACK_2_INPUTS_ALL_CASES(INSTR, OP_CODE) \ + TEST_STACK_2_INPUTS(INSTR, OP_CODE, 0, 0) \ + TEST_STACK_2_INPUTS(INSTR, OP_CODE, 0, 1) \ + TEST_STACK_2_INPUTS(INSTR, OP_CODE, 1, 0) \ + TEST_STACK_2_INPUTS(INSTR, OP_CODE, 1, 1) \ + TEST_STACK_2_INPUTS(INSTR, OP_CODE, 0xffffffffffffffff, 3) + + + // OP_STOP // no trace, non-void +TEST_STACK_2_INPUTS_ALL_CASES(add, OP_ADD) +TEST_STACK_2_INPUTS_ALL_CASES(mul, OP_MUL) +TEST_STACK_2_INPUTS_ALL_CASES(sub, OP_SUB) +TEST_STACK_2_INPUTS_ALL_CASES(div, OP_DIV) +TEST_STACK_2_INPUTS_ALL_CASES(sdiv, OP_SDIV) +TEST_STACK_2_INPUTS_ALL_CASES(mod, OP_MOD) +TEST_STACK_2_INPUTS_ALL_CASES(smod, OP_SMOD) +TEST_STACK_3_INPUTS(addmod, OP_ADDMOD, 11, 22, 5) +TEST_STACK_3_INPUTS(mulmod, OP_MULMOD, 11, 3, 7) + +TEST_F(AssignerTest, test_exp) { \ + test_stack_rw_circuit_exp( \ + evmone::instr::core::instructions::exp, + evmone::OP_EXP, + typename nil::evm_assigner::zkevm_word(1), + typename nil::evm_assigner::zkevm_word(1), + typename nil::evm_assigner::zkevm_word(1).exp(typename nil::evm_assigner::zkevm_word(1)), + this); +} + +TEST_STACK_2_INPUTS_ALL_CASES(signextend, OP_SIGNEXTEND) + +TEST_STACK_2_INPUTS_ALL_CASES(lt, OP_LT) +TEST_STACK_2_INPUTS_ALL_CASES(gt, OP_GT) +TEST_STACK_2_INPUTS_ALL_CASES(slt, OP_SLT) +TEST_STACK_2_INPUTS_ALL_CASES(sgt, OP_SGT) +TEST_STACK_2_INPUTS_ALL_CASES(eq, OP_EQ) +TEST_STACK_1_INPUTS_ALL_CASES(iszero, OP_ISZERO) +TEST_STACK_2_INPUTS_ALL_CASES(and_, OP_AND) +TEST_STACK_2_INPUTS_ALL_CASES(or_, OP_OR) +TEST_STACK_2_INPUTS_ALL_CASES(xor_, OP_XOR) +TEST_STACK_1_INPUTS_ALL_CASES(not_, OP_NOT) +TEST_STACK_2_INPUTS_ALL_CASES(byte, OP_BYTE) +TEST_STACK_2_INPUTS_ALL_CASES(shl, OP_SHL) +TEST_STACK_2_INPUTS_ALL_CASES(shr, OP_SHR) +TEST_STACK_2_INPUTS_ALL_CASES(sar, OP_SAR) + +TEST_F(AssignerTest, test_keccak) { \ + test_stack_rw_circuit_keccak( \ + evmone::instr::core::instructions::keccak256, + evmone::OP_KECCAK256, + typename nil::evm_assigner::zkevm_word(32), + typename nil::evm_assigner::zkevm_word(0), + this); +} + +TEST_F(AssignerTest, test_op_address) { + TestRWCircuitStruct wrapper(evmone::OP_ADDRESS, this); + evmone::instr::core::instructions::address(wrapper.position.stack_top, wrapper.state); + this->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(this->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, true /*is_write*/, typename nil::evm_assigner::zkevm_word(this->msg.recipient).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(this->msg.recipient).w_lo()/*value_lo*/); +} + +TEST_F(AssignerTest, test_op_balance) { + + TestRWCircuitStruct wrapper(evmone::OP_BALANCE, this); + + nil::evm_assigner::zkevm_word x = 0; + const auto addr = x.to_address(); + nil::evm_assigner::zkevm_word result = nil::evm_assigner::zkevm_word(wrapper.state.host.get_balance(addr)); + + *(++wrapper.position.stack_top) = result; + + evmone::instr::core::instructions::balance(wrapper.position.stack_top, msg.gas, wrapper.state); + assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, false/*is_write*/, result.w_hi()/*value_hi*/, result.w_lo()/*value_lo*/); + stack_rw_circuit_check(assignments, 1/*row*/, 0/*address in stack*/, 1/*trace size*/, true /*is_write*/, result.w_hi()/*value_hi*/, result.w_lo()/*value_lo*/); + +} + +TEST_F(AssignerTest, test_op_origin) { + TestRWCircuitStruct wrapper(evmone::OP_ORIGIN, this); + evmone::instr::core::instructions::origin(wrapper.position.stack_top, wrapper.state); + this->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(this->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, true /*is_write*/, typename nil::evm_assigner::zkevm_word(wrapper.state.get_tx_context().tx_origin).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(wrapper.state.get_tx_context().tx_origin).w_lo()/*value_lo*/); +} + + +TEST_F(AssignerTest, test_op_caller) { + TestRWCircuitStruct wrapper(evmone::OP_CALLER, this); + evmone::instr::core::instructions::caller(wrapper.position.stack_top, wrapper.state); + this->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(this->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, true /*is_write*/, typename nil::evm_assigner::zkevm_word(wrapper.state.msg->sender).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(wrapper.state.msg->sender).w_lo()/*value_lo*/); +} + + +TEST_F(AssignerTest, test_op_callvalue) { + TestRWCircuitStruct wrapper(evmone::OP_CALLVALUE, this); + evmone::instr::core::instructions::callvalue(wrapper.position.stack_top, wrapper.state); + this->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(this->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, true /*is_write*/, typename nil::evm_assigner::zkevm_word(wrapper.state.msg->value).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(wrapper.state.msg->value).w_lo()/*value_lo*/); +} + +template +void test_calldataload ( + TestRWCircuitStruct& wrapper, + typename nil::evm_assigner::zkevm_word index, + AssignerTest* testInstance +) { + *(++wrapper.position.stack_top) = index; + const auto index_uint64 = index.to_uint64(); + typename nil::evm_assigner::zkevm_word result; + if (wrapper.state.msg->input_size < index_uint64) + typename nil::evm_assigner::zkevm_word result = 0; + else + { + const auto begin = index_uint64; + const auto end = std::min(begin + 32, wrapper.state.msg->input_size); + uint8_t data[32] = {}; + for (size_t i = 0; i < (end - begin); ++i) + data[i] = wrapper.state.msg->input_data[begin + i]; + + result = typename nil::evm_assigner::zkevm_word(data, 32); + } + + evmone::instr::core::instructions::calldataload(wrapper.position.stack_top, wrapper.state); + testInstance->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(testInstance->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, false/*is_write*/, typename nil::evm_assigner::zkevm_word(index).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(index).w_lo()/*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, 1/*row*/, 0/*address in stack*/, 1/*trace size*/, true /*is_write*/, result.w_hi()/*value_hi*/, result.w_lo()/*value_lo*/); +} + +TEST_F(AssignerTest, test_op_calldataload_0) { + TestRWCircuitStruct wrapper(evmone::OP_CALLDATALOAD, this); + test_calldataload(wrapper, 0, this); +} +TEST_F(AssignerTest, test_op_calldataload_1) { + TestRWCircuitStruct wrapper(evmone::OP_CALLDATALOAD, this); + typename nil::evm_assigner::zkevm_word input = + typename nil::evm_assigner::zkevm_word(wrapper.state.msg->input_size) + 1; + test_calldataload(wrapper, input, this); +} +TEST_F(AssignerTest, test_op_calldataload_2) { + TestRWCircuitStruct wrapper(evmone::OP_CALLDATALOAD, this); + typename nil::evm_assigner::zkevm_word input = + typename nil::evm_assigner::zkevm_word(wrapper.state.msg->input_size) / 2; + test_calldataload(wrapper, input, this); +} + +TEST_F(AssignerTest, test_op_calldatasize) { + TestRWCircuitStruct wrapper(evmone::OP_CALLDATASIZE, this); + evmone::instr::core::instructions::calldatasize(wrapper.position.stack_top, wrapper.state); + this->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(this->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, true /*is_write*/, typename nil::evm_assigner::zkevm_word(wrapper.state.msg->input_size).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(wrapper.state.msg->input_size).w_lo()/*value_lo*/); +} + +template +void test_calldatacopy ( + TestRWCircuitStruct& wrapper, + AssignerTest* testInstance +) { + typename nil::evm_assigner::zkevm_word inp1 = 13; + typename nil::evm_assigner::zkevm_word inp2 = 0; + typename nil::evm_assigner::zkevm_word inp3 = 0; + + *(++wrapper.position.stack_top) = inp1; + *(++wrapper.position.stack_top) = inp2; + *(++wrapper.position.stack_top) = inp3; + const auto& mem_index = inp3; + const auto& input_index = inp2; + const auto& size = inp1; + + (&wrapper.state.memory)->grow(32); + for (std::size_t i = 0; i < 32; i++) { + wrapper.state.memory[i] = 255; + } + + evmone::instr::core::instructions::calldatacopy(wrapper.position.stack_top, wrapper.state.msg->gas, wrapper.state); + testInstance->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(testInstance->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, false/*is_write*/, typename nil::evm_assigner::zkevm_word(inp1).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(inp1).w_lo()/*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, 1/*row*/, 1/*address in stack*/, 1/*trace size*/, false/*is_write*/, typename nil::evm_assigner::zkevm_word(inp2).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(inp2).w_lo()/*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, 2/*row*/, 2/*address in stack*/, 2/*trace size*/, false/*is_write*/, typename nil::evm_assigner::zkevm_word(inp3).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(inp3).w_lo()/*value_lo*/); + + memory_rw_circuit_check(testInstance->assignments, 3, 0, 3, true, 0, 'H'); + memory_rw_circuit_check(testInstance->assignments, 4, 1, 4, true, 0, 'e'); + memory_rw_circuit_check(testInstance->assignments, 5, 2, 5, true, 0, 'l'); + memory_rw_circuit_check(testInstance->assignments, 6, 3, 6, true, 0, 'l'); + memory_rw_circuit_check(testInstance->assignments, 7, 4, 7, true, 0, 'o'); + memory_rw_circuit_check(testInstance->assignments, 8, 5, 8, true, 0, ' '); + memory_rw_circuit_check(testInstance->assignments, 9, 6, 9, true, 0, 'W'); + memory_rw_circuit_check(testInstance->assignments, 10, 7, 10, true, 0, 'o'); + memory_rw_circuit_check(testInstance->assignments, 11, 8, 11, true, 0, 'r'); + memory_rw_circuit_check(testInstance->assignments, 12, 9, 12, true, 0, 'l'); + memory_rw_circuit_check(testInstance->assignments, 13, 10, 13, true, 0, 'd'); + memory_rw_circuit_check(testInstance->assignments, 14, 11, 14, true, 0, '!'); +} + +TEST_F(AssignerTest, test_op_calldatacopy_1) { + TestRWCircuitStruct wrapper(evmone::OP_CALLDATACOPY, this); + test_calldatacopy(wrapper, this); +} + +TEST_F(AssignerTest, test_op_codesize) { + TestRWCircuitStruct wrapper(evmone::OP_CODESIZE, this); + evmone::instr::core::instructions::codesize(wrapper.position.stack_top, wrapper.state); + this->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(this->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, true /*is_write*/, 0/*value_hi*/, typename nil::evm_assigner::zkevm_word(wrapper.state.original_code.size()).w_lo()/*value_lo*/); +} + + + // OP_CODECOPY // contains TODOs, skipping for now + +TEST_F(AssignerTest, test_op_gasprice) { + TestRWCircuitStruct wrapper(evmone::OP_GASPRICE, this); + evmone::instr::core::instructions::gasprice(wrapper.position.stack_top, wrapper.state); + this->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(this->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, true /*is_write*/, 0/*value_hi*/, typename nil::evm_assigner::zkevm_word(wrapper.state.get_tx_context().tx_gas_price).w_lo()/*value_lo*/); +} + + // OP_EXTCODESIZE // which address should be used? + // OP_EXTCODECOPY // contains TODOs + +TEST_F(AssignerTest, test_op_returndatasize) { + TestRWCircuitStruct wrapper(evmone::OP_RETURNDATASIZE, this); + evmone::instr::core::instructions::returndatasize(wrapper.position.stack_top, wrapper.state); + this->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + stack_rw_circuit_check(this->assignments, 0/*row*/, 0/*address in stack*/, 0/*trace size*/, true /*is_write*/, 0/*value_hi*/, typename nil::evm_assigner::zkevm_word(wrapper.state.return_data.size()).w_lo()/*value_lo*/); +} + + // OP_RETURNDATACOPY = how returndata is filled and what it should contain? skipped for now + // OP_EXTCODEHASH // get_code_hash(addr), which address shodld use? + + // OP_BLOCKHASH // gettxcontext, filled with which data? + // OP_COINBASE // gettxcontext + // OP_TIMESTAMP // gettxcontext + // OP_NUMBER // gettxcontext + // OP_PREVRANDAO // gettxcontext + // OP_GASLIMIT // gettxcontext + // OP_CHAINID // gettxcontext + // OP_SELFBALANCE // contains TODO + // OP_BASEFEE // gettxcontext + // OP_BLOBHASH // gettxcontext + // OP_BLOBBASEFEE // gettxcontext + + // OP_POP // no trace + +template +void test_mload ( + TestRWCircuitStruct& wrapper, + typename nil::evm_assigner::zkevm_word index, + AssignerTest* testInstance +) { + + *(++wrapper.position.stack_top) = index; + const auto addr = index.to_uint64(); + + uint8_t expected_mem[64]; + for (std::uint8_t i = 0; i < 64; i++) { + expected_mem[i] = i; + } + + (&wrapper.state.memory)->grow(64); + for (std::uint8_t i = 0; i < 64; i++) { + wrapper.state.memory[i] = i; + } + + const auto& wordsize = nil::evm_assigner::zkevm_word::size; + + nil::evm_assigner::zkevm_word result = + nil::evm_assigner::zkevm_word(&wrapper.state.memory[addr], nil::evm_assigner::zkevm_word::size); + + evmone::instr::core::instructions::template mload(wrapper.position.stack_top, wrapper.state.msg->gas, wrapper.state); + testInstance->assigner_ptr->handle_rw(wrapper.state.rw_trace); + + std::uint32_t row = 0; + stack_rw_circuit_check(testInstance->assignments, row++ /*row*/, 0/*address in stack*/, 0 /*trace size*/, false/*is_write*/, typename nil::evm_assigner::zkevm_word(index).w_hi() /*value_hi*/, typename nil::evm_assigner::zkevm_word(index).w_lo() /*value_lo*/); + stack_rw_circuit_check(testInstance->assignments, row++ /*row*/, 0/*address in stack*/, 1 + wordsize/*trace size*/, true /*is_write*/, typename nil::evm_assigner::zkevm_word(result).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(result).w_lo()/*value_lo*/); + + for (std::uint32_t i = 0; i < wordsize; i++) { + memory_rw_circuit_check(testInstance->assignments, row++ , addr + i, 1 + i, false, 0, expected_mem[addr + i]); + } + +} + +TEST_F(AssignerTest, test_op_mload_0) { + TestRWCircuitStruct wrapper(evmone::OP_MLOAD, this); + test_mload(wrapper, 0, this); +} + +TEST_F(AssignerTest, test_op_mload_32) { + TestRWCircuitStruct wrapper(evmone::OP_MLOAD, this); + test_mload(wrapper, 32, this); +} + +TEST_F(AssignerTest, test_op_mstore) { + TestRWCircuitStruct wrapper(evmone::OP_MSTORE, this); + + typename nil::evm_assigner::zkevm_word value = {0x18191a1b1c1d1e1f, 0x1011121314151617, 0x08090a0b0c0d0e0f, 0x0001020304050607}; + typename nil::evm_assigner::zkevm_word index = 0; + + *(++wrapper.position.stack_top) = value; + *(++wrapper.position.stack_top) = index; + const auto addr = index.to_uint64(); + + uint8_t expected_mem[32]; + for (int i = 0; i < 32; i++) { + expected_mem[i] = i; + } + (&wrapper.state.memory)->grow(32); + + evmone::instr::core::instructions::template mstore(wrapper.position.stack_top, wrapper.state.msg->gas, wrapper.state); + assigner_ptr->handle_rw(wrapper.state.rw_trace); + + std::uint32_t row = 0; + + stack_rw_circuit_check(assignments, row++ /*row*/, 0/*address in stack*/, 0/*trace size*/, false/*is_write*/, typename nil::evm_assigner::zkevm_word(value).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(value).w_lo()/*value_lo*/); + stack_rw_circuit_check(assignments, row++ /*row*/, 1/*address in stack*/, 1/*trace size*/, false/*is_write*/, typename nil::evm_assigner::zkevm_word(index).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(index).w_lo()/*value_lo*/); + + for (std::uint32_t i = 0; i < 32; i++) { + memory_rw_circuit_check(assignments, row++ , addr + i, 2 + i, true, 0, expected_mem[i]); + } +} + +TEST_F(AssignerTest, test_op_mstore8) { + TestRWCircuitStruct wrapper(evmone::OP_MSTORE8, this); + + typename nil::evm_assigner::zkevm_word value = 0x0102030405060708; + typename nil::evm_assigner::zkevm_word index = 0; + + *(++wrapper.position.stack_top) = value; + *(++wrapper.position.stack_top) = index; + const auto addr = index.to_uint64(); + + uint8_t expected_mem[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + (&wrapper.state.memory)->grow(8); + + evmone::instr::core::instructions::mstore8(wrapper.position.stack_top, wrapper.state.msg->gas, wrapper.state); + assigner_ptr->handle_rw(wrapper.state.rw_trace); + + std::uint32_t row = 0; + + stack_rw_circuit_check(assignments, row++ /*row*/, 0/*address in stack*/, 0/*trace size*/, false/*is_write*/, typename nil::evm_assigner::zkevm_word(value).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(value).w_lo()/*value_lo*/); + stack_rw_circuit_check(assignments, row++ /*row*/, 1/*address in stack*/, 1/*trace size*/, false/*is_write*/, typename nil::evm_assigner::zkevm_word(index).w_hi()/*value_hi*/, typename nil::evm_assigner::zkevm_word(index).w_lo()/*value_lo*/); + + for (std::uint32_t i = 0; i < 8; i++) { + memory_rw_circuit_check(assignments, row++ , addr + i, 2 + i, true, 0, expected_mem[i]); + } +} + + // next opcode is OP_SLOAD = 0x54, + + +#undef TEST_STACK_1_INPUTS +#undef TEST_STACK_1_INPUTS_ALL_CASES +#undef TEST_STACK_2_INPUTS +#undef TEST_STACK_2_INPUTS_ALL_CASES +#undef TEST_STACK_3_INPUTS +#undef TEST_STACK_2_INPUTS_ALL_CASES + + // TODO add check assignment tables TEST_F(AssignerTest, DISABLED_callvalue_calldataload) {