diff --git a/avm-transpiler/src/instructions.rs b/avm-transpiler/src/instructions.rs index 9df6f20551c6..b49753c63579 100644 --- a/avm-transpiler/src/instructions.rs +++ b/avm-transpiler/src/instructions.rs @@ -7,6 +7,7 @@ use crate::opcodes::AvmOpcode; pub const ALL_DIRECT: u8 = 0b00000000; pub const ZEROTH_OPERAND_INDIRECT: u8 = 0b00000001; pub const FIRST_OPERAND_INDIRECT: u8 = 0b00000010; +pub const SECOND_OPERAND_INDIRECT: u8 = 0b00000100; pub const ZEROTH_FIRST_OPERANDS_INDIRECT: u8 = ZEROTH_OPERAND_INDIRECT | FIRST_OPERAND_INDIRECT; /// A simple representation of an AVM instruction for the purpose diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index e4a09137776f..a5ca11a5a470 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -7,7 +7,7 @@ use acvm::brillig_vm::brillig::{ use crate::instructions::{ AvmInstruction, AvmOperand, AvmTypeTag, ALL_DIRECT, FIRST_OPERAND_INDIRECT, - ZEROTH_OPERAND_INDIRECT, + SECOND_OPERAND_INDIRECT, ZEROTH_OPERAND_INDIRECT, }; use crate::opcodes::AvmOpcode; use crate::utils::{dbg_print_avm_program, dbg_print_brillig_program}; @@ -256,9 +256,11 @@ fn handle_foreign_call( } "avmOpcodePoseidon" => { handle_single_field_hash_instruction(avm_instrs, function, destinations, inputs) - } + }, + "storageRead" => handle_storage_read(avm_instrs, destinations, inputs), + "storageWrite" => handle_storage_write(avm_instrs, destinations, inputs), // Getters. - _ if inputs.len() == 0 && destinations.len() == 1 => { + _ if inputs.is_empty() && destinations.len() == 1 => { handle_getter_instruction(avm_instrs, function, destinations, inputs) } // Anything else. @@ -314,7 +316,7 @@ fn handle_emit_unencrypted_log( destinations: &Vec, inputs: &Vec, ) { - if destinations.len() != 0 || inputs.len() != 2 { + if !destinations.is_empty() || inputs.len() != 2 { panic!( "Transpiler expects ForeignCall::EMITUNENCRYPTEDLOG to have 0 destinations and 3 inputs, got {} and {}", destinations.len(), @@ -361,7 +363,7 @@ fn handle_emit_note_hash_or_nullifier( "EMITNOTEHASH" }; - if destinations.len() != 0 || inputs.len() != 1 { + if !destinations.is_empty() || inputs.len() != 1 { panic!( "Transpiler expects ForeignCall::{} to have 0 destinations and 1 input, got {} and {}", function_name, @@ -804,6 +806,86 @@ fn handle_black_box_function(avm_instrs: &mut Vec, operation: &B ), } } +/// Emit a storage write opcode +/// The current implementation writes an array of values into storage ( contiguous slots in memory ) +fn handle_storage_write( + avm_instrs: &mut Vec, + destinations: &Vec, + inputs: &Vec, +) { + assert!(inputs.len() == 2); + assert!(destinations.len() == 1); + + let slot_offset_maybe = inputs[0]; + let slot_offset = match slot_offset_maybe { + ValueOrArray::MemoryAddress(slot_offset) => slot_offset.0, + _ => panic!("ForeignCall address destination should be a single value"), + }; + + let src_offset_maybe = inputs[1]; + let (src_offset, src_size) = match src_offset_maybe { + ValueOrArray::HeapArray(HeapArray { pointer, size }) => (pointer.0, size), + _ => panic!("Storage write address inputs should be an array of values"), + }; + + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::SSTORE, + indirect: Some(ZEROTH_OPERAND_INDIRECT), + operands: vec![ + AvmOperand::U32 { + value: src_offset as u32, + }, + AvmOperand::U32 { + value: src_size as u32, + }, + AvmOperand::U32 { + value: slot_offset as u32, + }, + ], + ..Default::default() + }) +} + +/// Emit a storage read opcode +/// The current implementation reads an array of values from storage ( contiguous slots in memory ) +fn handle_storage_read( + avm_instrs: &mut Vec, + destinations: &Vec, + inputs: &Vec, +) { + // For the foreign calls we want to handle, we do not want inputs, as they are getters + assert!(inputs.len() == 2); // output, len - but we dont use this len - its for the oracle + assert!(destinations.len() == 1); + + let slot_offset_maybe = inputs[0]; + let slot_offset = match slot_offset_maybe { + ValueOrArray::MemoryAddress(slot_offset) => slot_offset.0, + _ => panic!("ForeignCall address destination should be a single value"), + }; + + let dest_offset_maybe = destinations[0]; + let (dest_offset, src_size) = match dest_offset_maybe { + ValueOrArray::HeapArray(HeapArray { pointer, size }) => (pointer.0, size), + _ => panic!("Storage write address inputs should be an array of values"), + }; + + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::SLOAD, + indirect: Some(SECOND_OPERAND_INDIRECT), + operands: vec![ + AvmOperand::U32 { + value: slot_offset as u32, + }, + AvmOperand::U32 { + value: src_size as u32, + }, + AvmOperand::U32 { + value: dest_offset as u32, + }, + ], + ..Default::default() + }) +} /// Compute an array that maps each Brillig pc to an AVM pc. /// This must be done before transpiling to properly transpile jump destinations. diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/avm_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/avm_flavor.hpp index 4387175c7990..06c9fe3159ab 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/avm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/avm_flavor.hpp @@ -45,7 +45,7 @@ class AvmFlavor { static constexpr size_t NUM_ALL_ENTITIES = 91; using Relations = - std::tuple, Avm_vm::avm_main, Avm_vm::avm_mem, equiv_inter_reg_alu_relation>; + std::tuple, Avm_vm::avm_mem, Avm_vm::avm_main, equiv_inter_reg_alu_relation>; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); @@ -316,20 +316,20 @@ class AvmFlavor { equiv_inter_reg_alu, equiv_tag_err, equiv_tag_err_counts, - avm_alu_alu_u16_r2_shift, - avm_alu_alu_u16_r1_shift, - avm_alu_alu_u16_r6_shift, - avm_alu_alu_u16_r5_shift, avm_alu_alu_u16_r4_shift, - avm_alu_alu_u16_r0_shift, + avm_alu_alu_u16_r5_shift, + avm_alu_alu_u16_r2_shift, avm_alu_alu_u16_r7_shift, + avm_alu_alu_u16_r0_shift, avm_alu_alu_u16_r3_shift, - avm_main_pc_shift, - avm_main_internal_return_ptr_shift, + avm_alu_alu_u16_r6_shift, + avm_alu_alu_u16_r1_shift, avm_mem_m_val_shift, avm_mem_m_rw_shift, avm_mem_m_tag_shift, - avm_mem_m_addr_shift) + avm_mem_m_addr_shift, + avm_main_internal_return_ptr_shift, + avm_main_pc_shift) RefVector get_wires() { @@ -410,20 +410,20 @@ class AvmFlavor { equiv_inter_reg_alu, equiv_tag_err, equiv_tag_err_counts, - avm_alu_alu_u16_r2_shift, - avm_alu_alu_u16_r1_shift, - avm_alu_alu_u16_r6_shift, - avm_alu_alu_u16_r5_shift, avm_alu_alu_u16_r4_shift, - avm_alu_alu_u16_r0_shift, + avm_alu_alu_u16_r5_shift, + avm_alu_alu_u16_r2_shift, avm_alu_alu_u16_r7_shift, + avm_alu_alu_u16_r0_shift, avm_alu_alu_u16_r3_shift, - avm_main_pc_shift, - avm_main_internal_return_ptr_shift, + avm_alu_alu_u16_r6_shift, + avm_alu_alu_u16_r1_shift, avm_mem_m_val_shift, avm_mem_m_rw_shift, avm_mem_m_tag_shift, - avm_mem_m_addr_shift }; + avm_mem_m_addr_shift, + avm_main_internal_return_ptr_shift, + avm_main_pc_shift }; }; RefVector get_unshifted() { @@ -507,23 +507,37 @@ class AvmFlavor { }; RefVector get_to_be_shifted() { - return { avm_alu_alu_u16_r2, avm_alu_alu_u16_r1, - avm_alu_alu_u16_r6, avm_alu_alu_u16_r5, - avm_alu_alu_u16_r4, avm_alu_alu_u16_r0, - avm_alu_alu_u16_r7, avm_alu_alu_u16_r3, - avm_main_pc, avm_main_internal_return_ptr, - avm_mem_m_val, avm_mem_m_rw, - avm_mem_m_tag, avm_mem_m_addr }; + return { avm_alu_alu_u16_r4, + avm_alu_alu_u16_r5, + avm_alu_alu_u16_r2, + avm_alu_alu_u16_r7, + avm_alu_alu_u16_r0, + avm_alu_alu_u16_r3, + avm_alu_alu_u16_r6, + avm_alu_alu_u16_r1, + avm_mem_m_val, + avm_mem_m_rw, + avm_mem_m_tag, + avm_mem_m_addr, + avm_main_internal_return_ptr, + avm_main_pc }; }; RefVector get_shifted() { - return { avm_alu_alu_u16_r2_shift, avm_alu_alu_u16_r1_shift, - avm_alu_alu_u16_r6_shift, avm_alu_alu_u16_r5_shift, - avm_alu_alu_u16_r4_shift, avm_alu_alu_u16_r0_shift, - avm_alu_alu_u16_r7_shift, avm_alu_alu_u16_r3_shift, - avm_main_pc_shift, avm_main_internal_return_ptr_shift, - avm_mem_m_val_shift, avm_mem_m_rw_shift, - avm_mem_m_tag_shift, avm_mem_m_addr_shift }; + return { avm_alu_alu_u16_r4_shift, + avm_alu_alu_u16_r5_shift, + avm_alu_alu_u16_r2_shift, + avm_alu_alu_u16_r7_shift, + avm_alu_alu_u16_r0_shift, + avm_alu_alu_u16_r3_shift, + avm_alu_alu_u16_r6_shift, + avm_alu_alu_u16_r1_shift, + avm_mem_m_val_shift, + avm_mem_m_rw_shift, + avm_mem_m_tag_shift, + avm_mem_m_addr_shift, + avm_main_internal_return_ptr_shift, + avm_main_pc_shift }; }; }; @@ -536,13 +550,20 @@ class AvmFlavor { RefVector get_to_be_shifted() { - return { avm_alu_alu_u16_r2, avm_alu_alu_u16_r1, - avm_alu_alu_u16_r6, avm_alu_alu_u16_r5, - avm_alu_alu_u16_r4, avm_alu_alu_u16_r0, - avm_alu_alu_u16_r7, avm_alu_alu_u16_r3, - avm_main_pc, avm_main_internal_return_ptr, - avm_mem_m_val, avm_mem_m_rw, - avm_mem_m_tag, avm_mem_m_addr }; + return { avm_alu_alu_u16_r4, + avm_alu_alu_u16_r5, + avm_alu_alu_u16_r2, + avm_alu_alu_u16_r7, + avm_alu_alu_u16_r0, + avm_alu_alu_u16_r3, + avm_alu_alu_u16_r6, + avm_alu_alu_u16_r1, + avm_mem_m_val, + avm_mem_m_rw, + avm_mem_m_tag, + avm_mem_m_addr, + avm_main_internal_return_ptr, + avm_main_pc }; }; // The plookup wires that store plookup read data. diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/avm_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/avm_circuit_builder.hpp index ba405ef0b0d0..c33c5b3f2062 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/avm_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/generated/avm_circuit_builder.hpp @@ -98,20 +98,20 @@ template struct AvmFullRow { FF equiv_inter_reg_alu{}; FF equiv_tag_err{}; FF equiv_tag_err_counts{}; - FF avm_alu_alu_u16_r2_shift{}; - FF avm_alu_alu_u16_r1_shift{}; - FF avm_alu_alu_u16_r6_shift{}; - FF avm_alu_alu_u16_r5_shift{}; FF avm_alu_alu_u16_r4_shift{}; - FF avm_alu_alu_u16_r0_shift{}; + FF avm_alu_alu_u16_r5_shift{}; + FF avm_alu_alu_u16_r2_shift{}; FF avm_alu_alu_u16_r7_shift{}; + FF avm_alu_alu_u16_r0_shift{}; FF avm_alu_alu_u16_r3_shift{}; - FF avm_main_pc_shift{}; - FF avm_main_internal_return_ptr_shift{}; + FF avm_alu_alu_u16_r6_shift{}; + FF avm_alu_alu_u16_r1_shift{}; FF avm_mem_m_val_shift{}; FF avm_mem_m_rw_shift{}; FF avm_mem_m_tag_shift{}; FF avm_mem_m_addr_shift{}; + FF avm_main_internal_return_ptr_shift{}; + FF avm_main_pc_shift{}; }; class AvmCircuitBuilder { @@ -220,20 +220,20 @@ class AvmCircuitBuilder { polys.equiv_tag_err_counts[i] = rows[i].equiv_tag_err_counts; } - polys.avm_alu_alu_u16_r2_shift = Polynomial(polys.avm_alu_alu_u16_r2.shifted()); - polys.avm_alu_alu_u16_r1_shift = Polynomial(polys.avm_alu_alu_u16_r1.shifted()); - polys.avm_alu_alu_u16_r6_shift = Polynomial(polys.avm_alu_alu_u16_r6.shifted()); - polys.avm_alu_alu_u16_r5_shift = Polynomial(polys.avm_alu_alu_u16_r5.shifted()); polys.avm_alu_alu_u16_r4_shift = Polynomial(polys.avm_alu_alu_u16_r4.shifted()); - polys.avm_alu_alu_u16_r0_shift = Polynomial(polys.avm_alu_alu_u16_r0.shifted()); + polys.avm_alu_alu_u16_r5_shift = Polynomial(polys.avm_alu_alu_u16_r5.shifted()); + polys.avm_alu_alu_u16_r2_shift = Polynomial(polys.avm_alu_alu_u16_r2.shifted()); polys.avm_alu_alu_u16_r7_shift = Polynomial(polys.avm_alu_alu_u16_r7.shifted()); + polys.avm_alu_alu_u16_r0_shift = Polynomial(polys.avm_alu_alu_u16_r0.shifted()); polys.avm_alu_alu_u16_r3_shift = Polynomial(polys.avm_alu_alu_u16_r3.shifted()); - polys.avm_main_pc_shift = Polynomial(polys.avm_main_pc.shifted()); - polys.avm_main_internal_return_ptr_shift = Polynomial(polys.avm_main_internal_return_ptr.shifted()); + polys.avm_alu_alu_u16_r6_shift = Polynomial(polys.avm_alu_alu_u16_r6.shifted()); + polys.avm_alu_alu_u16_r1_shift = Polynomial(polys.avm_alu_alu_u16_r1.shifted()); polys.avm_mem_m_val_shift = Polynomial(polys.avm_mem_m_val.shifted()); polys.avm_mem_m_rw_shift = Polynomial(polys.avm_mem_m_rw.shifted()); polys.avm_mem_m_tag_shift = Polynomial(polys.avm_mem_m_tag.shifted()); polys.avm_mem_m_addr_shift = Polynomial(polys.avm_mem_m_addr.shifted()); + polys.avm_main_internal_return_ptr_shift = Polynomial(polys.avm_main_internal_return_ptr.shifted()); + polys.avm_main_pc_shift = Polynomial(polys.avm_main_pc.shifted()); return polys; } @@ -309,14 +309,14 @@ class AvmCircuitBuilder { Avm_vm::get_relation_label_avm_alu)) { return false; } - if (!evaluate_relation.template operator()>("avm_main", - Avm_vm::get_relation_label_avm_main)) { - return false; - } if (!evaluate_relation.template operator()>("avm_mem", Avm_vm::get_relation_label_avm_mem)) { return false; } + if (!evaluate_relation.template operator()>("avm_main", + Avm_vm::get_relation_label_avm_main)) { + return false; + } if (!evaluate_logderivative.template operator()>("equiv_inter_reg_alu")) { return false; diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_alu.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_alu.hpp index 0d637d26295e..6c98b4125645 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_alu.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_alu.hpp @@ -7,60 +7,66 @@ namespace bb::Avm_vm { template struct Avm_aluRow { + FF avm_alu_alu_u128_tag{}; + FF avm_alu_alu_u16_r6{}; + FF avm_alu_alu_u16_tag{}; FF avm_alu_alu_cf{}; + FF avm_alu_alu_u16_r4_shift{}; + FF avm_alu_alu_u16_r5_shift{}; + FF avm_alu_alu_sel{}; FF avm_alu_alu_op_mul{}; - FF avm_alu_alu_op_eq{}; - FF avm_alu_alu_u8_r1{}; - FF avm_alu_alu_u16_r2_shift{}; - FF avm_alu_alu_in_tag{}; FF avm_alu_alu_u64_tag{}; + FF avm_alu_alu_u16_r2{}; + FF avm_alu_alu_ib{}; + FF avm_alu_alu_u16_r2_shift{}; + FF avm_alu_alu_u16_r7_shift{}; FF avm_alu_alu_u16_r5{}; - FF avm_alu_alu_u16_r1_shift{}; - FF avm_alu_alu_ff_tag{}; - FF avm_alu_alu_u16_r4{}; - FF avm_alu_alu_u16_r6_shift{}; + FF avm_alu_alu_u8_r1{}; + FF avm_alu_alu_in_tag{}; + FF avm_alu_alu_u16_r3{}; FF avm_alu_alu_u32_tag{}; + FF avm_alu_alu_ff_tag{}; + FF avm_alu_alu_u16_r1{}; FF avm_alu_alu_u16_r0{}; - FF avm_alu_alu_op_eq_diff_inv{}; - FF avm_alu_alu_ic{}; - FF avm_alu_alu_u16_r5_shift{}; - FF avm_alu_alu_op_not{}; - FF avm_alu_alu_u128_tag{}; - FF avm_alu_alu_u16_r7{}; - FF avm_alu_alu_u16_r4_shift{}; - FF avm_alu_alu_u16_r6{}; FF avm_alu_alu_u16_r0_shift{}; - FF avm_alu_alu_u16_r7_shift{}; - FF avm_alu_alu_sel{}; - FF avm_alu_alu_u16_r1{}; + FF avm_alu_alu_op_eq_diff_inv{}; + FF avm_alu_alu_op_add{}; FF avm_alu_alu_op_sub{}; - FF avm_alu_alu_ib{}; + FF avm_alu_alu_op_not{}; FF avm_alu_alu_u16_r3_shift{}; + FF avm_alu_alu_op_eq{}; FF avm_alu_alu_u8_r0{}; - FF avm_alu_alu_u16_tag{}; - FF avm_alu_alu_op_add{}; - FF avm_alu_alu_u8_tag{}; + FF avm_alu_alu_u16_r6_shift{}; FF avm_alu_alu_u64_r0{}; - FF avm_alu_alu_u16_r2{}; + FF avm_alu_alu_ic{}; + FF avm_alu_alu_u16_r7{}; + FF avm_alu_alu_u8_tag{}; + FF avm_alu_alu_u16_r1_shift{}; + FF avm_alu_alu_u16_r4{}; FF avm_alu_alu_ia{}; - FF avm_alu_alu_u16_r3{}; }; inline std::string get_relation_label_avm_alu(int index) { switch (index) { + case 19: + return "ALU_RES_IS_BOOL"; + + case 12: + return "ALU_MUL_COMMON_1"; + case 13: return "ALU_MUL_COMMON_2"; - case 11: - return "ALU_MULTIPLICATION_FF"; - - case 16: - return "ALU_MULTIPLICATION_OUT_U128"; + case 17: + return "ALU_FF_NOT_XOR"; case 18: return "ALU_OP_NOT"; + case 16: + return "ALU_MULTIPLICATION_OUT_U128"; + case 20: return "ALU_OP_EQ"; @@ -70,14 +76,8 @@ inline std::string get_relation_label_avm_alu(int index) case 10: return "ALU_ADD_SUB_2"; - case 12: - return "ALU_MUL_COMMON_1"; - - case 19: - return "ALU_RES_IS_BOOL"; - - case 17: - return "ALU_FF_NOT_XOR"; + case 11: + return "ALU_MULTIPLICATION_FF"; } return std::to_string(index); } diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_main.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_main.hpp index cfa91475426c..24df5d3d40c8 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_main.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_main.hpp @@ -7,64 +7,64 @@ namespace bb::Avm_vm { template struct Avm_mainRow { - FF avm_main_sel_op_not{}; - FF avm_main_sel_op_sub{}; - FF avm_main_ia{}; FF avm_main_sel_op_mul{}; + FF avm_main_rwa{}; + FF avm_main_mem_op_b{}; + FF avm_main_sel_internal_return{}; + FF avm_main_internal_return_ptr_shift{}; + FF avm_main_mem_idx_b{}; + FF avm_main_ia{}; FF avm_main_pc{}; FF avm_main_alu_sel{}; + FF avm_main_first{}; + FF avm_main_sel_op_not{}; + FF avm_main_inv{}; + FF avm_main_tag_err{}; + FF avm_main_sel_op_eq{}; + FF avm_main_ib{}; + FF avm_main_sel_internal_call{}; + FF avm_main_rwc{}; FF avm_main_op_err{}; - FF avm_main_mem_op_b{}; - FF avm_main_mem_op_a{}; - FF avm_main_pc_shift{}; - FF avm_main_rwa{}; + FF avm_main_mem_idx_a{}; + FF avm_main_internal_return_ptr{}; + FF avm_main_sel_jump{}; FF avm_main_sel_halt{}; + FF avm_main_ic{}; FF avm_main_sel_op_add{}; - FF avm_main_sel_internal_call{}; - FF avm_main_mem_op_c{}; FF avm_main_rwb{}; - FF avm_main_internal_return_ptr{}; - FF avm_main_rwc{}; - FF avm_main_inv{}; - FF avm_main_sel_jump{}; - FF avm_main_sel_internal_return{}; - FF avm_main_mem_idx_b{}; - FF avm_main_mem_idx_a{}; FF avm_main_sel_op_div{}; - FF avm_main_ic{}; - FF avm_main_first{}; - FF avm_main_tag_err{}; - FF avm_main_internal_return_ptr_shift{}; - FF avm_main_ib{}; - FF avm_main_sel_op_eq{}; + FF avm_main_mem_op_a{}; + FF avm_main_mem_op_c{}; + FF avm_main_sel_op_sub{}; + FF avm_main_pc_shift{}; }; inline std::string get_relation_label_avm_main(int index) { switch (index) { - case 19: - return "SUBOP_DIVISION_ZERO_ERR1"; - case 20: return "SUBOP_DIVISION_ZERO_ERR2"; - case 21: - return "SUBOP_ERROR_RELEVANT_OP"; - case 34: return "PC_INCREMENT"; - case 18: - return "SUBOP_DIVISION_FF"; - case 29: return "RETURN_POINTER_DECREMENT"; + case 19: + return "SUBOP_DIVISION_ZERO_ERR1"; + + case 18: + return "SUBOP_DIVISION_FF"; + case 35: return "INTERNAL_RETURN_POINTER_CONSISTENCY"; case 23: return "RETURN_POINTER_INCREMENT"; + + case 21: + return "SUBOP_ERROR_RELEVANT_OP"; } return std::to_string(index); } diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_mem.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_mem.hpp index b8c613401234..97125695b326 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_mem.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/avm/avm_mem.hpp @@ -7,41 +7,41 @@ namespace bb::Avm_vm { template struct Avm_memRow { - FF avm_mem_m_val{}; - FF avm_mem_m_addr{}; - FF avm_mem_m_last{}; - FF avm_mem_m_val_shift{}; - FF avm_mem_m_tag_err{}; - FF avm_mem_m_lastAccess{}; - FF avm_mem_m_tag{}; FF avm_mem_m_rw{}; + FF avm_mem_m_in_tag{}; + FF avm_mem_m_lastAccess{}; + FF avm_mem_m_val_shift{}; + FF avm_mem_m_val{}; FF avm_mem_m_rw_shift{}; + FF avm_mem_m_tag_err{}; FF avm_mem_m_tag_shift{}; - FF avm_mem_m_in_tag{}; - FF avm_mem_m_addr_shift{}; + FF avm_mem_m_tag{}; FF avm_mem_m_one_min_inv{}; + FF avm_mem_m_addr{}; + FF avm_mem_m_last{}; + FF avm_mem_m_addr_shift{}; }; inline std::string get_relation_label_avm_mem(int index) { switch (index) { - case 9: - return "MEM_IN_TAG_CONSISTENCY_2"; + case 4: + return "MEM_LAST_ACCESS_DELIMITER"; case 7: return "MEM_ZERO_INIT"; - case 6: - return "MEM_READ_WRITE_TAG_CONSISTENCY"; + case 8: + return "MEM_IN_TAG_CONSISTENCY_1"; case 5: return "MEM_READ_WRITE_VAL_CONSISTENCY"; - case 4: - return "MEM_LAST_ACCESS_DELIMITER"; + case 6: + return "MEM_READ_WRITE_TAG_CONSISTENCY"; - case 8: - return "MEM_IN_TAG_CONSISTENCY_1"; + case 9: + return "MEM_IN_TAG_CONSISTENCY_2"; } return std::to_string(index); } diff --git a/barretenberg/cpp/src/barretenberg/relations/generated/avm/declare_views.hpp b/barretenberg/cpp/src/barretenberg/relations/generated/avm/declare_views.hpp index 708ecd4ea29f..838e689b7186 100644 --- a/barretenberg/cpp/src/barretenberg/relations/generated/avm/declare_views.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/generated/avm/declare_views.hpp @@ -79,17 +79,17 @@ [[maybe_unused]] auto equiv_inter_reg_alu = View(new_term.equiv_inter_reg_alu); \ [[maybe_unused]] auto equiv_tag_err = View(new_term.equiv_tag_err); \ [[maybe_unused]] auto equiv_tag_err_counts = View(new_term.equiv_tag_err_counts); \ - [[maybe_unused]] auto avm_alu_alu_u16_r2_shift = View(new_term.avm_alu_alu_u16_r2_shift); \ - [[maybe_unused]] auto avm_alu_alu_u16_r1_shift = View(new_term.avm_alu_alu_u16_r1_shift); \ - [[maybe_unused]] auto avm_alu_alu_u16_r6_shift = View(new_term.avm_alu_alu_u16_r6_shift); \ - [[maybe_unused]] auto avm_alu_alu_u16_r5_shift = View(new_term.avm_alu_alu_u16_r5_shift); \ [[maybe_unused]] auto avm_alu_alu_u16_r4_shift = View(new_term.avm_alu_alu_u16_r4_shift); \ - [[maybe_unused]] auto avm_alu_alu_u16_r0_shift = View(new_term.avm_alu_alu_u16_r0_shift); \ + [[maybe_unused]] auto avm_alu_alu_u16_r5_shift = View(new_term.avm_alu_alu_u16_r5_shift); \ + [[maybe_unused]] auto avm_alu_alu_u16_r2_shift = View(new_term.avm_alu_alu_u16_r2_shift); \ [[maybe_unused]] auto avm_alu_alu_u16_r7_shift = View(new_term.avm_alu_alu_u16_r7_shift); \ + [[maybe_unused]] auto avm_alu_alu_u16_r0_shift = View(new_term.avm_alu_alu_u16_r0_shift); \ [[maybe_unused]] auto avm_alu_alu_u16_r3_shift = View(new_term.avm_alu_alu_u16_r3_shift); \ - [[maybe_unused]] auto avm_main_pc_shift = View(new_term.avm_main_pc_shift); \ - [[maybe_unused]] auto avm_main_internal_return_ptr_shift = View(new_term.avm_main_internal_return_ptr_shift); \ + [[maybe_unused]] auto avm_alu_alu_u16_r6_shift = View(new_term.avm_alu_alu_u16_r6_shift); \ + [[maybe_unused]] auto avm_alu_alu_u16_r1_shift = View(new_term.avm_alu_alu_u16_r1_shift); \ [[maybe_unused]] auto avm_mem_m_val_shift = View(new_term.avm_mem_m_val_shift); \ [[maybe_unused]] auto avm_mem_m_rw_shift = View(new_term.avm_mem_m_rw_shift); \ [[maybe_unused]] auto avm_mem_m_tag_shift = View(new_term.avm_mem_m_tag_shift); \ - [[maybe_unused]] auto avm_mem_m_addr_shift = View(new_term.avm_mem_m_addr_shift); + [[maybe_unused]] auto avm_mem_m_addr_shift = View(new_term.avm_mem_m_addr_shift); \ + [[maybe_unused]] auto avm_main_internal_return_ptr_shift = View(new_term.avm_main_internal_return_ptr_shift); \ + [[maybe_unused]] auto avm_main_pc_shift = View(new_term.avm_main_pc_shift); diff --git a/noir-projects/aztec-nr/aztec/src/context.nr b/noir-projects/aztec-nr/aztec/src/context.nr index 09162c6cf84b..9f5ae7efb82c 100644 --- a/noir-projects/aztec-nr/aztec/src/context.nr +++ b/noir-projects/aztec-nr/aztec/src/context.nr @@ -14,18 +14,23 @@ use avm::AVMContext; struct Context { private: Option<&mut PrivateContext>, public: Option<&mut PublicContext>, + public_vm: Option<&mut AVMContext>, } impl Context { pub fn private(context: &mut PrivateContext) -> Context { - Context { private: Option::some(context), public: Option::none() } + Context { private: Option::some(context), public: Option::none(), public_vm: Option::none() } } pub fn public(context: &mut PublicContext) -> Context { - Context { public: Option::some(context), private: Option::none() } + Context { public: Option::some(context), private: Option::none(), public_vm: Option::none() } + } + + pub fn public_vm(context: &mut AVMContext) -> Context { + Context { public_vm: Option::some(context), public: Option::none(), private: Option::none() } } pub fn none() -> Context { - Context { public: Option::none(), private: Option::none() } + Context { public: Option::none(), private: Option::none(), public_vm: Option::none() } } } diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index f5134cb89401..f039bb367e30 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -1,5 +1,6 @@ contract AvmTest { // Libs + use dep::aztec::state_vars::PublicMutable; use dep::aztec::protocol_types::{address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH}; use dep::compressed_string::CompressedString; @@ -9,7 +10,21 @@ contract AvmTest { #[aztec(private)] fn constructor() {} - // Public-vm macro will prefix avm to the function name for transpilation + struct Storage { + owner: PublicMutable + } + + #[aztec(public-vm)] + fn setAdmin() { + storage.owner.write(context.sender()); + } + + #[aztec(public-vm)] + fn setAndRead() -> pub AztecAddress { + storage.owner.write(context.sender()); + storage.owner.read() + } + #[aztec(public-vm)] fn addArgsReturn(argA: Field, argB: Field) -> pub Field { argA + argB diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index f9df3f101299..012995d6ed45 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -727,8 +727,14 @@ fn transform_function( /// Transform a function to work with AVM bytecode fn transform_vm_function( func: &mut NoirFunction, - _storage_defined: bool, + storage_defined: bool, ) -> Result<(), AztecMacroError> { + // Create access to storage + if storage_defined { + let storage = abstract_storage("public_vm", true); + func.def.body.0.insert(0, storage); + } + // Push Avm context creation to the beginning of the function let create_context = create_avm_context()?; func.def.body.0.insert(0, create_context); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index f1a8f24ed03f..662dc074d980 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -32,7 +32,7 @@ use num_bigint::BigUint; /// The Brillig VM does not apply a limit to the memory address space, /// As a convention, we take use 64 bits. This means that we assume that /// memory has 2^64 memory slots. -pub(crate) const BRILLIG_MEMORY_ADDRESSING_BIT_SIZE: u32 = 32; +pub(crate) const BRILLIG_MEMORY_ADDRESSING_BIT_SIZE: u32 = 64; // Registers reserved in runtime for special purposes. pub(crate) enum ReservedRegisters { @@ -562,6 +562,7 @@ impl BrilligContext { bit_size: u32, ) { self.debug_show.const_instruction(result, constant); + self.push_opcode(BrilligOpcode::Const { destination: result, value: constant, bit_size }); } diff --git a/noir/noir-repo/test_programs/noir_test_failure/should_fail_suite_with_one_failure/Nargo.toml b/noir/noir-repo/test_programs/noir_test_failure/should_fail_suite_with_one_failure/Nargo.toml new file mode 100644 index 000000000000..3d2cf2c60965 --- /dev/null +++ b/noir/noir-repo/test_programs/noir_test_failure/should_fail_suite_with_one_failure/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "should_fail_with_mismatch" +type = "bin" +authors = [""] +[dependencies] diff --git a/yarn-project/simulator/src/avm/avm_memory_types.ts b/yarn-project/simulator/src/avm/avm_memory_types.ts index 9b26c3f8b4d2..48e56ed30260 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.ts @@ -255,6 +255,12 @@ export class TaggedMemory { } } + public checkIsValidMemoryOffsetTag(offset: number) { + if (this.getTag(offset) > TypeTag.UINT64) { + throw TagCheckError.forOffset(offset, TypeTag[this.getTag(offset)], 'UINT64'); + } + } + public static checkIsIntegralTag(tag: TypeTag) { if (![TypeTag.UINT8, TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64, TypeTag.UINT128].includes(tag)) { throw TagCheckError.forTag(TypeTag[tag], 'integral'); diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index 5c2ce65376cc..e10905caa362 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -393,5 +393,63 @@ describe('AVM simulator', () => { expect(trace.l1ToL2MessageChecks[0].exists).toEqual(true); }); }); + + describe('Storage accesses', () => { + it('Should set a single value in storage', async () => { + // We are setting the owner + const slot = 1n; + const sender = AztecAddress.fromField(new Fr(1)); + const address = AztecAddress.fromField(new Fr(420)); + + const context = initContext({ + env: initExecutionEnvironment({ sender, address, storageAddress: address }), + }); + const bytecode = getAvmTestContractBytecode('avm_setAdmin'); + const results = await new AvmSimulator(context).executeBytecode(bytecode); + + // Get contract function artifact + expect(results.reverted).toBe(false); + + // Contract 420 - Storage slot 1 should contain the value 1 + const worldState = context.persistableState.flush(); + + const storageSlot = worldState.currentStorageValue.get(address.toBigInt())!; + const adminSlotValue = storageSlot.get(slot); + expect(adminSlotValue).toEqual(sender.toField()); + + // Tracing + const storageTrace = worldState.storageWrites.get(address.toBigInt())!; + const slotTrace = storageTrace.get(slot); + expect(slotTrace).toEqual([sender.toField()]); + }); + + it('Should read a value from storage', async () => { + // We are setting the owner + const sender = AztecAddress.fromField(new Fr(1)); + const address = AztecAddress.fromField(new Fr(420)); + + const context = initContext({ + env: initExecutionEnvironment({ sender, address, storageAddress: address }), + }); + const bytecode = getAvmTestContractBytecode('avm_setAndRead'); + const results = await new AvmSimulator(context).executeBytecode(bytecode); + + expect(results.reverted).toBe(false); + + expect(results.output).toEqual([sender.toField()]); + + const worldState = context.persistableState.flush(); + + // Test read trace + const storageReadTrace = worldState.storageReads.get(address.toBigInt())!; + const slotReadTrace = storageReadTrace.get(1n); + expect(slotReadTrace).toEqual([sender.toField()]); + + // Test write trace + const storageWriteTrace = worldState.storageWrites.get(address.toBigInt())!; + const slotWriteTrace = storageWriteTrace.get(1n); + expect(slotWriteTrace).toEqual([sender.toField()]); + }); + }); }); }); diff --git a/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts b/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts index 1fb4137205a5..280d15e1adee 100644 --- a/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts +++ b/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; -import { TaggedMemory, TypeTag } from '../avm_memory_types.js'; +import { TaggedMemory } from '../avm_memory_types.js'; export enum AddressingMode { DIRECT, @@ -51,7 +51,8 @@ export class Addressing { for (const [i, offset] of offsets.entries()) { switch (this.modePerOperand[i]) { case AddressingMode.INDIRECT: - mem.checkTag(TypeTag.UINT32, offset); + // NOTE(reviewer): less than equal is a deviation from the spec - i dont see why this shouldnt be possible! + mem.checkIsValidMemoryOffsetTag(offset); resolved[i] = Number(mem.get(offset).toBigInt()); break; case AddressingMode.DIRECT: diff --git a/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts index e557f3242782..2f14a7a23989 100644 --- a/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts @@ -70,7 +70,7 @@ describe('External Calls', () => { const successOffset = 7; const otherContextInstructionsBytecode = encodeToBytecode([ new CalldataCopy(/*indirect=*/ 0, /*csOffset=*/ 0, /*copySize=*/ argsSize, /*dstOffset=*/ 0), - new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 0), + new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*size=*/ 1, /*slotOffset=*/ 0), new Return(/*indirect=*/ 0, /*retOffset=*/ 0, /*size=*/ 2), ]); @@ -159,7 +159,7 @@ describe('External Calls', () => { const otherContextInstructions: Instruction[] = [ new CalldataCopy(/*indirect=*/ 0, /*csOffset=*/ 0, /*copySize=*/ argsSize, /*dstOffset=*/ 0), - new SStore(/*indirect=*/ 0, /*srcOffset=*/ 1, /*slotOffset=*/ 0), + new SStore(/*indirect=*/ 0, /*srcOffset=*/ 1, /*size=*/ 1, /*slotOffset=*/ 0), ]; const otherContextInstructionsBytecode = encodeToBytecode(otherContextInstructions); diff --git a/yarn-project/simulator/src/avm/opcodes/hashing.test.ts b/yarn-project/simulator/src/avm/opcodes/hashing.test.ts index 452b997a62fa..0db16f99013b 100644 --- a/yarn-project/simulator/src/avm/opcodes/hashing.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/hashing.test.ts @@ -126,7 +126,6 @@ describe('Hashing Opcodes', () => { expect(combined).toEqual(expectedHash); }); - // TODO: indirect }); describe('Sha256', () => { diff --git a/yarn-project/simulator/src/avm/opcodes/storage.test.ts b/yarn-project/simulator/src/avm/opcodes/storage.test.ts index bd53a1d3324d..ab98f3e7140a 100644 --- a/yarn-project/simulator/src/avm/opcodes/storage.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/storage.test.ts @@ -28,9 +28,15 @@ describe('Storage Instructions', () => { SStore.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // srcOffset - ...Buffer.from('a2345678', 'hex'), // slotOffset + ...Buffer.from('a2345678', 'hex'), // size + ...Buffer.from('3456789a', 'hex'), // slotOffset ]); - const inst = new SStore(/*indirect=*/ 0x01, /*srcOffset=*/ 0x12345678, /*slotOffset=*/ 0xa2345678); + const inst = new SStore( + /*indirect=*/ 0x01, + /*srcOffset=*/ 0x12345678, + /*size=*/ 0xa2345678, + /*slotOffset=*/ 0x3456789a, + ); expect(SStore.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); @@ -43,7 +49,7 @@ describe('Storage Instructions', () => { context.machineState.memory.set(0, a); context.machineState.memory.set(1, b); - await new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 1).execute(context); + await new SStore(/*indirect=*/ 0, /*srcOffset=*/ 1, /*size=*/ 1, /*slotOffset=*/ 0).execute(context); expect(journal.writeStorage).toHaveBeenCalledWith(address, new Fr(a.toBigInt()), new Fr(b.toBigInt())); }); @@ -60,7 +66,8 @@ describe('Storage Instructions', () => { context.machineState.memory.set(0, a); context.machineState.memory.set(1, b); - const instruction = () => new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 1).execute(context); + const instruction = () => + new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*size=*/ 1, /*slotOffset=*/ 1).execute(context); await expect(instruction()).rejects.toThrow(StaticCallStorageAlterError); }); }); @@ -71,9 +78,15 @@ describe('Storage Instructions', () => { SLoad.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // slotOffset - ...Buffer.from('a2345678', 'hex'), // dstOffset + ...Buffer.from('a2345678', 'hex'), // size + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - const inst = new SLoad(/*indirect=*/ 0x01, /*slotOffset=*/ 0x12345678, /*dstOffset=*/ 0xa2345678); + const inst = new SLoad( + /*indirect=*/ 0x01, + /*slotOffset=*/ 0x12345678, + /*size=*/ 0xa2345678, + /*dstOffset=*/ 0x3456789a, + ); expect(SLoad.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); @@ -90,7 +103,7 @@ describe('Storage Instructions', () => { context.machineState.memory.set(0, a); context.machineState.memory.set(1, b); - await new SLoad(/*indirect=*/ 0, /*slotOffset=*/ 0, /*dstOffset=*/ 1).execute(context); + await new SLoad(/*indirect=*/ 0, /*slotOffset=*/ 0, /*size=*/ 1, /*dstOffset=*/ 1).execute(context); expect(journal.readStorage).toHaveBeenCalledWith(address, new Fr(a.toBigInt())); diff --git a/yarn-project/simulator/src/avm/opcodes/storage.ts b/yarn-project/simulator/src/avm/opcodes/storage.ts index 1090d5d35403..3ca3bfa19aaf 100644 --- a/yarn-project/simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/simulator/src/avm/opcodes/storage.ts @@ -4,6 +4,7 @@ import type { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; import { InstructionExecutionError } from '../errors.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; +import { Addressing } from './addressing_mode.js'; import { Instruction } from './instruction.js'; abstract class BaseStorageInstruction extends Instruction { @@ -13,9 +14,15 @@ abstract class BaseStorageInstruction extends Instruction { OperandType.UINT8, OperandType.UINT32, OperandType.UINT32, + OperandType.UINT32, ]; - constructor(protected indirect: number, protected aOffset: number, protected bOffset: number) { + constructor( + protected indirect: number, + protected aOffset: number, + protected /*temporary*/ size: number, + protected bOffset: number, + ) { super(); } } @@ -24,8 +31,8 @@ export class SStore extends BaseStorageInstruction { static readonly type: string = 'SSTORE'; static readonly opcode = Opcode.SSTORE; - constructor(indirect: number, srcOffset: number, slotOffset: number) { - super(indirect, srcOffset, slotOffset); + constructor(indirect: number, srcOffset: number, /*temporary*/ srcSize: number, slotOffset: number) { + super(indirect, srcOffset, srcSize, slotOffset); } async execute(context: AvmContext): Promise { @@ -33,15 +40,19 @@ export class SStore extends BaseStorageInstruction { throw new StaticCallStorageAlterError(); } - const slot = context.machineState.memory.get(this.aOffset); - const data = context.machineState.memory.get(this.bOffset); - - context.persistableState.writeStorage( - context.environment.storageAddress, - new Fr(slot.toBigInt()), - new Fr(data.toBigInt()), + const [srcOffset, slotOffset] = Addressing.fromWire(this.indirect).resolve( + [this.aOffset, this.bOffset], + context.machineState.memory, ); + const slot = context.machineState.memory.get(slotOffset).toFr(); + const data = context.machineState.memory.getSlice(srcOffset, this.size).map(field => field.toFr()); + + for (const [index, value] of Object.entries(data)) { + const adjustedSlot = slot.add(new Fr(BigInt(index))); + context.persistableState.writeStorage(context.environment.storageAddress, adjustedSlot, value); + } + context.machineState.incrementPc(); } } @@ -50,19 +61,27 @@ export class SLoad extends BaseStorageInstruction { static readonly type: string = 'SLOAD'; static readonly opcode = Opcode.SLOAD; - constructor(indirect: number, slotOffset: number, dstOffset: number) { - super(indirect, slotOffset, dstOffset); + constructor(indirect: number, slotOffset: number, size: number, dstOffset: number) { + super(indirect, slotOffset, size, dstOffset); } async execute(context: AvmContext): Promise { - const slot = context.machineState.memory.get(this.aOffset); - - const data: Fr = await context.persistableState.readStorage( - context.environment.storageAddress, - new Fr(slot.toBigInt()), + const [aOffset, size, bOffset] = Addressing.fromWire(this.indirect).resolve( + [this.aOffset, this.size, this.bOffset], + context.machineState.memory, ); - context.machineState.memory.set(this.bOffset, new Field(data)); + const slot = context.machineState.memory.get(aOffset); + + // Write each read value from storage into memory + for (let i = 0; i < size; i++) { + const data: Fr = await context.persistableState.readStorage( + context.environment.storageAddress, + new Fr(slot.toBigInt() + BigInt(i)), + ); + + context.machineState.memory.set(bOffset + i, new Field(data)); + } context.machineState.incrementPc(); }