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