1use crate::{
4 access, alu, asm,
5 compute::ComputeInputs,
6 crypto,
7 error::{OpError, OpResult, ParentMemoryError},
8 pred, repeat, total_control_flow, Access, GasLimit, LazyCache, Memory, OpAccess, OpGasCost,
9 ProgramControlFlow, Repeat, Stack, StateReads, Vm,
10};
11use essential_asm::Op;
12use essential_types::ContentAddress;
13use std::sync::Arc;
14
15pub fn step_op<S, OA>(
18 access: Access,
19 op: Op,
20 vm: &mut Vm,
21 state: &S,
22 op_access: OA,
23 op_gas_cost: &impl OpGasCost,
24 gas_limit: GasLimit,
25) -> OpResult<Option<ProgramControlFlow>, S::Error>
26where
27 S: StateReads,
28 OA: OpAccess<Op = Op>,
29 OA::Error: Into<OpError<S::Error>>,
30{
31 let r = match op {
32 Op::Access(op) => step_op_access(access, op, &mut vm.stack, &mut vm.repeat, &vm.cache)
33 .map(|_| None)
34 .map_err(OpError::from_infallible)?,
35 Op::Alu(op) => step_op_alu(op, &mut vm.stack)
36 .map(|_| None)
37 .map_err(OpError::from_infallible)?,
38 Op::Crypto(op) => step_op_crypto(op, &mut vm.stack)
39 .map(|_| None)
40 .map_err(OpError::from_infallible)?,
41 Op::ParentMemory(op) => step_op_parent_memory(op, &mut vm.stack, &vm.parent_memory)
42 .map(|_| None)
43 .map_err(OpError::from_infallible)?,
44 Op::Pred(op) => step_op_pred(op, &mut vm.stack)
45 .map(|_| None)
46 .map_err(OpError::from_infallible)?,
47 Op::Stack(op) => step_op_stack(op, vm.pc, &mut vm.stack, &mut vm.repeat)
48 .map_err(OpError::from_infallible)?,
49 Op::TotalControlFlow(op) => step_op_total_control_flow(op, &mut vm.stack, vm.pc)
50 .map_err(OpError::from_infallible)?,
51 Op::Memory(op) => step_op_memory(op, &mut vm.stack, &mut vm.memory)
52 .map(|_| None)
53 .map_err(OpError::from_infallible)?,
54 Op::StateRead(op) => step_op_state_reads(
55 op,
56 &access.this_solution().predicate_to_solve.contract,
57 state,
58 &mut vm.stack,
59 &mut vm.memory,
60 )
61 .map(|_| None)?,
62 Op::Compute(op) => step_op_compute(
63 op,
64 ComputeInputs {
65 pc: vm.pc,
66 stack: &mut vm.stack,
67 memory: &mut vm.memory,
68 parent_memory: vm.parent_memory.clone(),
69 halt: vm.halt,
70 repeat: &vm.repeat,
71 cache: vm.cache.clone(),
72 access,
73 state_reads: state,
74 op_access,
75 op_gas_cost,
76 gas_limit,
77 },
78 )
79 .map(Some)?,
80 };
81
82 Ok(r)
83}
84
85pub fn step_op_state_reads<S>(
87 op: asm::StateRead,
88 contract_addr: &ContentAddress,
89 state: &S,
90 stack: &mut Stack,
91 memory: &mut Memory,
92) -> OpResult<(), S::Error>
93where
94 S: StateReads,
95{
96 match op {
97 asm::StateRead::KeyRange => {
98 crate::state_read::key_range(state.pre(), contract_addr, stack, memory)
99 }
100 asm::StateRead::KeyRangeExtern => {
101 crate::state_read::key_range_ext(state.pre(), stack, memory)
102 }
103 essential_asm::StateRead::PostKeyRange => {
104 crate::state_read::key_range(state.post(), contract_addr, stack, memory)
105 }
106 essential_asm::StateRead::PostKeyRangeExtern => {
107 crate::state_read::key_range_ext(state.post(), stack, memory)
108 }
109 }
110}
111
112pub fn step_op_compute<S, OA, OG>(
114 op: asm::Compute,
115 inputs: ComputeInputs<S, OA, OG>,
116) -> OpResult<ProgramControlFlow, S::Error>
117where
118 S: StateReads,
119 OA: OpAccess<Op = Op>,
120 OA::Error: Into<OpError<S::Error>>,
121 OG: OpGasCost,
122{
123 match op {
124 asm::Compute::Compute => {
125 crate::compute::compute(inputs).map(ProgramControlFlow::ComputeResult)
126 }
127 asm::Compute::ComputeEnd => Ok(ProgramControlFlow::ComputeEnd),
128 }
129}
130
131pub fn step_op_access(
133 access: Access,
134 op: asm::Access,
135 stack: &mut Stack,
136 repeat: &mut Repeat,
137 cache: &LazyCache,
138) -> OpResult<()> {
139 match op {
140 asm::Access::PredicateData => {
141 access::predicate_data(&access.this_solution().predicate_data, stack)
142 }
143 asm::Access::PredicateDataLen => {
144 access::predicate_data_len(&access.this_solution().predicate_data, stack)
145 .map_err(From::from)
146 }
147 asm::Access::PredicateDataSlots => {
148 access::predicate_data_slots(stack, &access.this_solution().predicate_data)
149 }
150 asm::Access::ThisAddress => access::this_address(access.this_solution(), stack),
151 asm::Access::ThisContractAddress => {
152 access::this_contract_address(access.this_solution(), stack)
153 }
154 asm::Access::RepeatCounter => access::repeat_counter(stack, repeat),
155 asm::Access::PredicateExists => access::predicate_exists(stack, access.solutions, cache),
156 }
157}
158
159pub fn step_op_alu(op: asm::Alu, stack: &mut Stack) -> OpResult<()> {
161 match op {
162 asm::Alu::Add => stack.pop2_push1(alu::add),
163 asm::Alu::Sub => stack.pop2_push1(alu::sub),
164 asm::Alu::Mul => stack.pop2_push1(alu::mul),
165 asm::Alu::Div => stack.pop2_push1(alu::div),
166 asm::Alu::Mod => stack.pop2_push1(alu::mod_),
167 asm::Alu::Shl => stack.pop2_push1(alu::shl),
168 asm::Alu::Shr => stack.pop2_push1(alu::shr),
169 asm::Alu::ShrI => stack.pop2_push1(alu::arithmetic_shr),
170 }
171}
172
173pub fn step_op_crypto(op: asm::Crypto, stack: &mut Stack) -> OpResult<()> {
175 match op {
176 asm::Crypto::Sha256 => crypto::sha256(stack),
177 asm::Crypto::VerifyEd25519 => crypto::verify_ed25519(stack),
178 asm::Crypto::RecoverSecp256k1 => crypto::recover_secp256k1(stack),
179 }
180}
181
182pub fn step_op_pred(op: asm::Pred, stack: &mut Stack) -> OpResult<()> {
184 match op {
185 asm::Pred::Eq => stack.pop2_push1(|a, b| Ok((a == b).into())),
186 asm::Pred::EqRange => pred::eq_range(stack),
187 asm::Pred::Gt => stack.pop2_push1(|a, b| Ok((a > b).into())),
188 asm::Pred::Lt => stack.pop2_push1(|a, b| Ok((a < b).into())),
189 asm::Pred::Gte => stack.pop2_push1(|a, b| Ok((a >= b).into())),
190 asm::Pred::Lte => stack.pop2_push1(|a, b| Ok((a <= b).into())),
191 asm::Pred::And => stack.pop2_push1(|a, b| Ok((a != 0 && b != 0).into())),
192 asm::Pred::Or => stack.pop2_push1(|a, b| Ok((a != 0 || b != 0).into())),
193 asm::Pred::Not => stack.pop1_push1(|a| Ok((a == 0).into())),
194 asm::Pred::EqSet => pred::eq_set(stack),
195 asm::Pred::BitAnd => stack.pop2_push1(|a, b| Ok(a & b)),
196 asm::Pred::BitOr => stack.pop2_push1(|a, b| Ok(a | b)),
197 }
198}
199
200pub fn step_op_stack(
202 op: asm::Stack,
203 pc: usize,
204 stack: &mut Stack,
205 repeat: &mut Repeat,
206) -> OpResult<Option<ProgramControlFlow>> {
207 if let asm::Stack::RepeatEnd = op {
208 return Ok(repeat.repeat()?.map(ProgramControlFlow::Pc));
209 }
210 let r = match op {
211 asm::Stack::Drop => stack.pop_len_words(|_| Ok(())),
212 asm::Stack::Dup => stack.pop1_push2(|w| Ok([w, w])),
213 asm::Stack::DupFrom => stack.dup_from().map_err(From::from),
214 asm::Stack::Push(word) => stack.push(word).map_err(From::from),
215 asm::Stack::Pop => stack.pop().map(|_| ()).map_err(From::from),
216 asm::Stack::Swap => stack.pop2_push2(|a, b| Ok([b, a])),
217 asm::Stack::SwapIndex => stack.swap_index().map_err(From::from),
218 asm::Stack::Select => stack.select().map_err(From::from),
219 asm::Stack::SelectRange => stack.select_range().map_err(From::from),
220 asm::Stack::Repeat => repeat::repeat(pc, stack, repeat),
221 asm::Stack::Reserve => stack.reserve_zeroed().map_err(From::from),
222 asm::Stack::Load => stack.load().map_err(From::from),
223 asm::Stack::Store => stack.store().map_err(From::from),
224 asm::Stack::RepeatEnd => unreachable!(),
225 };
226 r.map(|_| None)
227}
228
229pub fn step_op_total_control_flow(
231 op: asm::TotalControlFlow,
232 stack: &mut Stack,
233 pc: usize,
234) -> OpResult<Option<ProgramControlFlow>> {
235 match op {
236 asm::TotalControlFlow::JumpIf => total_control_flow::jump_if(stack, pc),
237 asm::TotalControlFlow::HaltIf => total_control_flow::halt_if(stack),
238 asm::TotalControlFlow::Halt => Ok(Some(ProgramControlFlow::Halt)),
239 asm::TotalControlFlow::PanicIf => total_control_flow::panic_if(stack).map(|_| None),
240 }
241}
242
243pub fn step_op_memory(op: asm::Memory, stack: &mut Stack, memory: &mut Memory) -> OpResult<()> {
245 match op {
246 asm::Memory::Alloc => {
247 let w = stack.pop()?;
248 let len = memory.len()?;
249 memory.alloc(w)?;
250 Ok(stack.push(len)?)
251 }
252 asm::Memory::Store => {
253 let [w, addr] = stack.pop2()?;
254 memory.store(addr, w)?;
255 Ok(())
256 }
257 asm::Memory::Load => stack.pop1_push1(|addr| {
258 let w = memory.load(addr)?;
259 Ok(w)
260 }),
261 asm::Memory::Free => {
262 let addr = stack.pop()?;
263 memory.free(addr)?;
264 Ok(())
265 }
266 asm::Memory::LoadRange => {
267 let [addr, size] = stack.pop2()?;
268 let words = memory.load_range(addr, size)?;
269 Ok(stack.extend(words)?)
270 }
271 asm::Memory::StoreRange => {
272 let addr = stack.pop()?;
273 stack.pop_len_words(|words| {
274 memory.store_range(addr, words)?;
275 Ok::<_, OpError>(())
276 })?;
277 Ok(())
278 }
279 }
280}
281
282pub fn step_op_parent_memory(
284 op: asm::ParentMemory,
285 stack: &mut Stack,
286 parent_memory: &[Arc<Memory>],
287) -> OpResult<()> {
288 let Some(memory) = parent_memory.last() else {
289 return Err(ParentMemoryError::NoParent.into());
290 };
291 match op {
292 asm::ParentMemory::Load => stack.pop1_push1(|addr| {
293 let w = memory.load(addr)?;
294 Ok(w)
295 }),
296 asm::ParentMemory::LoadRange => {
297 let [addr, size] = stack.pop2()?;
298 let words = memory.load_range(addr, size)?;
299 Ok(stack.extend(words)?)
300 }
301 }
302}
303
304#[cfg(test)]
305pub(crate) mod test_util {
306 use crate::{
307 types::{solution::Solution, ContentAddress, PredicateAddress},
308 *,
309 };
310 use std::sync::Arc;
311
312 pub(crate) const TEST_SET_CA: ContentAddress = ContentAddress([0xFF; 32]);
313 pub(crate) const TEST_PREDICATE_CA: ContentAddress = ContentAddress([0xAA; 32]);
314 pub(crate) const TEST_PREDICATE_ADDR: PredicateAddress = PredicateAddress {
315 contract: TEST_SET_CA,
316 predicate: TEST_PREDICATE_CA,
317 };
318 pub(crate) const TEST_SOLUTION: Solution = Solution {
319 predicate_to_solve: TEST_PREDICATE_ADDR,
320 predicate_data: vec![],
321 state_mutations: vec![],
322 };
323
324 pub(crate) fn test_solutions() -> Arc<Vec<Solution>> {
325 Arc::new(vec![TEST_SOLUTION])
326 }
327
328 pub(crate) fn test_access() -> &'static Access {
329 static INSTANCE: std::sync::LazyLock<Access> = std::sync::LazyLock::new(|| Access {
330 solutions: test_solutions(),
331 index: 0,
332 });
333 &INSTANCE
334 }
335}