1#[cfg(feature = "enable-ffi")]
25pub(crate) mod ffi;
26mod types;
27
28use crate::{constants, ExecResult, InterpreterEnergy, OutOfEnergy};
29use anyhow::{anyhow, bail, ensure};
30use concordium_contracts_common::*;
31use concordium_wasm::{
32 artifact::{Artifact, RunnableCode},
33 machine::{self, ExecutionOutcome, NoInterrupt},
34 utils,
35 validate::ValidationConfig,
36 CostConfiguration,
37};
38use machine::Value;
39use std::{collections::LinkedList, convert::TryInto, io::Write};
40pub use types::*;
41
42impl Logs {
43 pub fn new() -> Self {
45 Self {
46 logs: LinkedList::new(),
47 }
48 }
49
50 pub fn log_event(&mut self, event: Vec<u8>, limit_num_logs: bool) -> i32 {
56 let cur_len = self.logs.len();
57 if (!limit_num_logs && cur_len <= u32::MAX as usize) || cur_len < constants::MAX_NUM_LOGS {
58 self.logs.push_back(event);
59 1
60 } else {
61 0
62 }
63 }
64
65 pub fn iterate(&self) -> impl Iterator<Item = &Vec<u8>> { self.logs.iter() }
68
69 #[cfg(feature = "enable-ffi")]
70 pub(crate) fn to_bytes(&self) -> Vec<u8> {
72 let len = self.logs.len();
73 let mut out = Vec::with_capacity(4 * len + 4);
74 out.extend_from_slice(&(len as u32).to_be_bytes());
75 for v in self.iterate() {
76 out.extend_from_slice(&(v.len() as u32).to_be_bytes());
77 out.extend_from_slice(v);
78 }
79 out
80 }
81}
82
83#[derive(Debug, Clone, Default)]
84pub(crate) struct Outcome {
90 pub cur_state: Vec<Action>,
91}
92
93impl Outcome {
94 pub fn new() -> Outcome { Self::default() }
95
96 pub(crate) fn accept(&mut self) -> u32 {
98 let response = self.cur_state.len();
99 self.cur_state.push(Action::Accept);
100 response as u32
101 }
102
103 pub(crate) fn simple_transfer(&mut self, bytes: &[u8], micro_ccd: u64) -> ExecResult<u32> {
105 let response = self.cur_state.len();
106 let addr: [u8; 32] = bytes.try_into()?;
107 let to_addr = AccountAddress(addr);
108 let data = std::rc::Rc::new(SimpleTransferAction {
109 to_addr,
110 amount: Amount::from_micro_ccd(micro_ccd),
111 });
112 self.cur_state.push(Action::SimpleTransfer {
113 data,
114 });
115 Ok(response as u32)
116 }
117
118 pub(crate) fn send(
120 &mut self,
121 addr_index: u64,
122 addr_subindex: u64,
123 receive_name_bytes: &[u8],
124 micro_ccd: u64,
125 parameter_bytes: &[u8],
126 max_parameter_size: usize,
127 ) -> ExecResult<u32> {
128 let response = self.cur_state.len();
129
130 let name_str = std::str::from_utf8(receive_name_bytes)?;
131 let rn = ReceiveName::new(name_str)?;
132 let name = rn.to_owned();
133
134 ensure!(parameter_bytes.len() <= max_parameter_size, "Parameter exceeds max size.");
135 let parameter = OwnedParameter::new_unchecked(parameter_bytes.to_vec());
136
137 let to_addr = ContractAddress {
138 index: addr_index,
139 subindex: addr_subindex,
140 };
141 let data = std::rc::Rc::new(SendAction {
142 to_addr,
143 name,
144 amount: Amount::from_micro_ccd(micro_ccd),
145 parameter,
146 });
147 self.cur_state.push(Action::Send {
148 data,
149 });
150 Ok(response as u32)
151 }
152
153 pub(crate) fn combine_and(&mut self, l: u32, r: u32) -> ExecResult<u32> {
156 let response = self.cur_state.len() as u32;
157 ensure!(l < response && r < response, "Combining unknown actions.");
158 self.cur_state.push(Action::And {
159 l,
160 r,
161 });
162 Ok(response)
163 }
164
165 pub(crate) fn combine_or(&mut self, l: u32, r: u32) -> ExecResult<u32> {
168 let response = self.cur_state.len() as u32;
169 ensure!(l < response && r < response, "Combining unknown actions.");
170 self.cur_state.push(Action::Or {
171 l,
172 r,
173 });
174 Ok(response)
175 }
176}
177
178impl State {
179 pub fn is_empty(&self) -> bool { self.state.is_empty() }
180
181 pub fn new(st: Option<&[u8]>) -> Self {
183 match st {
184 None => Self {
185 state: Vec::new(),
186 },
187 Some(bytes) => Self {
188 state: Vec::from(bytes),
189 },
190 }
191 }
192
193 pub fn len(&self) -> u32 { self.state.len() as u32 }
194
195 pub(crate) fn write_state(&mut self, offset: u32, bytes: &[u8]) -> ExecResult<u32> {
196 let length = bytes.len();
197 ensure!(offset <= self.len(), "Cannot write past the offset.");
198 let offset = offset as usize;
199 let end =
200 offset.checked_add(length).ok_or_else(|| anyhow!("Writing past the end of memory."))?;
201 let end = std::cmp::min(end, constants::MAX_CONTRACT_STATE as usize) as u32;
202 if self.len() < end {
203 self.state.resize(end as usize, 0u8);
204 }
205 let written = (&mut self.state[offset..end as usize]).write(bytes)?;
206 Ok(written as u32)
207 }
208
209 pub(crate) fn load_state(&self, offset: u32, mut bytes: &mut [u8]) -> ExecResult<u32> {
210 let offset = offset as usize;
211 ensure!(offset <= self.state.len());
212 let amt = bytes.write(&self.state[offset..])?;
215 Ok(amt as u32)
216 }
217
218 pub(crate) fn resize_state(&mut self, new_size: u32) -> u32 {
219 if new_size > constants::MAX_CONTRACT_STATE {
220 0
221 } else {
222 self.state.resize(new_size as usize, 0u8);
223 1
224 }
225 }
226}
227
228#[doc(hidden)] pub struct InitHost<ParamType, Ctx> {
233 pub(crate) energy: InterpreterEnergy,
235 pub(crate) activation_frames: u32,
238 pub(crate) logs: Logs,
240 pub(crate) state: State,
242 pub(crate) param: ParamType,
244 pub(crate) init_ctx: Ctx,
246 pub(crate) limit_logs_and_return_values: bool,
249}
250
251impl<ParamType, Ctx> InitHost<ParamType, Ctx> {
252 pub fn init(
255 energy: InterpreterEnergy,
256 param: ParamType,
257 init_ctx: Ctx,
258 limit_logs_and_return_values: bool,
259 ) -> Self {
260 Self {
261 energy,
262 activation_frames: constants::MAX_ACTIVATION_FRAMES,
263 logs: Logs::new(),
264 state: State::new(None),
265 param,
266 init_ctx,
267 limit_logs_and_return_values,
268 }
269 }
270}
271
272#[derive(Debug)]
275#[doc(hidden)] pub struct ReceiveHost<ParamType, Ctx> {
278 pub(crate) energy: InterpreterEnergy,
280 pub(crate) activation_frames: u32,
283 pub(crate) logs: Logs,
285 pub(crate) state: State,
287 pub(crate) param: ParamType,
289 pub(crate) outcomes: Outcome,
291 pub(crate) receive_ctx: Ctx,
293 pub(crate) max_parameter_size: usize,
297 pub(crate) limit_logs_and_return_values: bool,
300}
301
302impl<ParamType, Ctx> ReceiveHost<ParamType, Ctx> {
303 pub fn init(
305 energy: InterpreterEnergy,
306 state: State,
307 param: ParamType,
308 receive_ctx: Ctx,
309 max_parameter_size: usize,
310 limit_logs_and_return_values: bool,
311 ) -> Self {
312 ReceiveHost {
313 energy,
314 activation_frames: constants::MAX_ACTIVATION_FRAMES,
315 logs: Logs::new(),
316 state,
317 param,
318 outcomes: Default::default(),
319 receive_ctx,
320 max_parameter_size,
321 limit_logs_and_return_values,
322 }
323 }
324}
325
326pub trait HasInitContext {
337 type MetadataType: HasChainMetadata;
338
339 fn metadata(&self) -> &Self::MetadataType;
340 fn init_origin(&self) -> ExecResult<&AccountAddress>;
341 fn sender_policies(&self) -> ExecResult<&[u8]>;
342}
343
344impl<'a, X: HasInitContext> HasInitContext for &'a X {
349 type MetadataType = X::MetadataType;
350
351 fn metadata(&self) -> &Self::MetadataType { (*self).metadata() }
352
353 fn init_origin(&self) -> ExecResult<&AccountAddress> { (*self).init_origin() }
354
355 fn sender_policies(&self) -> ExecResult<&[u8]> { (*self).sender_policies() }
356}
357
358impl<X: AsRef<[u8]>> HasInitContext for InitContext<X> {
359 type MetadataType = ChainMetadata;
360
361 fn metadata(&self) -> &Self::MetadataType { &self.metadata }
362
363 fn init_origin(&self) -> ExecResult<&AccountAddress> { Ok(&self.init_origin) }
364
365 fn sender_policies(&self) -> ExecResult<&[u8]> { Ok(self.sender_policies.as_ref()) }
366}
367
368pub trait HasReceiveContext {
379 type MetadataType: HasChainMetadata;
380
381 fn metadata(&self) -> &Self::MetadataType;
382 fn invoker(&self) -> ExecResult<&AccountAddress>;
383 fn self_address(&self) -> ExecResult<&ContractAddress>;
384 fn self_balance(&self) -> ExecResult<Amount>;
385 fn sender(&self) -> ExecResult<&Address>;
386 fn owner(&self) -> ExecResult<&AccountAddress>;
387 fn sender_policies(&self) -> ExecResult<&[u8]>;
388}
389
390impl<'a, X: HasReceiveContext> HasReceiveContext for &'a X {
395 type MetadataType = X::MetadataType;
396
397 fn metadata(&self) -> &Self::MetadataType { (*self).metadata() }
398
399 fn invoker(&self) -> ExecResult<&AccountAddress> { (*self).invoker() }
400
401 fn self_address(&self) -> ExecResult<&ContractAddress> { (*self).self_address() }
402
403 fn self_balance(&self) -> ExecResult<Amount> { (*self).self_balance() }
404
405 fn sender(&self) -> ExecResult<&Address> { (*self).sender() }
406
407 fn owner(&self) -> ExecResult<&AccountAddress> { (*self).owner() }
408
409 fn sender_policies(&self) -> ExecResult<&[u8]> { (*self).sender_policies() }
410}
411
412impl<X: AsRef<[u8]>> HasReceiveContext for ReceiveContext<X> {
413 type MetadataType = ChainMetadata;
414
415 fn metadata(&self) -> &Self::MetadataType { &self.metadata }
416
417 fn invoker(&self) -> ExecResult<&AccountAddress> { Ok(&self.invoker) }
418
419 fn self_address(&self) -> ExecResult<&ContractAddress> { Ok(&self.self_address) }
420
421 fn self_balance(&self) -> ExecResult<Amount> { Ok(self.self_balance) }
422
423 fn sender(&self) -> ExecResult<&Address> { Ok(&self.sender) }
424
425 fn owner(&self) -> ExecResult<&AccountAddress> { Ok(&self.owner) }
426
427 fn sender_policies(&self) -> ExecResult<&[u8]> { Ok(self.sender_policies.as_ref()) }
428}
429
430pub trait HasChainMetadata {
433 fn slot_time(&self) -> ExecResult<SlotTime>;
436}
437
438impl HasChainMetadata for ChainMetadata {
439 fn slot_time(&self) -> ExecResult<SlotTime> { Ok(self.slot_time) }
440}
441
442pub(crate) mod host {
446 use super::*;
447 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
448 pub(crate) fn get_parameter_size(
449 stack: &mut machine::RuntimeStack,
450 param_len: u32,
451 ) -> machine::RunResult<()> {
452 stack.push_value(param_len);
455 Ok(())
456 }
457
458 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
459 pub(crate) fn get_parameter_section(
460 memory: &mut [u8],
461 stack: &mut machine::RuntimeStack,
462 energy: &mut InterpreterEnergy,
463 param: &[u8],
464 ) -> machine::RunResult<()> {
465 let offset = unsafe { stack.pop_u32() } as usize;
466 let length = unsafe { stack.pop_u32() };
467 let start = unsafe { stack.pop_u32() } as usize;
468 energy.tick_energy(constants::copy_parameter_cost(length))?;
470 let write_end = start + length as usize; ensure!(write_end <= memory.len(), "Illegal memory access.");
472 let end = std::cmp::min(offset + length as usize, param.len());
473 ensure!(offset <= end, "Attempting to read non-existent parameter.");
474 let amt = (&mut memory[start..write_end]).write(¶m[offset..end])?;
475 stack.push_value(amt as u32);
476 Ok(())
477 }
478
479 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
480 pub(crate) fn get_policy_section(
481 memory: &mut [u8],
482 stack: &mut machine::RuntimeStack,
483 energy: &mut InterpreterEnergy,
484 policies: ExecResult<&[u8]>,
485 ) -> machine::RunResult<()> {
486 let offset = unsafe { stack.pop_u32() } as usize;
487 let length = unsafe { stack.pop_u32() };
488 energy.tick_energy(constants::copy_from_host_cost(length))?;
490 let start = unsafe { stack.pop_u32() } as usize;
491 let write_end = start + length as usize; ensure!(write_end <= memory.len(), "Illegal memory access.");
493 let policies_bytes = policies?;
494 let end = std::cmp::min(offset + length as usize, policies_bytes.len());
495 ensure!(offset <= end, "Attempting to read non-existent policy.");
496 let amt = (&mut memory[start..write_end]).write(&policies_bytes[offset..end])?;
497 stack.push_value(amt as u32);
498 Ok(())
499 }
500
501 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
502 pub(crate) fn log_event(
503 memory: &mut [u8],
504 stack: &mut machine::RuntimeStack,
505 energy: &mut InterpreterEnergy,
506 logs: &mut Logs,
507 limit_num_logs: bool,
508 ) -> machine::RunResult<()> {
509 let length = unsafe { stack.pop_u32() };
510 let start = unsafe { stack.pop_u32() } as usize;
511 let end = start + length as usize;
512 ensure!(end <= memory.len(), "Illegal memory access.");
513 if length <= constants::MAX_LOG_SIZE {
514 energy.tick_energy(constants::log_event_cost(length))?;
516 stack.push_value(logs.log_event(memory[start..end].to_vec(), limit_num_logs))
517 } else {
518 stack.push_value(-1i32)
521 }
522 Ok(())
523 }
524
525 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
526 pub(crate) fn load_state(
527 memory: &mut [u8],
528 stack: &mut machine::RuntimeStack,
529 energy: &mut InterpreterEnergy,
530 state: &mut State,
531 ) -> machine::RunResult<()> {
532 let offset = unsafe { stack.pop_u32() };
533 let length = unsafe { stack.pop_u32() };
534 let start = unsafe { stack.pop_u32() } as usize;
535 energy.tick_energy(constants::copy_from_host_cost(length))?;
537 let end = start + length as usize; ensure!(end <= memory.len(), "Illegal memory access.");
539 let res = state.load_state(offset, &mut memory[start..end])?;
540 stack.push_value(res);
541 Ok(())
542 }
543
544 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
545 pub(crate) fn write_state(
546 memory: &mut [u8],
547 stack: &mut machine::RuntimeStack,
548 energy: &mut InterpreterEnergy,
549 state: &mut State,
550 ) -> machine::RunResult<()> {
551 let offset = unsafe { stack.pop_u32() };
552 let length = unsafe { stack.pop_u32() };
553 let start = unsafe { stack.pop_u32() } as usize;
554 energy.tick_energy(constants::copy_to_host_cost(length))?;
556 let end = start + length as usize; ensure!(end <= memory.len(), "Illegal memory access.");
558 let res = state.write_state(offset, &memory[start..end])?;
559 stack.push_value(res);
560 Ok(())
561 }
562
563 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
564 pub(crate) fn resize_state(
565 stack: &mut machine::RuntimeStack,
566 energy: &mut InterpreterEnergy,
567 state: &mut State,
568 ) -> machine::RunResult<()> {
569 let new_size = stack.pop();
570 let new_size = unsafe { new_size.short } as u32;
571 let old_size = state.len();
572 if new_size > old_size {
573 energy.tick_energy(constants::additional_state_size_cost(u64::from(
576 new_size - old_size,
577 )))?;
578 }
579 stack.push_value(state.resize_state(new_size));
580 Ok(())
581 }
582
583 #[inline(always)]
584 pub(crate) fn state_size(
585 stack: &mut machine::RuntimeStack,
586 state: &mut State,
587 ) -> machine::RunResult<()> {
588 stack.push_value(state.len());
591 Ok(())
592 }
593
594 #[inline(always)]
595 pub(crate) fn get_slot_time(
596 stack: &mut machine::RuntimeStack,
597 metadata: &impl HasChainMetadata,
598 ) -> machine::RunResult<()> {
599 stack.push_value(metadata.slot_time()?.timestamp_millis());
602 Ok(())
603 }
604
605 pub(crate) fn get_init_origin(
606 memory: &mut [u8],
607 stack: &mut machine::RuntimeStack,
608 init_origin: ExecResult<&AccountAddress>,
609 ) -> machine::RunResult<()> {
610 let start = unsafe { stack.pop_u32() } as usize;
611 ensure!(start + 32 <= memory.len(), "Illegal memory access for init origin.");
612 (&mut memory[start..start + 32]).write_all(init_origin?.as_ref())?;
613 Ok(())
614 }
615
616 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
617 pub(crate) fn accept(
618 stack: &mut machine::RuntimeStack,
619 energy: &mut InterpreterEnergy,
620 outcomes: &mut Outcome,
621 ) -> machine::RunResult<()> {
622 energy.tick_energy(constants::BASE_ACTION_COST)?;
623 stack.push_value(outcomes.accept());
624 Ok(())
625 }
626
627 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
628 pub(crate) fn simple_transfer(
629 memory: &mut [u8],
630 stack: &mut machine::RuntimeStack,
631 energy: &mut InterpreterEnergy,
632 outcomes: &mut Outcome,
633 ) -> machine::RunResult<()> {
634 energy.tick_energy(constants::BASE_ACTION_COST)?;
635 let amount = unsafe { stack.pop_u64() };
636 let addr_start = unsafe { stack.pop_u32() } as usize;
637 ensure!(addr_start + 32 <= memory.len(), "Illegal memory access.");
639 stack.push_value(outcomes.simple_transfer(&memory[addr_start..addr_start + 32], amount)?);
640 Ok(())
641 }
642
643 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
644 pub(crate) fn send(
645 memory: &mut [u8],
646 stack: &mut machine::RuntimeStack,
647 energy: &mut InterpreterEnergy,
648 outcomes: &mut Outcome,
649 max_parameter_size: usize,
650 ) -> machine::RunResult<()> {
651 let parameter_len = unsafe { stack.pop_u32() };
653 energy.tick_energy(constants::action_send_cost(parameter_len))?;
654 let parameter_start = unsafe { stack.pop_u32() } as usize;
655 let parameter_end = parameter_start + parameter_len as usize;
657 let amount = unsafe { stack.pop_u64() };
658 let receive_name_len = unsafe { stack.pop_u32() } as usize;
659 let receive_name_start = unsafe { stack.pop_u32() } as usize;
660 let receive_name_end = receive_name_start + receive_name_len;
662 let addr_subindex = unsafe { stack.pop_u64() };
663 let addr_index = unsafe { stack.pop_u64() };
664 ensure!(parameter_end <= memory.len(), "Illegal memory access.");
665 ensure!(receive_name_end <= memory.len(), "Illegal memory access.");
666 let res = outcomes.send(
667 addr_index,
668 addr_subindex,
669 &memory[receive_name_start..receive_name_end],
670 amount,
671 &memory[parameter_start..parameter_end],
672 max_parameter_size,
673 )?;
674 stack.push_value(res);
675 Ok(())
676 }
677
678 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
679 pub(crate) fn combine_and(
680 stack: &mut machine::RuntimeStack,
681 energy: &mut InterpreterEnergy,
682 outcomes: &mut Outcome,
683 ) -> machine::RunResult<()> {
684 energy.tick_energy(constants::BASE_ACTION_COST)?;
685 let right = unsafe { stack.pop_u32() };
686 let left = unsafe { stack.pop_u32() };
687 let res = outcomes.combine_and(left, right)?;
688 stack.push_value(res);
689 Ok(())
690 }
691
692 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
693 pub(crate) fn combine_or(
694 stack: &mut machine::RuntimeStack,
695 energy: &mut InterpreterEnergy,
696 outcomes: &mut Outcome,
697 ) -> machine::RunResult<()> {
698 energy.tick_energy(constants::BASE_ACTION_COST)?;
699 let right = unsafe { stack.pop_u32() };
700 let left = unsafe { stack.pop_u32() };
701 let res = outcomes.combine_or(left, right)?;
702 stack.push_value(res);
703 Ok(())
704 }
705
706 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
707 pub(crate) fn get_receive_invoker(
708 memory: &mut [u8],
709 stack: &mut machine::RuntimeStack,
710 invoker: ExecResult<&AccountAddress>,
711 ) -> machine::RunResult<()> {
712 let start = unsafe { stack.pop_u32() } as usize;
713 ensure!(start + 32 <= memory.len(), "Illegal memory access for receive invoker.");
714 (&mut memory[start..start + 32]).write_all(invoker?.as_ref())?;
715 Ok(())
716 }
717
718 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
719 pub(crate) fn get_receive_self_address(
720 memory: &mut [u8],
721 stack: &mut machine::RuntimeStack,
722 self_address: ExecResult<&ContractAddress>,
723 ) -> machine::RunResult<()> {
724 let start = unsafe { stack.pop_u32() } as usize;
725 ensure!(start + 16 <= memory.len(), "Illegal memory access for receive owner.");
726 let self_address = self_address?;
727 (&mut memory[start..start + 8]).write_all(&self_address.index.to_le_bytes())?;
728 (&mut memory[start + 8..start + 16]).write_all(&self_address.subindex.to_le_bytes())?;
729 Ok(())
730 }
731
732 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
733 pub(crate) fn get_receive_self_balance(
734 stack: &mut machine::RuntimeStack,
735 self_balance: ExecResult<Amount>,
736 ) -> machine::RunResult<()> {
737 stack.push_value(self_balance?.micro_ccd);
738 Ok(())
739 }
740
741 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
742 pub(crate) fn get_receive_sender(
743 memory: &mut [u8],
744 stack: &mut machine::RuntimeStack,
745 sender: ExecResult<&Address>,
746 ) -> machine::RunResult<()> {
747 let start = unsafe { stack.pop_u32() } as usize;
748 ensure!(start < memory.len(), "Illegal memory access for receive sender.");
749 sender?
750 .serial::<&mut [u8]>(&mut &mut memory[start..])
751 .map_err(|_| anyhow!("Memory out of bounds."))?;
752 Ok(())
753 }
754
755 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
756 pub(crate) fn get_receive_owner(
757 memory: &mut [u8],
758 stack: &mut machine::RuntimeStack,
759 owner: ExecResult<&AccountAddress>,
760 ) -> machine::RunResult<()> {
761 let start = unsafe { stack.pop_u32() } as usize;
762 ensure!(start + 32 <= memory.len(), "Illegal memory access for receive owner.");
763 (&mut memory[start..start + 32]).write_all(owner?.as_ref())?;
764 Ok(())
765 }
766
767 #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
768 pub(crate) fn track_call(activation_frames: &mut u32) -> machine::RunResult<()> {
769 if let Some(fr) = activation_frames.checked_sub(1) {
770 *activation_frames = fr;
771 Ok(())
772 } else {
773 bail!("Too many nested functions.")
774 }
775 }
776
777 #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
778 pub(crate) fn track_return(activation_frames: &mut u32) { *activation_frames += 1; }
779
780 #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
781 pub(crate) fn charge_memory_alloc(
782 stack: &mut machine::RuntimeStack,
783 energy: &mut InterpreterEnergy,
784 ) -> machine::RunResult<()> {
785 energy.charge_memory_alloc(unsafe { stack.peek_u32() })
786 }
787}
788
789impl<ParamType: AsRef<[u8]>, Ctx: HasInitContext> machine::Host<ProcessedImports>
790 for InitHost<ParamType, Ctx>
791{
792 type Interrupt = NoInterrupt;
793
794 #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
795 fn tick_initial_memory(&mut self, num_pages: u32) -> machine::RunResult<()> {
796 self.energy.charge_memory_alloc(num_pages)
797 }
798
799 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
800 fn call(
801 &mut self,
802 f: &ProcessedImports,
803 memory: &mut [u8],
804 stack: &mut machine::RuntimeStack,
805 ) -> machine::RunResult<Option<NoInterrupt>> {
806 match f.tag {
807 ImportFunc::ChargeMemoryAlloc => host::charge_memory_alloc(stack, &mut self.energy)?,
808 ImportFunc::Common(cf) => match cf {
809 CommonFunc::GetParameterSize => {
810 host::get_parameter_size(stack, self.param.as_ref().len() as u32)
811 }
812 CommonFunc::GetParameterSection => host::get_parameter_section(
813 memory,
814 stack,
815 &mut self.energy,
816 self.param.as_ref(),
817 ),
818 CommonFunc::GetPolicySection => host::get_policy_section(
819 memory,
820 stack,
821 &mut self.energy,
822 self.init_ctx.sender_policies(),
823 ),
824 CommonFunc::LogEvent => host::log_event(
825 memory,
826 stack,
827 &mut self.energy,
828 &mut self.logs,
829 self.limit_logs_and_return_values,
830 ),
831 CommonFunc::LoadState => {
832 host::load_state(memory, stack, &mut self.energy, &mut self.state)
833 }
834 CommonFunc::WriteState => {
835 host::write_state(memory, stack, &mut self.energy, &mut self.state)
836 }
837 CommonFunc::ResizeState => {
838 host::resize_state(stack, &mut self.energy, &mut self.state)
839 }
840 CommonFunc::StateSize => host::state_size(stack, &mut self.state),
841 CommonFunc::GetSlotTime => host::get_slot_time(stack, self.init_ctx.metadata()),
842 }?,
843 ImportFunc::InitOnly(InitOnlyFunc::GetInitOrigin) => {
844 host::get_init_origin(memory, stack, self.init_ctx.init_origin())?
845 }
846 ImportFunc::ReceiveOnly(_) => {
847 bail!("Not implemented for init {:#?}.", f);
848 }
849 }
850 Ok(None)
851 }
852
853 #[inline(always)]
854 fn tick_energy(&mut self, energy: u64) -> machine::RunResult<()> {
855 self.energy.tick_energy(energy)
856 }
857
858 #[inline(always)]
859 fn track_call(&mut self) -> machine::RunResult<()> {
860 host::track_call(&mut self.activation_frames)
861 }
862
863 #[inline(always)]
864 fn track_return(&mut self) { host::track_return(&mut self.activation_frames) }
865}
866
867impl<ParamType: AsRef<[u8]>, Ctx: HasReceiveContext> machine::Host<ProcessedImports>
868 for ReceiveHost<ParamType, Ctx>
869{
870 type Interrupt = NoInterrupt;
871
872 #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
873 fn tick_initial_memory(&mut self, num_pages: u32) -> machine::RunResult<()> {
874 self.energy.charge_memory_alloc(num_pages)
875 }
876
877 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
878 fn call(
879 &mut self,
880 f: &ProcessedImports,
881 memory: &mut [u8],
882 stack: &mut machine::RuntimeStack,
883 ) -> machine::RunResult<Option<NoInterrupt>> {
884 match f.tag {
885 ImportFunc::ChargeMemoryAlloc => host::charge_memory_alloc(stack, &mut self.energy)?,
886 ImportFunc::Common(cf) => match cf {
887 CommonFunc::GetParameterSize => {
888 host::get_parameter_size(stack, self.param.as_ref().len() as u32)
889 }
890 CommonFunc::GetParameterSection => host::get_parameter_section(
891 memory,
892 stack,
893 &mut self.energy,
894 self.param.as_ref(),
895 ),
896 CommonFunc::GetPolicySection => host::get_policy_section(
897 memory,
898 stack,
899 &mut self.energy,
900 self.receive_ctx.sender_policies(),
901 ),
902 CommonFunc::LogEvent => host::log_event(
903 memory,
904 stack,
905 &mut self.energy,
906 &mut self.logs,
907 self.limit_logs_and_return_values,
908 ),
909 CommonFunc::LoadState => {
910 host::load_state(memory, stack, &mut self.energy, &mut self.state)
911 }
912 CommonFunc::WriteState => {
913 host::write_state(memory, stack, &mut self.energy, &mut self.state)
914 }
915 CommonFunc::ResizeState => {
916 host::resize_state(stack, &mut self.energy, &mut self.state)
917 }
918 CommonFunc::StateSize => host::state_size(stack, &mut self.state),
919 CommonFunc::GetSlotTime => host::get_slot_time(stack, self.receive_ctx.metadata()),
920 }?,
921 ImportFunc::ReceiveOnly(rof) => match rof {
922 ReceiveOnlyFunc::Accept => {
923 host::accept(stack, &mut self.energy, &mut self.outcomes)
924 }
925 ReceiveOnlyFunc::SimpleTransfer => {
926 host::simple_transfer(memory, stack, &mut self.energy, &mut self.outcomes)
927 }
928 ReceiveOnlyFunc::Send => host::send(
929 memory,
930 stack,
931 &mut self.energy,
932 &mut self.outcomes,
933 self.max_parameter_size,
934 ),
935 ReceiveOnlyFunc::CombineAnd => {
936 host::combine_and(stack, &mut self.energy, &mut self.outcomes)
937 }
938 ReceiveOnlyFunc::CombineOr => {
939 host::combine_or(stack, &mut self.energy, &mut self.outcomes)
940 }
941 ReceiveOnlyFunc::GetReceiveInvoker => {
942 host::get_receive_invoker(memory, stack, self.receive_ctx.invoker())
943 }
944 ReceiveOnlyFunc::GetReceiveSelfAddress => {
945 host::get_receive_self_address(memory, stack, self.receive_ctx.self_address())
946 }
947 ReceiveOnlyFunc::GetReceiveSelfBalance => {
948 host::get_receive_self_balance(stack, self.receive_ctx.self_balance())
949 }
950 ReceiveOnlyFunc::GetReceiveSender => {
951 host::get_receive_sender(memory, stack, self.receive_ctx.sender())
952 }
953 ReceiveOnlyFunc::GetReceiveOwner => {
954 host::get_receive_owner(memory, stack, self.receive_ctx.owner())
955 }
956 }?,
957 ImportFunc::InitOnly(InitOnlyFunc::GetInitOrigin) => {
958 bail!("Not implemented for receive.");
959 }
960 }
961 Ok(None)
962 }
963
964 #[inline(always)]
965 fn tick_energy(&mut self, energy: u64) -> machine::RunResult<()> {
966 self.energy.tick_energy(energy)
967 }
968
969 #[inline(always)]
970 fn track_call(&mut self) -> machine::RunResult<()> {
971 host::track_call(&mut self.activation_frames)
972 }
973
974 #[inline(always)]
975 fn track_return(&mut self) { host::track_return(&mut self.activation_frames) }
976}
977
978#[derive(Debug)]
980pub struct InitInvocation<'a> {
981 pub amount: u64,
983 pub init_name: &'a str,
985 pub parameter: Parameter<'a>,
987 pub energy: InterpreterEnergy,
989}
990
991pub fn invoke_init<C: RunnableCode, Ctx: HasInitContext>(
993 artifact: &Artifact<ProcessedImports, C>,
994 init_ctx: Ctx,
995 init_invocation: InitInvocation,
996 limit_logs_and_return_values: bool,
997) -> ExecResult<InitResult> {
998 let mut host = InitHost {
999 energy: init_invocation.energy,
1000 activation_frames: constants::MAX_ACTIVATION_FRAMES,
1001 logs: Logs::new(),
1002 state: State::new(None),
1003 param: init_invocation.parameter,
1004 limit_logs_and_return_values,
1005 init_ctx,
1006 };
1007
1008 let res = match artifact
1009 .run(&mut host, init_invocation.init_name, &[Value::I64(init_invocation.amount as i64)])
1010 {
1011 Ok(ExecutionOutcome::Success {
1012 result,
1013 ..
1014 }) => result,
1015 Ok(ExecutionOutcome::Interrupted {
1016 reason,
1017 ..
1018 }) => match reason {}, Err(e) => {
1020 if e.downcast_ref::<OutOfEnergy>().is_some() {
1021 return Ok(InitResult::OutOfEnergy);
1022 } else {
1023 return Err(e);
1024 }
1025 }
1026 };
1027 let remaining_energy = host.energy.energy;
1028 if let Some(Value::I32(n)) = res {
1033 if n == 0 {
1034 Ok(InitResult::Success {
1035 logs: host.logs,
1036 state: host.state,
1037 remaining_energy: remaining_energy.into(),
1038 })
1039 } else {
1040 Ok(InitResult::Reject {
1041 reason: reason_from_wasm_error_code(n)?,
1042 remaining_energy: remaining_energy.into(),
1043 })
1044 }
1045 } else {
1046 bail!("Wasm module should return a value.")
1047 }
1048}
1049
1050#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1052pub fn invoke_init_from_artifact<Ctx: HasInitContext>(
1053 artifact_bytes: &[u8],
1054 amount: u64,
1055 init_ctx: Ctx,
1056 init_name: &str,
1057 parameter: Parameter,
1058 limit_logs_and_return_values: bool,
1059 energy: InterpreterEnergy,
1060) -> ExecResult<InitResult> {
1061 let artifact = utils::parse_artifact(artifact_bytes)?;
1062 invoke_init(
1063 &artifact,
1064 init_ctx,
1065 InitInvocation {
1066 amount,
1067 init_name,
1068 parameter,
1069 energy,
1070 },
1071 limit_logs_and_return_values,
1072 )
1073}
1074
1075#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1077pub fn invoke_init_from_source<Ctx: HasInitContext>(
1078 source_bytes: &[u8],
1079 amount: u64,
1080 init_ctx: Ctx,
1081 init_name: &str,
1082 parameter: Parameter,
1083 limit_logs_and_return_values: bool,
1084 energy: InterpreterEnergy,
1085) -> ExecResult<InitResult> {
1086 let artifact =
1087 utils::instantiate(ValidationConfig::V0, &ConcordiumAllowedImports, source_bytes)?.artifact;
1088 invoke_init(
1089 &artifact,
1090 init_ctx,
1091 InitInvocation {
1092 amount,
1093 init_name,
1094 parameter,
1095 energy,
1096 },
1097 limit_logs_and_return_values,
1098 )
1099}
1100
1101#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1104#[allow(clippy::too_many_arguments)]
1105pub fn invoke_init_with_metering_from_source<Ctx: HasInitContext>(
1106 source_bytes: &[u8],
1107 amount: u64,
1108 init_ctx: Ctx,
1109 init_name: &str,
1110 parameter: Parameter,
1111 limit_logs_and_return_values: bool,
1112 cost_config: impl CostConfiguration,
1113 energy: InterpreterEnergy,
1114) -> ExecResult<InitResult> {
1115 let artifact = utils::instantiate_with_metering(
1116 ValidationConfig::V0,
1117 cost_config,
1118 &ConcordiumAllowedImports,
1119 source_bytes,
1120 )?
1121 .artifact;
1122 invoke_init(
1123 &artifact,
1124 init_ctx,
1125 InitInvocation {
1126 amount,
1127 init_name,
1128 parameter,
1129 energy,
1130 },
1131 limit_logs_and_return_values,
1132 )
1133}
1134
1135#[derive(Debug)]
1137pub struct ReceiveInvocation<'a> {
1138 pub amount: u64,
1140 pub receive_name: &'a str,
1142 pub parameter: Parameter<'a>,
1144 pub energy: InterpreterEnergy,
1146}
1147
1148pub fn invoke_receive<C: RunnableCode, Ctx: HasReceiveContext>(
1150 artifact: &Artifact<ProcessedImports, C>,
1151 receive_ctx: Ctx,
1152 receive_invocation: ReceiveInvocation,
1153 current_state: &[u8],
1154 max_parameter_size: usize,
1155 limit_logs_and_return_values: bool,
1156) -> ExecResult<ReceiveResult> {
1157 let mut host = ReceiveHost {
1158 energy: receive_invocation.energy,
1159 activation_frames: constants::MAX_ACTIVATION_FRAMES,
1160 logs: Logs::new(),
1161 state: State::new(Some(current_state)),
1162 param: &receive_invocation.parameter,
1163 max_parameter_size,
1164 limit_logs_and_return_values,
1165 receive_ctx,
1166 outcomes: Outcome::new(),
1167 };
1168
1169 let res = match artifact.run(&mut host, receive_invocation.receive_name, &[Value::I64(
1170 receive_invocation.amount as i64,
1171 )]) {
1172 Ok(ExecutionOutcome::Success {
1173 result,
1174 ..
1175 }) => result,
1176 Ok(ExecutionOutcome::Interrupted {
1177 reason,
1178 ..
1179 }) => match reason {}, Err(e) => {
1181 if e.downcast_ref::<OutOfEnergy>().is_some() {
1182 return Ok(ReceiveResult::OutOfEnergy);
1183 } else {
1184 return Err(e);
1185 }
1186 }
1187 };
1188 let remaining_energy = host.energy.energy;
1189 if let Some(Value::I32(n)) = res {
1190 let mut actions = host.outcomes.cur_state;
1193 if n >= 0 && (n as usize) < actions.len() {
1194 let n = n as usize;
1195 actions.truncate(n + 1);
1196 Ok(ReceiveResult::Success {
1197 logs: host.logs,
1198 state: host.state,
1199 actions,
1200 remaining_energy: remaining_energy.into(),
1201 })
1202 } else if n >= 0 {
1203 bail!("Invalid return.")
1204 } else {
1205 Ok(ReceiveResult::Reject {
1206 reason: reason_from_wasm_error_code(n)?,
1207 remaining_energy: remaining_energy.into(),
1208 })
1209 }
1210 } else {
1211 bail!(
1212 "Invalid return. Expected a value, but receive nothing. This should not happen for \
1213 well-formed modules"
1214 );
1215 }
1216}
1217
1218fn reason_from_wasm_error_code(n: i32) -> ExecResult<i32> {
1221 ensure!(
1222 n < 0,
1223 "Wasm return value of {} is treated as an error. Only negative should be treated as error.",
1224 n
1225 );
1226 Ok(n)
1227}
1228
1229#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1231pub fn invoke_receive_from_artifact<Ctx: HasReceiveContext>(
1232 artifact_bytes: &[u8],
1233 receive_ctx: Ctx,
1234 receive_invocation: ReceiveInvocation,
1235 current_state: &[u8],
1236 max_parameter_size: usize,
1237 limit_logs_and_return_values: bool,
1238) -> ExecResult<ReceiveResult> {
1239 let artifact = utils::parse_artifact(artifact_bytes)?;
1240 invoke_receive(
1241 &artifact,
1242 receive_ctx,
1243 receive_invocation,
1244 current_state,
1245 max_parameter_size,
1246 limit_logs_and_return_values,
1247 )
1248}
1249
1250#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1252pub fn invoke_receive_from_source<Ctx: HasReceiveContext>(
1253 source_bytes: &[u8],
1254 receive_ctx: Ctx,
1255 receive_invocation: ReceiveInvocation,
1256 current_state: &[u8],
1257 max_parameter_size: usize,
1258 limit_logs_and_return_values: bool,
1259) -> ExecResult<ReceiveResult> {
1260 let artifact =
1261 utils::instantiate(ValidationConfig::V0, &ConcordiumAllowedImports, source_bytes)?.artifact;
1262 invoke_receive(
1263 &artifact,
1264 receive_ctx,
1265 receive_invocation,
1266 current_state,
1267 max_parameter_size,
1268 limit_logs_and_return_values,
1269 )
1270}
1271
1272#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1275pub fn invoke_receive_with_metering_from_source<Ctx: HasReceiveContext>(
1276 source_bytes: &[u8],
1277 receive_ctx: Ctx,
1278 receive_invocation: ReceiveInvocation,
1279 current_state: &[u8],
1280 max_parameter_size: usize,
1281 limit_logs_and_return_values: bool,
1282 cost_config: impl CostConfiguration,
1283) -> ExecResult<ReceiveResult> {
1284 let artifact = utils::instantiate_with_metering(
1285 ValidationConfig::V0,
1286 cost_config,
1287 &ConcordiumAllowedImports,
1288 source_bytes,
1289 )?
1290 .artifact;
1291 invoke_receive(
1292 &artifact,
1293 receive_ctx,
1294 receive_invocation,
1295 current_state,
1296 max_parameter_size,
1297 limit_logs_and_return_values,
1298 )
1299}