openvm_native_circuit/adapters/
convert_adapter.rs

1use std::{
2    borrow::{Borrow, BorrowMut},
3    marker::PhantomData,
4};
5
6use openvm_circuit::{
7    arch::{
8        AdapterAirContext, AdapterRuntimeContext, BasicAdapterInterface, ExecutionBridge,
9        ExecutionBus, ExecutionState, MinimalInstruction, Result, VmAdapterAir, VmAdapterChip,
10        VmAdapterInterface,
11    },
12    system::{
13        memory::{
14            offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols},
15            MemoryAddress, MemoryController, OfflineMemory, RecordId,
16        },
17        program::ProgramBus,
18    },
19};
20use openvm_circuit_primitives_derive::AlignedBorrow;
21use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP};
22use openvm_native_compiler::conversion::AS;
23use openvm_stark_backend::{
24    interaction::InteractionBuilder,
25    p3_air::BaseAir,
26    p3_field::{Field, FieldAlgebra, PrimeField32},
27};
28use serde::{Deserialize, Serialize};
29use serde_big_array::BigArray;
30
31#[repr(C)]
32#[derive(Debug, Serialize, Deserialize)]
33pub struct VectorReadRecord<const NUM_READS: usize, const READ_SIZE: usize> {
34    #[serde(with = "BigArray")]
35    pub reads: [RecordId; NUM_READS],
36}
37
38#[repr(C)]
39#[derive(Debug, Serialize, Deserialize)]
40pub struct VectorWriteRecord<const WRITE_SIZE: usize> {
41    pub from_state: ExecutionState<u32>,
42    pub writes: [RecordId; 1],
43}
44
45#[allow(dead_code)]
46#[derive(Debug)]
47pub struct ConvertAdapterChip<F: Field, const READ_SIZE: usize, const WRITE_SIZE: usize> {
48    pub air: ConvertAdapterAir<READ_SIZE, WRITE_SIZE>,
49    _marker: PhantomData<F>,
50}
51
52impl<F: PrimeField32, const READ_SIZE: usize, const WRITE_SIZE: usize>
53    ConvertAdapterChip<F, READ_SIZE, WRITE_SIZE>
54{
55    pub fn new(
56        execution_bus: ExecutionBus,
57        program_bus: ProgramBus,
58        memory_bridge: MemoryBridge,
59    ) -> Self {
60        Self {
61            air: ConvertAdapterAir {
62                execution_bridge: ExecutionBridge::new(execution_bus, program_bus),
63                memory_bridge,
64            },
65            _marker: PhantomData,
66        }
67    }
68}
69
70#[repr(C)]
71#[derive(AlignedBorrow)]
72pub struct ConvertAdapterCols<T, const READ_SIZE: usize, const WRITE_SIZE: usize> {
73    pub from_state: ExecutionState<T>,
74    pub a_pointer: T,
75    pub b_pointer: T,
76    pub writes_aux: [MemoryWriteAuxCols<T, WRITE_SIZE>; 1],
77    pub reads_aux: [MemoryReadAuxCols<T>; 1],
78}
79
80#[derive(Clone, Copy, Debug, derive_new::new)]
81pub struct ConvertAdapterAir<const READ_SIZE: usize, const WRITE_SIZE: usize> {
82    pub(super) execution_bridge: ExecutionBridge,
83    pub(super) memory_bridge: MemoryBridge,
84}
85
86impl<F: Field, const READ_SIZE: usize, const WRITE_SIZE: usize> BaseAir<F>
87    for ConvertAdapterAir<READ_SIZE, WRITE_SIZE>
88{
89    fn width(&self) -> usize {
90        ConvertAdapterCols::<F, READ_SIZE, WRITE_SIZE>::width()
91    }
92}
93
94impl<AB: InteractionBuilder, const READ_SIZE: usize, const WRITE_SIZE: usize> VmAdapterAir<AB>
95    for ConvertAdapterAir<READ_SIZE, WRITE_SIZE>
96{
97    type Interface =
98        BasicAdapterInterface<AB::Expr, MinimalInstruction<AB::Expr>, 1, 1, READ_SIZE, WRITE_SIZE>;
99
100    fn eval(
101        &self,
102        builder: &mut AB,
103        local: &[AB::Var],
104        ctx: AdapterAirContext<AB::Expr, Self::Interface>,
105    ) {
106        let cols: &ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = local.borrow();
107        let timestamp = cols.from_state.timestamp;
108        let mut timestamp_delta = 0usize;
109        let mut timestamp_pp = || {
110            timestamp_delta += 1;
111            timestamp + AB::F::from_canonical_usize(timestamp_delta - 1)
112        };
113
114        let d = AB::Expr::TWO;
115        let e = AB::Expr::from_canonical_u32(AS::Native as u32);
116
117        self.memory_bridge
118            .read(
119                MemoryAddress::new(e.clone(), cols.b_pointer),
120                ctx.reads[0].clone(),
121                timestamp_pp(),
122                &cols.reads_aux[0],
123            )
124            .eval(builder, ctx.instruction.is_valid.clone());
125
126        self.memory_bridge
127            .write(
128                MemoryAddress::new(d.clone(), cols.a_pointer),
129                ctx.writes[0].clone(),
130                timestamp_pp(),
131                &cols.writes_aux[0],
132            )
133            .eval(builder, ctx.instruction.is_valid.clone());
134
135        self.execution_bridge
136            .execute_and_increment_or_set_pc(
137                ctx.instruction.opcode,
138                [
139                    cols.a_pointer.into(),
140                    cols.b_pointer.into(),
141                    AB::Expr::ZERO,
142                    d,
143                    e,
144                ],
145                cols.from_state,
146                AB::F::from_canonical_usize(timestamp_delta),
147                (DEFAULT_PC_STEP, ctx.to_pc),
148            )
149            .eval(builder, ctx.instruction.is_valid);
150    }
151
152    fn get_from_pc(&self, local: &[AB::Var]) -> AB::Var {
153        let cols: &ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = local.borrow();
154        cols.from_state.pc
155    }
156}
157
158impl<F: PrimeField32, const READ_SIZE: usize, const WRITE_SIZE: usize> VmAdapterChip<F>
159    for ConvertAdapterChip<F, READ_SIZE, WRITE_SIZE>
160{
161    type ReadRecord = VectorReadRecord<1, READ_SIZE>;
162    type WriteRecord = VectorWriteRecord<WRITE_SIZE>;
163    type Air = ConvertAdapterAir<READ_SIZE, WRITE_SIZE>;
164    type Interface = BasicAdapterInterface<F, MinimalInstruction<F>, 1, 1, READ_SIZE, WRITE_SIZE>;
165
166    fn preprocess(
167        &mut self,
168        memory: &mut MemoryController<F>,
169        instruction: &Instruction<F>,
170    ) -> Result<(
171        <Self::Interface as VmAdapterInterface<F>>::Reads,
172        Self::ReadRecord,
173    )> {
174        let Instruction { b, e, .. } = *instruction;
175
176        let y_val = memory.read::<READ_SIZE>(e, b);
177
178        Ok(([y_val.1], Self::ReadRecord { reads: [y_val.0] }))
179    }
180
181    fn postprocess(
182        &mut self,
183        memory: &mut MemoryController<F>,
184        instruction: &Instruction<F>,
185        from_state: ExecutionState<u32>,
186        output: AdapterRuntimeContext<F, Self::Interface>,
187        _read_record: &Self::ReadRecord,
188    ) -> Result<(ExecutionState<u32>, Self::WriteRecord)> {
189        let Instruction { a, d, .. } = *instruction;
190        let (write_id, _) = memory.write::<WRITE_SIZE>(d, a, output.writes[0]);
191
192        Ok((
193            ExecutionState {
194                pc: output.to_pc.unwrap_or(from_state.pc + DEFAULT_PC_STEP),
195                timestamp: memory.timestamp(),
196            },
197            Self::WriteRecord {
198                from_state,
199                writes: [write_id],
200            },
201        ))
202    }
203
204    fn generate_trace_row(
205        &self,
206        row_slice: &mut [F],
207        read_record: Self::ReadRecord,
208        write_record: Self::WriteRecord,
209        memory: &OfflineMemory<F>,
210    ) {
211        let aux_cols_factory = memory.aux_cols_factory();
212        let row_slice: &mut ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = row_slice.borrow_mut();
213
214        let read = memory.record_by_id(read_record.reads[0]);
215        let write = memory.record_by_id(write_record.writes[0]);
216
217        row_slice.from_state = write_record.from_state.map(F::from_canonical_u32);
218        row_slice.a_pointer = write.pointer;
219        row_slice.b_pointer = read.pointer;
220
221        aux_cols_factory.generate_read_aux(read, &mut row_slice.reads_aux[0]);
222        aux_cols_factory.generate_write_aux(write, &mut row_slice.writes_aux[0]);
223    }
224
225    fn air(&self) -> &Self::Air {
226        &self.air
227    }
228}