1extern crate alloc;
2
3use ab_blake3::{CHUNK_LEN, OUT_LEN};
4use ab_contract_file::ContractInstruction;
5use ab_core_primitives::ed25519::{Ed25519PublicKey, Ed25519Signature};
6use ab_io_type::IoType;
7use ab_io_type::bool::Bool;
8use ab_riscv_interpreter::rv64::{Rv64InterpreterState, Rv64SystemInstructionHandler};
9use ab_riscv_interpreter::{
10 BasicInt, ExecutableInstruction, ExecutionError, FetchInstructionResult, InstructionFetcher,
11 ProgramCounter, ProgramCounterError, VirtualMemory, VirtualMemoryError,
12};
13use ab_riscv_primitives::instruction::Instruction;
14use ab_riscv_primitives::instruction::rv64::Rv64Instruction;
15use ab_riscv_primitives::registers::{Register, Registers};
16use alloc::vec::Vec;
17use core::marker::PhantomData;
18use core::mem::offset_of;
19use core::ops::ControlFlow;
20
21pub const RISCV_CONTRACT_BYTES: &[u8] = {
23 #[cfg(target_env = "abundance")]
24 {
25 &[]
26 }
27 #[cfg(not(target_env = "abundance"))]
28 {
29 include_bytes!(env!("CONTRACT_PATH"))
30 }
31};
32
33#[derive(Debug, Copy, Clone)]
39#[repr(C)]
40pub struct Blake3HashChunkInternalArgs {
41 chunk_ptr: u64,
42 chunk_size: u32,
43 chunk_capacity: u32,
44 result_ptr: u64,
45 chunk: [u8; CHUNK_LEN],
46 result: [u8; OUT_LEN],
47}
48
49impl Blake3HashChunkInternalArgs {
50 pub fn new(internal_args_addr: u64, chunk: [u8; CHUNK_LEN]) -> Self {
52 Self {
53 chunk_ptr: internal_args_addr + offset_of!(Self, chunk) as u64,
54 chunk_size: CHUNK_LEN as u32,
55 chunk_capacity: CHUNK_LEN as u32,
56 result_ptr: internal_args_addr + offset_of!(Self, result) as u64,
57 chunk,
58 result: [0; _],
59 }
60 }
61
62 pub fn result(&self) -> [u8; OUT_LEN] {
64 self.result
65 }
66}
67
68#[derive(Debug, Copy, Clone)]
74#[repr(C)]
75pub struct Ed25519VerifyInternalArgs {
76 pub public_key_ptr: u64,
77 pub public_key_size: u32,
78 pub public_key_capacity: u32,
79 pub signature_ptr: u64,
80 pub signature_size: u32,
81 pub signature_capacity: u32,
82 pub message_ptr: u64,
83 pub message_size: u32,
84 pub message_capacity: u32,
85 pub result_ptr: u64,
86 pub public_key: Ed25519PublicKey,
87 pub signature: Ed25519Signature,
88 pub message: [u8; OUT_LEN],
89 pub result: Bool,
90}
91
92impl Ed25519VerifyInternalArgs {
93 pub fn new(
95 internal_args_addr: u64,
96 public_key: Ed25519PublicKey,
97 signature: Ed25519Signature,
98 message: [u8; OUT_LEN],
99 ) -> Self {
100 Self {
101 public_key_ptr: internal_args_addr + offset_of!(Self, public_key) as u64,
102 public_key_size: Ed25519PublicKey::SIZE as u32,
103 public_key_capacity: Ed25519PublicKey::SIZE as u32,
104 signature_ptr: internal_args_addr + offset_of!(Self, signature) as u64,
105 signature_size: Ed25519Signature::SIZE as u32,
106 signature_capacity: Ed25519Signature::SIZE as u32,
107 message_ptr: internal_args_addr + offset_of!(Self, message) as u64,
108 message_size: OUT_LEN as u32,
109 message_capacity: OUT_LEN as u32,
110 result_ptr: internal_args_addr + offset_of!(Self, result) as u64,
111 public_key,
112 signature,
113 message,
114 result: Bool::new(false),
115 }
116 }
117
118 pub fn result(&self) -> Bool {
120 self.result
121 }
122}
123
124#[derive(Debug, Copy, Clone)]
126pub struct TestMemory<const MEMORY_SIZE: usize> {
127 data: [u8; MEMORY_SIZE],
128 base_addr: u64,
129}
130
131impl<const MEMORY_SIZE: usize> VirtualMemory for TestMemory<MEMORY_SIZE> {
132 fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
133 where
134 T: BasicInt,
135 {
136 let offset = address
137 .checked_sub(self.base_addr)
138 .ok_or(VirtualMemoryError::OutOfBoundsRead { address })? as usize;
139
140 if offset + size_of::<T>() > self.data.len() {
141 return Err(VirtualMemoryError::OutOfBoundsRead { address });
142 }
143
144 unsafe {
146 Ok(self
147 .data
148 .as_ptr()
149 .cast::<T>()
150 .byte_add(offset)
151 .read_unaligned())
152 }
153 }
154
155 unsafe fn read_unchecked<T>(&self, address: u64) -> T
156 where
157 T: BasicInt,
158 {
159 unsafe {
161 let offset = address.unchecked_sub(self.base_addr) as usize;
162 self.data
163 .as_ptr()
164 .cast::<T>()
165 .byte_add(offset)
166 .read_unaligned()
167 }
168 }
169
170 fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
171 where
172 T: BasicInt,
173 {
174 let offset = address
175 .checked_sub(self.base_addr)
176 .ok_or(VirtualMemoryError::OutOfBoundsWrite { address })? as usize;
177
178 if offset + size_of::<T>() > self.data.len() {
179 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
180 }
181
182 unsafe {
184 self.data
185 .as_mut_ptr()
186 .cast::<T>()
187 .byte_add(offset)
188 .write_unaligned(value);
189 }
190
191 Ok(())
192 }
193}
194
195impl<const MEMORY_SIZE: usize> TestMemory<MEMORY_SIZE> {
196 pub fn new(base_addr: u64) -> Self {
198 Self {
199 data: [0; _],
200 base_addr,
201 }
202 }
203
204 pub fn get_bytes(&self, address: u64, size: usize) -> Result<&[u8], VirtualMemoryError> {
206 let offset = address
207 .checked_sub(self.base_addr)
208 .ok_or(VirtualMemoryError::OutOfBoundsRead { address })? as usize;
209
210 if offset + size > self.data.len() {
211 return Err(VirtualMemoryError::OutOfBoundsRead { address });
212 }
213
214 Ok(&self.data[offset..][..size])
215 }
216
217 pub fn get_mut_bytes(
219 &mut self,
220 address: u64,
221 size: usize,
222 ) -> Result<&mut [u8], VirtualMemoryError> {
223 let offset = address
224 .checked_sub(self.base_addr)
225 .ok_or(VirtualMemoryError::OutOfBoundsRead { address })? as usize;
226
227 if offset + size > self.data.len() {
228 return Err(VirtualMemoryError::OutOfBoundsRead { address });
229 }
230
231 Ok(&mut self.data[offset..][..size])
232 }
233}
234
235#[derive(Debug, Default, Clone)]
237pub struct EagerTestInstructionFetcher {
238 instructions: Vec<ContractInstruction>,
239 return_trap_address: u64,
240 base_addr: u64,
241 instruction_offset: usize,
242}
243
244impl<Memory> ProgramCounter<u64, Memory, &'static str> for EagerTestInstructionFetcher
245where
246 Memory: VirtualMemory,
247{
248 #[inline(always)]
249 fn get_pc(&self) -> u64 {
250 self.base_addr + self.instruction_offset as u64 * size_of::<u32>() as u64
251 }
252
253 #[inline]
254 fn set_pc(
255 &mut self,
256 _memory: &mut Memory,
257 pc: u64,
258 ) -> Result<ControlFlow<()>, ProgramCounterError<u64, &'static str>> {
259 let address = pc;
260
261 if address == self.return_trap_address {
262 return Ok(ControlFlow::Break(()));
263 }
264
265 if !address.is_multiple_of(size_of::<u32>() as u64) {
266 return Err(ProgramCounterError::UnalignedInstruction { address });
267 }
268
269 let offset = address
270 .checked_sub(self.base_addr)
271 .ok_or(VirtualMemoryError::OutOfBoundsRead { address })? as usize;
272 let instruction_offset = offset / size_of::<u32>();
273
274 if instruction_offset >= self.instructions.len() {
275 return Err(VirtualMemoryError::OutOfBoundsRead { address }.into());
276 }
277
278 self.instruction_offset = instruction_offset;
279
280 Ok(ControlFlow::Continue(()))
281 }
282}
283
284impl<Memory> InstructionFetcher<ContractInstruction, Memory, &'static str>
285 for EagerTestInstructionFetcher
286where
287 Memory: VirtualMemory,
288{
289 #[inline(always)]
290 fn fetch_instruction(
291 &mut self,
292 _memory: &mut Memory,
293 ) -> Result<
294 FetchInstructionResult<ContractInstruction>,
295 ExecutionError<u64, ContractInstruction, &'static str>,
296 > {
297 let instruction = *unsafe { self.instructions.get_unchecked(self.instruction_offset) };
301 self.instruction_offset += 1;
302
303 Ok(FetchInstructionResult::Instruction(instruction))
304 }
305}
306
307impl EagerTestInstructionFetcher {
308 #[inline(always)]
320 pub unsafe fn new(
321 instructions: Vec<ContractInstruction>,
322 return_trap_address: u64,
323 base_addr: u64,
324 pc: u64,
325 ) -> Self {
326 Self {
327 instructions,
328 return_trap_address,
329 base_addr,
330 instruction_offset: (pc - base_addr) as usize / size_of::<u32>(),
331 }
332 }
333}
334
335#[derive(Debug, Clone, Copy)]
337pub struct NoopRv64SystemInstructionHandler<Instruction> {
338 _phantom: PhantomData<Instruction>,
339}
340
341impl<Reg> Default for NoopRv64SystemInstructionHandler<Reg> {
342 #[inline(always)]
343 fn default() -> Self {
344 Self {
345 _phantom: PhantomData,
346 }
347 }
348}
349
350impl<Reg, Memory, PC, CustomError> Rv64SystemInstructionHandler<Reg, Memory, PC, CustomError>
351 for NoopRv64SystemInstructionHandler<Rv64Instruction<Reg>>
352where
353 Reg: Register<Type = u64>,
354 [(); Reg::N]:,
355{
356 #[inline(always)]
357 fn handle_ecall(
358 &mut self,
359 _regs: &mut Registers<Reg>,
360 _memory: &mut Memory,
361 _program_counter: &mut PC,
362 ) -> Result<ControlFlow<()>, ExecutionError<u64, Rv64Instruction<Reg>, CustomError>> {
363 Ok(ControlFlow::Continue(()))
367 }
368}
369
370#[expect(clippy::type_complexity)]
372pub fn execute<Memory, IF>(
373 state: &mut Rv64InterpreterState<
374 <ContractInstruction as Instruction>::Reg,
375 Memory,
376 IF,
377 NoopRv64SystemInstructionHandler<
378 Rv64Instruction<<ContractInstruction as Instruction>::Reg>,
379 >,
380 &'static str,
381 >,
382) -> Result<(), ExecutionError<u64, ContractInstruction, &'static str>>
383where
384 Memory: VirtualMemory,
385 IF: InstructionFetcher<ContractInstruction, Memory, &'static str>,
386{
387 loop {
388 let instruction = match state
389 .instruction_fetcher
390 .fetch_instruction(&mut state.memory)?
391 {
392 FetchInstructionResult::Instruction(instruction) => instruction,
393 FetchInstructionResult::ControlFlow(ControlFlow::Continue(())) => {
394 continue;
395 }
396 FetchInstructionResult::ControlFlow(ControlFlow::Break(())) => {
397 break;
398 }
399 };
400
401 match instruction.execute(state)? {
402 ControlFlow::Continue(()) => {
403 continue;
404 }
405 ControlFlow::Break(()) => {
406 break;
407 }
408 }
409 }
410
411 Ok(())
412}