essential_vm/
sync.rs

1//! Items related to stepping forward VM execution by synchronous operations.
2
3use 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
15/// Step forward execution by the given synchronous operation.
16/// This includes the synchronous state read operation.
17pub 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
85/// Step forward execution by the given state reads operation.
86pub 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
112/// Step forward execution by the given compute operation.
113pub 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
131/// Step forward execution by the given access operation.
132pub 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
159/// Step forward execution by the given ALU operation.
160pub 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
173/// Step forward execution by the given crypto operation.
174pub 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
182/// Step forward execution by the given predicate operation.
183pub 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
200/// Step forward execution by the given stack operation.
201pub 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
229/// Step forward execution by the given total control flow operation.
230pub 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
243/// Step forward execution by the given memory operation.
244pub 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
282/// Step forward execution by the given parent memory operation.
283pub 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}