1#[cfg(test)]
29mod crypto_primitives_tests;
30#[cfg(test)]
31mod tests;
32
33#[cfg(feature = "enable-ffi")]
34mod ffi;
35pub mod trie;
36mod types;
37
38use crate::{constants, v0, DebugInfo, ExecResult, InterpreterEnergy, OutOfEnergy};
39use anyhow::{bail, ensure};
40use concordium_contracts_common::{
41 AccountAddress, Address, Amount, ChainMetadata, ContractAddress, EntrypointName,
42 ModuleReference, OwnedEntrypointName, ReceiveName,
43};
44use concordium_wasm::{
45 artifact::{Artifact, CompiledFunction, CompiledFunctionBytes, RunnableCode},
46 machine::{self, ExecutionOutcome, NoInterrupt},
47 utils,
48 validate::ValidationConfig,
49 CostConfiguration,
50};
51use machine::Value;
52use sha3::Digest;
53use std::{borrow::Borrow, collections::BTreeMap, io::Write, sync::Arc};
54use trie::BackingStoreLoad;
55pub use types::*;
56
57#[derive(thiserror::Error, Debug)]
58#[error("Unexpected return value from the invocation: {value:?}")]
62pub struct InvalidReturnCodeError<Debug> {
63 pub value: Option<i32>,
64 pub debug_trace: Debug,
65}
66
67impl<R, Debug: DebugInfo, Ctx> From<InvalidReturnCodeError<Debug>>
68 for types::ReceiveResult<R, Debug, Ctx>
69{
70 fn from(value: InvalidReturnCodeError<Debug>) -> Self {
71 Self::Trap {
72 error: anyhow::anyhow!("Invalid return code: {:?}", value.value),
73 remaining_energy: 0.into(), trace: value.debug_trace,
75 }
76 }
77}
78
79pub type InvokeResult<A, Debug> = Result<A, InvalidReturnCodeError<Debug>>;
81
82#[derive(Debug)]
86pub enum Interrupt {
87 Transfer {
89 to: AccountAddress,
90 amount: Amount,
91 },
92 Call {
94 address: ContractAddress,
95 parameter: ParameterVec,
96 name: OwnedEntrypointName,
97 amount: Amount,
98 },
99 Upgrade {
101 module_ref: ModuleReference,
102 },
103 QueryAccountBalance {
105 address: AccountAddress,
106 },
107 QueryContractBalance {
109 address: ContractAddress,
110 },
111 QueryExchangeRates,
113 CheckAccountSignature {
115 address: AccountAddress,
116 payload: Vec<u8>,
117 },
118 QueryAccountKeys {
120 address: AccountAddress,
121 },
122 QueryContractModuleReference {
124 address: ContractAddress,
125 },
126 QueryContractName {
128 address: ContractAddress,
129 },
130}
131
132impl Interrupt {
133 pub(crate) fn should_clear_logs(&self) -> bool {
143 match self {
144 Interrupt::Transfer {
145 ..
146 } => true,
147 Interrupt::Call {
148 ..
149 } => true,
150 Interrupt::Upgrade {
151 ..
152 } => true,
153 Interrupt::QueryAccountBalance {
154 ..
155 } => false,
156 Interrupt::QueryContractBalance {
157 ..
158 } => false,
159 Interrupt::QueryExchangeRates => false,
160 Interrupt::CheckAccountSignature {
161 ..
162 } => false,
163 Interrupt::QueryAccountKeys {
164 ..
165 } => false,
166 Interrupt::QueryContractModuleReference {
167 ..
168 } => false,
169 Interrupt::QueryContractName {
170 ..
171 } => false,
172 }
173 }
174}
175
176impl Interrupt {
177 pub fn to_bytes(&self, out: &mut Vec<u8>) -> anyhow::Result<()> {
178 match self {
179 Interrupt::Transfer {
180 to,
181 amount,
182 } => {
183 out.push(0u8);
184 out.write_all(to.as_ref())?;
185 out.write_all(&amount.micro_ccd.to_be_bytes())?;
186 Ok(())
187 }
188 Interrupt::Call {
189 address,
190 parameter,
191 name,
192 amount,
193 } => {
194 out.push(1u8);
195 out.write_all(&address.index.to_be_bytes())?;
196 out.write_all(&address.subindex.to_be_bytes())?;
197 out.write_all(&(parameter.len() as u16).to_be_bytes())?;
198 out.write_all(parameter)?;
199 let name_str: &str = name.as_entrypoint_name().into();
200 out.write_all(&(name_str.as_bytes().len() as u16).to_be_bytes())?;
201 out.write_all(name_str.as_bytes())?;
202 out.write_all(&amount.micro_ccd.to_be_bytes())?;
203 Ok(())
204 }
205 Interrupt::Upgrade {
206 module_ref,
207 } => {
208 out.push(2u8);
209 out.write_all(module_ref.as_ref())?;
210 Ok(())
211 }
212 Interrupt::QueryAccountBalance {
213 address,
214 } => {
215 out.push(3u8);
216 out.write_all(address.as_ref())?;
217 Ok(())
218 }
219 Interrupt::QueryContractBalance {
220 address,
221 } => {
222 out.push(4u8);
223 out.write_all(&address.index.to_be_bytes())?;
224 out.write_all(&address.subindex.to_be_bytes())?;
225 Ok(())
226 }
227 Interrupt::QueryExchangeRates => {
228 out.push(5u8);
229 Ok(())
230 }
231 Interrupt::CheckAccountSignature {
232 address,
233 payload,
234 } => {
235 out.push(6u8);
236 out.write_all(address.as_ref())?;
237 out.write_all(&(payload.len() as u64).to_be_bytes())?;
238 out.write_all(payload)?;
239 Ok(())
240 }
241 Interrupt::QueryAccountKeys {
242 address,
243 } => {
244 out.push(7u8);
245 out.write_all(address.as_ref())?;
246 Ok(())
247 }
248 Interrupt::QueryContractModuleReference {
249 address,
250 } => {
251 out.push(8u8);
252 out.write_all(&address.index.to_be_bytes())?;
253 out.write_all(&address.subindex.to_be_bytes())?;
254 Ok(())
255 }
256 Interrupt::QueryContractName {
257 address,
258 } => {
259 out.push(9u8);
260 out.write_all(&address.index.to_be_bytes())?;
261 out.write_all(&address.subindex.to_be_bytes())?;
262 Ok(())
263 }
264 }
265 }
266}
267
268#[derive(Debug)]
269pub(crate) struct InitHost<'a, BackingStore, ParamType, Ctx, A: DebugInfo> {
276 pub energy: InterpreterEnergy,
278 pub activation_frames: u32,
281 pub logs: v0::Logs,
283 pub state: InstanceState<'a, BackingStore>,
285 pub return_value: ReturnValue,
287 pub parameter: ParamType,
289 pub init_ctx: Ctx,
291 limit_logs_and_return_values: bool,
294 pub trace: A,
295}
296
297impl<'a, 'b, BackingStore, Ctx2, Ctx1: Into<Ctx2>, A: DebugInfo>
298 From<InitHost<'b, BackingStore, ParameterRef<'a>, Ctx1, A>>
299 for InitHost<'b, BackingStore, ParameterVec, Ctx2, A>
300{
301 fn from(host: InitHost<'b, BackingStore, ParameterRef<'a>, Ctx1, A>) -> Self {
302 Self {
303 energy: host.energy,
304 activation_frames: host.activation_frames,
305 logs: host.logs,
306 state: host.state,
307 return_value: host.return_value,
308 parameter: host.parameter.into(),
309 init_ctx: host.init_ctx.into(),
310 limit_logs_and_return_values: host.limit_logs_and_return_values,
311 trace: host.trace,
312 }
313 }
314}
315
316#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
317pub enum HostFunctionV1 {
319 Common(CommonFunc),
321 Init(InitOnlyFunc),
323 Receive(ReceiveOnlyFunc),
325}
326
327impl std::fmt::Display for HostFunctionV1 {
330 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331 match self {
332 HostFunctionV1::Common(c) => c.fmt(f),
333 HostFunctionV1::Init(io) => io.fmt(f),
334 HostFunctionV1::Receive(ro) => ro.fmt(f),
335 }
336 }
337}
338
339#[derive(Debug, Copy, Clone)]
340pub struct HostCall {
342 pub host_function: HostFunctionV1,
344 pub energy_used: InterpreterEnergy,
346}
347
348#[derive(Default, Debug)]
349pub struct DebugTracker {
352 pub operation: InterpreterEnergy,
355 pub memory_alloc: InterpreterEnergy,
358 pub host_call_trace: Vec<(usize, HostCall)>,
363 pub emitted_events: Vec<(usize, EmittedDebugStatement)>,
366 next_index: usize,
368}
369
370impl std::fmt::Display for DebugTracker {
374 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
375 let DebugTracker {
376 operation,
377 memory_alloc,
378 host_call_trace,
379 emitted_events,
380 next_index: _,
381 } = self;
382 writeln!(f, "Wasm instruction cost: {operation}")?;
383 writeln!(f, "Memory alocation cost: {memory_alloc}")?;
384 let mut iter1 = host_call_trace.iter().peekable();
385 let mut iter2 = emitted_events.iter().peekable();
386 while let (Some((i1, call)), Some((i2, event))) = (iter1.peek(), iter2.peek()) {
387 if i1 < i2 {
388 iter1.next();
389 writeln!(f, "{} used {} interpreter energy", call.host_function, call.energy_used)?;
390 } else {
391 iter2.next();
392 writeln!(f, "{event}")?;
393 }
394 }
395 for (
396 _,
397 HostCall {
398 host_function,
399 energy_used,
400 },
401 ) in iter1
402 {
403 writeln!(f, "{host_function} used {energy_used} interpreter energy")?;
404 }
405 for (_, event) in iter2 {
406 writeln!(f, "{event}")?;
407 }
408 Ok(())
409 }
410}
411
412impl DebugTracker {
413 pub fn host_call_summary(&self) -> BTreeMap<HostFunctionV1, (usize, InterpreterEnergy)> {
418 let mut out = BTreeMap::new();
419 for (
420 _,
421 HostCall {
422 host_function,
423 energy_used,
424 },
425 ) in self.host_call_trace.iter()
426 {
427 let summary = out.entry(*host_function).or_insert((0, InterpreterEnergy {
428 energy: 0,
429 }));
430 summary.0 += 1;
431 summary.1.energy += energy_used.energy;
432 }
433 out
434 }
435}
436
437impl crate::DebugInfo for DebugTracker {
438 const ENABLE_DEBUG: bool = true;
439
440 fn empty_trace() -> Self { Self::default() }
441
442 fn trace_host_call(&mut self, f: self::ImportFunc, energy_used: InterpreterEnergy) {
443 let next_idx = self.next_index;
444 match f {
445 ImportFunc::ChargeMemoryAlloc => self.memory_alloc.add(energy_used),
446 ImportFunc::Common(c) => {
447 self.next_index += 1;
448 self.host_call_trace.push((next_idx, HostCall {
449 host_function: HostFunctionV1::Common(c),
450 energy_used,
451 }));
452 }
453 ImportFunc::InitOnly(io) => {
454 self.next_index += 1;
455 self.host_call_trace.push((next_idx, HostCall {
456 host_function: HostFunctionV1::Init(io),
457 energy_used,
458 }));
459 }
460 ImportFunc::ReceiveOnly(ro) => {
461 self.next_index += 1;
462 self.host_call_trace.push((next_idx, HostCall {
463 host_function: HostFunctionV1::Receive(ro),
464 energy_used,
465 }));
466 }
467 }
468 }
469
470 fn emit_debug_event(&mut self, event: EmittedDebugStatement) {
471 let next_idx = self.next_index;
472 self.next_index += 1;
473 self.emitted_events.push((next_idx, event));
474 }
475}
476
477#[derive(Debug)]
478#[doc(hidden)] pub struct ReceiveHost<'a, BackingStore, ParamType, Ctx, A: DebugInfo> {
487 pub energy: InterpreterEnergy,
488 pub stateless: StateLessReceiveHost<ParamType, Ctx>,
489 pub state: InstanceState<'a, BackingStore>,
490 pub trace: A,
491}
492
493#[derive(Debug)]
494#[doc(hidden)] pub struct StateLessReceiveHost<ParamType, Ctx> {
501 pub activation_frames: u32,
504 pub logs: v0::Logs,
506 pub return_value: ReturnValue,
508 pub parameters: Vec<ParamType>,
511 pub receive_ctx: Ctx,
513 pub params: ReceiveParams,
515}
516
517impl<'a, Ctx2, Ctx1: Into<Ctx2>> From<StateLessReceiveHost<ParameterRef<'a>, Ctx1>>
518 for StateLessReceiveHost<ParameterVec, Ctx2>
519{
520 fn from(host: StateLessReceiveHost<ParameterRef<'a>, Ctx1>) -> Self {
521 Self {
522 activation_frames: host.activation_frames,
523 logs: host.logs,
524 return_value: host.return_value,
525 parameters: host.parameters.into_iter().map(|x| x.to_vec()).collect(),
526 receive_ctx: host.receive_ctx.into(),
527 params: host.params,
528 }
529 }
530}
531
532#[derive(Debug)]
534pub struct EmittedDebugStatement {
535 pub filename: String,
537 pub line: u32,
539 pub column: u32,
541 pub msg: String,
543 pub remaining_energy: InterpreterEnergy,
545}
546
547impl std::fmt::Display for EmittedDebugStatement {
548 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
549 write!(
550 f,
551 "{}:{}:{}:@{}:{}",
552 self.filename, self.line, self.column, self.remaining_energy, self.msg
553 )
554 }
555}
556
557pub(crate) mod host {
558 use std::convert::TryFrom;
572
573 use super::*;
574 use concordium_contracts_common::{
575 Cursor, EntrypointName, Get, ParseError, ParseResult, ACCOUNT_ADDRESS_SIZE,
576 };
577
578 const TRANSFER_TAG: u32 = 0;
579 const CALL_TAG: u32 = 1;
580 const QUERY_ACCOUNT_BALANCE_TAG: u32 = 2;
581 const QUERY_CONTRACT_BALANCE_TAG: u32 = 3;
582 const QUERY_EXCHANGE_RATE_TAG: u32 = 4;
583 const CHECK_ACCOUNT_SIGNATURE_TAG: u32 = 5;
584 const QUERY_ACCOUNT_KEYS_TAG: u32 = 6;
585 const QUERY_CONTRACT_MODULE_REFERENCE_TAG: u32 = 7;
586 const QUERY_CONTRACT_NAME_TAG: u32 = 8;
587
588 fn parse_call_args(
593 energy: &mut InterpreterEnergy,
594 cursor: &mut Cursor<&[u8]>,
595 max_parameter_size: usize,
596 ) -> ParseResult<Result<Interrupt, OutOfEnergy>> {
597 let address = cursor.get()?;
598 let parameter_len: u16 = cursor.get()?;
599 if usize::from(parameter_len) > max_parameter_size {
600 return Err(ParseError {});
601 }
602 if energy.tick_energy(constants::copy_parameter_cost(parameter_len.into())).is_err() {
603 return Ok(Err(OutOfEnergy));
604 }
605 let start = cursor.offset;
606 let end = cursor.offset + parameter_len as usize;
607 if end > cursor.data.len() {
608 return Err(ParseError {});
609 }
610 let parameter: ParameterVec = cursor.data[start..end].to_vec();
611 cursor.offset = end;
612 let name = cursor.get()?;
613 let amount = cursor.get()?;
614 Ok(Ok(Interrupt::Call {
615 address,
616 parameter,
617 name,
618 amount,
619 }))
620 }
621
622 fn write_return_value_helper(
624 rv: &mut ReturnValue,
625 energy: &mut InterpreterEnergy,
626 offset: u32,
627 bytes: &[u8],
628 limit_return_value_size: bool,
629 ) -> ExecResult<u32> {
630 let length = bytes.len();
631 let offset = offset as usize;
632 ensure!(offset <= rv.len(), "Cannot write past the offset.");
633 let end = offset
634 .checked_add(length)
635 .ok_or_else(|| anyhow::anyhow!("Writing past the end of memory."))?;
636 let end = if limit_return_value_size {
637 std::cmp::min(end, constants::MAX_CONTRACT_STATE as usize)
638 } else {
639 end
640 };
641 if rv.len() < end {
642 energy.tick_energy(constants::additional_output_size_cost(
643 end as u64 - rv.len() as u64,
644 ))?;
645 rv.resize(end, 0u8);
646 }
647 let written = (&mut rv[offset..end]).write(bytes)?;
648 Ok(written as u32)
649 }
650
651 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
652 pub(crate) fn write_return_value(
653 memory: &mut [u8],
654 stack: &mut machine::RuntimeStack,
655 energy: &mut InterpreterEnergy,
656 rv: &mut ReturnValue,
657 limit_return_value_size: bool,
658 ) -> machine::RunResult<()> {
659 let offset = unsafe { stack.pop_u32() };
660 let length = unsafe { stack.pop_u32() };
661 let start = unsafe { stack.pop_u32() } as usize;
662 energy.tick_energy(constants::write_output_cost(length))?;
664 let end = start + length as usize; ensure!(end <= memory.len(), "Illegal memory access.");
666 let res = write_return_value_helper(
667 rv,
668 energy,
669 offset,
670 &memory[start..end],
671 limit_return_value_size,
672 )?;
673 stack.push_value(res);
674 Ok(())
675 }
676
677 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
678 pub(crate) fn invoke(
680 params: ReceiveParams,
681 memory: &mut [u8],
682 stack: &mut machine::RuntimeStack,
683 energy: &mut InterpreterEnergy,
684 ) -> machine::RunResult<Option<Interrupt>> {
685 energy.tick_energy(constants::INVOKE_BASE_COST)?;
686 let length_u32 = unsafe { stack.pop_u32() }; let length = length_u32 as usize;
688 let start = unsafe { stack.pop_u32() } as usize; let tag = unsafe { stack.pop_u32() }; match tag {
691 TRANSFER_TAG => {
692 ensure!(
693 length == ACCOUNT_ADDRESS_SIZE + 8,
694 "Transfers must have exactly 40 bytes of payload, but was {}",
695 length
696 );
697 ensure!(start + length <= memory.len(), "Illegal memory access.");
699 let mut addr_bytes = [0u8; ACCOUNT_ADDRESS_SIZE];
700 addr_bytes.copy_from_slice(&memory[start..start + ACCOUNT_ADDRESS_SIZE]);
701 let to = AccountAddress(addr_bytes);
702 let mut amount_bytes = [0; 8];
703 amount_bytes.copy_from_slice(
704 &memory[start + ACCOUNT_ADDRESS_SIZE..start + ACCOUNT_ADDRESS_SIZE + 8],
705 );
706 let amount = Amount {
707 micro_ccd: u64::from_le_bytes(amount_bytes),
708 };
709 Ok(Interrupt::Transfer {
710 to,
711 amount,
712 }
713 .into())
714 }
715 CALL_TAG => {
716 ensure!(start + length <= memory.len(), "Illegal memory access.");
717 let mut cursor = Cursor::new(&memory[start..start + length]);
718 match parse_call_args(energy, &mut cursor, params.max_parameter_size) {
719 Ok(Ok(i)) => Ok(Some(i)),
720 Ok(Err(OutOfEnergy)) => bail!(OutOfEnergy),
721 Err(e) => bail!("Illegal call, cannot parse arguments: {:?}", e),
722 }
723 }
724 QUERY_ACCOUNT_BALANCE_TAG if params.support_queries => {
725 ensure!(
726 length == ACCOUNT_ADDRESS_SIZE,
727 "Account balance queries must have exactly 32 bytes of payload, but was {}",
728 length
729 );
730 ensure!(start + length <= memory.len(), "Illegal memory access.");
732 let mut addr_bytes = [0u8; ACCOUNT_ADDRESS_SIZE];
733 addr_bytes.copy_from_slice(&memory[start..start + ACCOUNT_ADDRESS_SIZE]);
734 let address = AccountAddress(addr_bytes);
735 Ok(Interrupt::QueryAccountBalance {
736 address,
737 }
738 .into())
739 }
740 QUERY_CONTRACT_BALANCE_TAG if params.support_queries => {
741 ensure!(
742 length == 8 + 8,
743 "Contract balance queries must have exactly 16 bytes of payload, but was {}",
744 length
745 );
746 ensure!(start + length <= memory.len(), "Illegal memory access.");
748 let mut buf = [0u8; 8];
749 buf.copy_from_slice(&memory[start..start + 8]);
750 let index = u64::from_le_bytes(buf);
751 buf.copy_from_slice(&memory[start + 8..start + 16]);
752 let subindex = u64::from_le_bytes(buf);
753 let address = ContractAddress {
754 index,
755 subindex,
756 };
757 Ok(Interrupt::QueryContractBalance {
758 address,
759 }
760 .into())
761 }
762 QUERY_EXCHANGE_RATE_TAG if params.support_queries => {
763 ensure!(
764 length == 0,
765 "Exchange rate query must have no payload, but was {}",
766 length
767 );
768 Ok(Interrupt::QueryExchangeRates.into())
769 }
770 CHECK_ACCOUNT_SIGNATURE_TAG if params.support_account_signature_checks => {
771 ensure!(
772 length >= ACCOUNT_ADDRESS_SIZE,
773 "Account signature check queries must have at least the 32 bytes for an \
774 account address, but was {}",
775 length
776 );
777 ensure!(start + length <= memory.len(), "Illegal memory access.");
779 if energy.tick_energy(constants::copy_to_host_cost(length_u32)).is_err() {
780 bail!(OutOfEnergy);
781 }
782 let mut addr_bytes = [0u8; ACCOUNT_ADDRESS_SIZE];
783 addr_bytes.copy_from_slice(&memory[start..start + ACCOUNT_ADDRESS_SIZE]);
784 let address = AccountAddress(addr_bytes);
785 let payload = memory[start + ACCOUNT_ADDRESS_SIZE..start + length].to_vec();
786 Ok(Interrupt::CheckAccountSignature {
787 address,
788 payload,
789 }
790 .into())
791 }
792 QUERY_ACCOUNT_KEYS_TAG if params.support_account_signature_checks => {
793 ensure!(
794 length == ACCOUNT_ADDRESS_SIZE,
795 "Account keys queries must have exactly 32 bytes of payload, but was {}",
796 length
797 );
798 ensure!(start + length <= memory.len(), "Illegal memory access.");
800 let mut addr_bytes = [0u8; ACCOUNT_ADDRESS_SIZE];
801 addr_bytes.copy_from_slice(&memory[start..start + ACCOUNT_ADDRESS_SIZE]);
802 let address = AccountAddress(addr_bytes);
803 Ok(Interrupt::QueryAccountKeys {
804 address,
805 }
806 .into())
807 }
808 QUERY_CONTRACT_MODULE_REFERENCE_TAG if params.support_contract_inspection_queries => {
809 ensure!(
810 length == 8 + 8,
811 "Contract module reference queries must have exactly 16 bytes of payload, but \
812 was {}",
813 length
814 );
815 ensure!(start + length <= memory.len(), "Illegal memory access.");
817 let mut buf = [0u8; 8];
818 buf.copy_from_slice(&memory[start..start + 8]);
819 let index = u64::from_le_bytes(buf);
820 buf.copy_from_slice(&memory[start + 8..start + 16]);
821 let subindex = u64::from_le_bytes(buf);
822 let address = ContractAddress {
823 index,
824 subindex,
825 };
826 Ok(Interrupt::QueryContractModuleReference {
827 address,
828 }
829 .into())
830 }
831 QUERY_CONTRACT_NAME_TAG if params.support_contract_inspection_queries => {
832 ensure!(
833 length == 8 + 8,
834 "Contract name queries must have exactly 16 bytes of payload, but was {}",
835 length
836 );
837 ensure!(start + length <= memory.len(), "Illegal memory access.");
839 let mut buf = [0u8; 8];
840 buf.copy_from_slice(&memory[start..start + 8]);
841 let index = u64::from_le_bytes(buf);
842 buf.copy_from_slice(&memory[start + 8..start + 16]);
843 let subindex = u64::from_le_bytes(buf);
844 let address = ContractAddress {
845 index,
846 subindex,
847 };
848 Ok(Interrupt::QueryContractName {
849 address,
850 }
851 .into())
852 }
853 c => bail!("Illegal instruction code {}.", c),
854 }
855 }
856
857 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
858 pub(crate) fn get_parameter_size(
861 stack: &mut machine::RuntimeStack,
862 parameters: &[impl AsRef<[u8]>],
863 ) -> machine::RunResult<()> {
864 let param_num = unsafe { stack.pop_u32() } as usize;
867 if let Some(param) = parameters.get(param_num) {
868 stack.push_value(param.as_ref().len() as u32);
869 } else {
870 stack.push_value(-1i32);
871 }
872 Ok(())
873 }
874
875 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
876 pub(crate) fn get_parameter_section(
879 memory: &mut [u8],
880 stack: &mut machine::RuntimeStack,
881 energy: &mut InterpreterEnergy,
882 parameters: &[impl AsRef<[u8]>],
883 ) -> machine::RunResult<()> {
884 let offset = unsafe { stack.pop_u32() } as usize;
885 let length = unsafe { stack.pop_u32() };
886 let start = unsafe { stack.pop_u32() } as usize;
887 let param_num = unsafe { stack.pop_u32() } as usize;
888 energy.tick_energy(constants::copy_parameter_cost(length))?;
890 if let Some(param) = parameters.get(param_num) {
891 let write_end = start + length as usize; ensure!(write_end <= memory.len(), "Illegal memory access.");
893 let end = std::cmp::min(offset + length as usize, param.as_ref().len());
894 ensure!(offset <= end, "Attempting to read non-existent parameter.");
895 let amt = (&mut memory[start..write_end]).write(¶m.as_ref()[offset..end])?;
896 stack.push_value(amt as u32);
897 } else {
898 stack.push_value(-1i32);
899 }
900 Ok(())
901 }
902
903 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
904 pub(crate) fn state_lookup_entry<BackingStore: BackingStoreLoad>(
907 memory: &mut [u8],
908 stack: &mut machine::RuntimeStack,
909 energy: &mut InterpreterEnergy,
910 state: &mut InstanceState<BackingStore>,
911 ) -> machine::RunResult<()> {
912 let key_len = unsafe { stack.pop_u32() };
913 let key_start = unsafe { stack.pop_u32() } as usize;
914 let key_end = key_start + key_len as usize;
915 energy.tick_energy(constants::lookup_entry_cost(key_len))?;
916 ensure!(key_end <= memory.len(), "Illegal memory access.");
917 let key = &memory[key_start..key_end];
918 let result = state.lookup_entry(key);
919 stack.push_value(u64::from(result));
920 Ok(())
921 }
922
923 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
924 pub(crate) fn state_create_entry<BackingStore: BackingStoreLoad>(
927 memory: &mut [u8],
928 stack: &mut machine::RuntimeStack,
929 energy: &mut InterpreterEnergy,
930 state: &mut InstanceState<BackingStore>,
931 ) -> machine::RunResult<()> {
932 let key_len = unsafe { stack.pop_u32() };
933 let key_start = unsafe { stack.pop_u32() } as usize;
934 let key_end = key_start + key_len as usize;
935 energy.tick_energy(constants::create_entry_cost(key_len))?;
936 ensure!(key_end <= memory.len(), "Illegal memory access.");
937 let key = &memory[key_start..key_end];
938 let entry_index = state.create_entry(key)?;
939 stack.push_value(u64::from(entry_index));
940 Ok(())
941 }
942
943 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
944 pub(crate) fn state_delete_entry<BackingStore: BackingStoreLoad>(
947 memory: &mut [u8],
948 stack: &mut machine::RuntimeStack,
949 energy: &mut InterpreterEnergy,
950 state: &mut InstanceState<BackingStore>,
951 ) -> machine::RunResult<()> {
952 let key_len = unsafe { stack.pop_u32() };
953 let key_start = unsafe { stack.pop_u32() } as usize;
954 let key_end = key_start + key_len as usize;
955 energy.tick_energy(constants::delete_entry_cost(key_len))?;
956 ensure!(key_end <= memory.len(), "Illegal memory access.");
957 let key = &memory[key_start..key_end];
958 let result = state.delete_entry(key)?;
959 stack.push_value(result);
960 Ok(())
961 }
962
963 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
964 pub(crate) fn state_delete_prefix<BackingStore: BackingStoreLoad>(
967 memory: &mut [u8],
968 stack: &mut machine::RuntimeStack,
969 energy: &mut InterpreterEnergy,
970 state: &mut InstanceState<BackingStore>,
971 ) -> machine::RunResult<()> {
972 let key_len = unsafe { stack.pop_u32() };
973 let key_start = unsafe { stack.pop_u32() } as usize;
974 let key_end = key_start + key_len as usize;
975 ensure!(key_end <= memory.len(), "Illegal memory access.");
977 let key = &memory[key_start..key_end];
978 energy.tick_energy(constants::delete_prefix_find_cost(key_len))?;
979 let result = state.delete_prefix(energy, key)?;
980 stack.push_value(result);
981 Ok(())
982 }
983
984 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
985 pub(crate) fn state_iterator<BackingStore: BackingStoreLoad>(
988 memory: &mut [u8],
989 stack: &mut machine::RuntimeStack,
990 energy: &mut InterpreterEnergy,
991 state: &mut InstanceState<BackingStore>,
992 ) -> machine::RunResult<()> {
993 let prefix_len = unsafe { stack.pop_u32() };
994 let prefix_start = unsafe { stack.pop_u32() } as usize;
995 let prefix_end = prefix_start + prefix_len as usize;
996 ensure!(prefix_end <= memory.len(), "Illegal memory access.");
997 energy.tick_energy(constants::new_iterator_cost(prefix_len))?;
998 let prefix = &memory[prefix_start..prefix_end];
999 let iterator_index = state.iterator(prefix);
1000 stack.push_value(u64::from(iterator_index));
1001 Ok(())
1002 }
1003
1004 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1005 pub(crate) fn state_iterator_next<BackingStore: BackingStoreLoad>(
1008 stack: &mut machine::RuntimeStack,
1009 energy: &mut InterpreterEnergy,
1010 state: &mut InstanceState<BackingStore>,
1011 ) -> machine::RunResult<()> {
1012 let iter_index = unsafe { stack.pop_u64() };
1013 let entry_option = state.iterator_next(energy, InstanceStateIterator::from(iter_index))?;
1014 stack.push_value(u64::from(entry_option));
1015 Ok(())
1016 }
1017
1018 pub(crate) fn state_iterator_delete<BackingStore: BackingStoreLoad>(
1021 stack: &mut machine::RuntimeStack,
1022 energy: &mut InterpreterEnergy,
1023 state: &mut InstanceState<BackingStore>,
1024 ) -> machine::RunResult<()> {
1025 let iter = unsafe { stack.pop_u64() };
1026 let result = state.iterator_delete(energy, InstanceStateIterator::from(iter))?;
1027 stack.push_value(result);
1028 Ok(())
1029 }
1030
1031 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1034 pub(crate) fn state_iterator_key_size<BackingStore: BackingStoreLoad>(
1035 stack: &mut machine::RuntimeStack,
1036 energy: &mut InterpreterEnergy,
1037 state: &mut InstanceState<BackingStore>,
1038 ) -> machine::RunResult<()> {
1039 energy.tick_energy(constants::ITERATOR_KEY_SIZE_COST)?;
1040 let iter = unsafe { stack.pop_u64() };
1043 let result = state.iterator_key_size(InstanceStateIterator::from(iter));
1044 stack.push_value(result);
1045 Ok(())
1046 }
1047
1048 pub(crate) fn state_iterator_key_read<BackingStore: BackingStoreLoad>(
1051 memory: &mut [u8],
1052 stack: &mut machine::RuntimeStack,
1053 energy: &mut InterpreterEnergy,
1054 state: &mut InstanceState<BackingStore>,
1055 ) -> machine::RunResult<()> {
1056 let offset = unsafe { stack.pop_u32() };
1057 let length = unsafe { stack.pop_u32() };
1058 let start = unsafe { stack.pop_u32() } as usize;
1059 let iter = unsafe { stack.pop_u64() };
1060 energy.tick_energy(constants::copy_from_host_cost(length))?;
1061 let dest_end = start + length as usize;
1062 ensure!(dest_end <= memory.len(), "Illegal memory access.");
1063 let dest = &mut memory[start..dest_end];
1064 let result = state.iterator_key_read(InstanceStateIterator::from(iter), dest, offset);
1065 stack.push_value(result);
1066 Ok(())
1067 }
1068
1069 pub(crate) fn state_entry_read<BackingStore: BackingStoreLoad>(
1072 memory: &mut [u8],
1073 stack: &mut machine::RuntimeStack,
1074 energy: &mut InterpreterEnergy,
1075 state: &mut InstanceState<BackingStore>,
1076 ) -> machine::RunResult<()> {
1077 let offset = unsafe { stack.pop_u32() };
1078 let length = unsafe { stack.pop_u32() };
1079 let dest_start = unsafe { stack.pop_u32() } as usize;
1080 let entry_index = unsafe { stack.pop_u64() };
1081 energy.tick_energy(constants::read_entry_cost(length))?;
1082 let dest_end = dest_start + length as usize;
1083 ensure!(dest_end <= memory.len(), "Illegal memory access.");
1084 let dest = &mut memory[dest_start..dest_end];
1085 let result = state.entry_read(InstanceStateEntry::from(entry_index), dest, offset);
1086 stack.push_value(result);
1087 Ok(())
1088 }
1089
1090 pub(crate) fn state_entry_write<BackingStore: BackingStoreLoad>(
1093 memory: &mut [u8],
1094 stack: &mut machine::RuntimeStack,
1095 energy: &mut InterpreterEnergy,
1096 state: &mut InstanceState<BackingStore>,
1097 ) -> machine::RunResult<()> {
1098 let offset = unsafe { stack.pop_u32() };
1099 let length = unsafe { stack.pop_u32() };
1100 let source_start = unsafe { stack.pop_u32() } as usize;
1101 let entry_index = unsafe { stack.pop_u64() };
1102 energy.tick_energy(constants::write_entry_cost(length))?;
1103 let source_end = source_start + length as usize;
1104 ensure!(source_end <= memory.len(), "Illegal memory access.");
1105 let source = &memory[source_start..source_end];
1106 let result =
1107 state.entry_write(energy, InstanceStateEntry::from(entry_index), source, offset)?;
1108 stack.push_value(result);
1109 Ok(())
1110 }
1111
1112 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1113 pub(crate) fn state_entry_size<BackingStore: BackingStoreLoad>(
1116 stack: &mut machine::RuntimeStack,
1117 energy: &mut InterpreterEnergy,
1118 state: &mut InstanceState<BackingStore>,
1119 ) -> machine::RunResult<()> {
1120 let entry_index = unsafe { stack.pop_u64() };
1121 energy.tick_energy(constants::ENTRY_SIZE_COST)?;
1122 let result = state.entry_size(InstanceStateEntry::from(entry_index));
1123 stack.push_value(result);
1124 Ok(())
1125 }
1126
1127 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1128 pub(crate) fn state_entry_resize<BackingStore: BackingStoreLoad>(
1131 stack: &mut machine::RuntimeStack,
1132 energy: &mut InterpreterEnergy,
1133 state: &mut InstanceState<BackingStore>,
1134 ) -> machine::RunResult<()> {
1135 energy.tick_energy(constants::RESIZE_ENTRY_BASE_COST)?;
1136 let new_size = unsafe { stack.pop_u32() };
1137 let entry_index = unsafe { stack.pop_u64() };
1138 let result = state.entry_resize(energy, InstanceStateEntry::from(entry_index), new_size)?;
1139 stack.push_value(result);
1140 Ok(())
1141 }
1142
1143 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1144 pub(crate) fn get_receive_entrypoint_size(
1146 stack: &mut machine::RuntimeStack,
1147 entrypoint: EntrypointName,
1148 ) -> machine::RunResult<()> {
1149 let size: u32 = entrypoint.size();
1150 stack.push_value(size);
1151 Ok(())
1152 }
1153
1154 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1155 pub(crate) fn get_receive_entrypoint(
1157 memory: &mut [u8],
1158 stack: &mut machine::RuntimeStack,
1159 entrypoint: EntrypointName,
1160 ) -> machine::RunResult<()> {
1161 let start = unsafe { stack.pop_u32() };
1162 let size = entrypoint.size();
1163 let end: usize = start as usize + size as usize;
1165 ensure!(end <= memory.len(), "Illegal memory access.");
1166 let entrypoint_str: &str = entrypoint.into();
1167 memory[start as usize..end].copy_from_slice(entrypoint_str.as_bytes());
1168 Ok(())
1169 }
1170
1171 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1172 pub(crate) fn verify_ed25519_signature(
1173 memory: &mut [u8],
1174 stack: &mut machine::RuntimeStack,
1175 energy: &mut InterpreterEnergy,
1176 ) -> machine::RunResult<()> {
1177 let message_len = unsafe { stack.pop_u32() };
1178 let message_start = unsafe { stack.pop_u32() };
1179 let signature_start = unsafe { stack.pop_u32() };
1180 let public_key_start = unsafe { stack.pop_u32() };
1181 let message_end = message_start as usize + message_len as usize;
1182 ensure!(message_end <= memory.len(), "Illegal memory access.");
1183 let public_key_end = public_key_start as usize + 32;
1184 ensure!(public_key_end <= memory.len(), "Illegal memory access.");
1185 let signature_end = signature_start as usize + 64;
1186 ensure!(signature_end <= memory.len(), "Illegal memory access.");
1187 energy.tick_energy(constants::verify_ed25519_cost(message_len))?;
1189 let signature =
1190 ed25519_zebra::Signature::try_from(&memory[signature_start as usize..signature_end]);
1191 let message = &memory[message_start as usize..message_end];
1192 let public_key = ed25519_zebra::VerificationKey::try_from(
1193 &memory[public_key_start as usize..public_key_end],
1194 );
1195 match (signature, public_key) {
1196 (Ok(ref signature), Ok(public_key)) => {
1197 if public_key.verify(signature, message).is_ok() {
1198 stack.push_value(1u32);
1199 } else {
1200 stack.push_value(0u32);
1201 }
1202 }
1203 _ => stack.push_value(0u32),
1204 }
1205 Ok(())
1206 }
1207
1208 pub(crate) fn debug_print<Debug: DebugInfo>(
1209 debug: &mut Debug,
1210 memory: &mut [u8],
1211 stack: &mut machine::RuntimeStack,
1212 energy: &mut InterpreterEnergy,
1213 ) -> machine::RunResult<()> {
1214 let (filename, line, column, msg) = crate::utils::extract_debug(memory, stack)?;
1215 debug.emit_debug_event(EmittedDebugStatement {
1216 filename,
1217 line,
1218 column,
1219 msg,
1220 remaining_energy: *energy,
1221 });
1222 Ok(())
1223 }
1224
1225 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1226 pub(crate) fn verify_ecdsa_secp256k1_signature(
1227 memory: &mut [u8],
1228 stack: &mut machine::RuntimeStack,
1229 energy: &mut InterpreterEnergy,
1230 ) -> machine::RunResult<()> {
1231 let message_start = unsafe { stack.pop_u32() };
1232 let signature_start = unsafe { stack.pop_u32() };
1233 let public_key_start = unsafe { stack.pop_u32() };
1234 let message_end = message_start as usize + 32;
1235 ensure!(message_end <= memory.len(), "Illegal memory access.");
1236 let public_key_end = public_key_start as usize + 33;
1237 ensure!(public_key_end <= memory.len(), "Illegal memory access.");
1238 let signature_end = signature_start as usize + 64;
1239 ensure!(signature_end <= memory.len(), "Illegal memory access.");
1240 energy.tick_energy(constants::VERIFY_ECDSA_SECP256K1_COST)?;
1242 let signature = secp256k1::ecdsa::Signature::from_compact(
1243 &memory[signature_start as usize..signature_end],
1244 );
1245 let message = secp256k1::Message::from_slice(&memory[message_start as usize..message_end]);
1246 let public_key =
1247 secp256k1::PublicKey::from_slice(&memory[public_key_start as usize..public_key_end]);
1248 match (signature, message, public_key) {
1249 (Ok(signature), Ok(message), Ok(public_key)) => {
1250 let verifier = secp256k1::Secp256k1::verification_only();
1251 if verifier.verify_ecdsa(&message, &signature, &public_key).is_ok() {
1252 stack.push_value(1u32);
1253 } else {
1254 stack.push_value(0u32);
1255 }
1256 }
1257 _ => stack.push_value(0u32),
1258 }
1259 Ok(())
1260 }
1261
1262 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1263 pub(crate) fn hash_sha2_256(
1264 memory: &mut [u8],
1265 stack: &mut machine::RuntimeStack,
1266 energy: &mut InterpreterEnergy,
1267 ) -> machine::RunResult<()> {
1268 let output_start = unsafe { stack.pop_u32() };
1269 let data_len = unsafe { stack.pop_u32() };
1270 let data_start = unsafe { stack.pop_u32() };
1271 let data_end = data_start as usize + data_len as usize;
1272 ensure!(data_end <= memory.len(), "Illegal memory access.");
1273 let output_end = output_start as usize + 32;
1274 ensure!(output_end <= memory.len(), "Illegal memory access.");
1275 energy.tick_energy(constants::hash_sha2_256_cost(data_len))?;
1277 let hash = sha2::Sha256::digest(&memory[data_start as usize..data_end]);
1278 memory[output_start as usize..output_end].copy_from_slice(&hash);
1279 Ok(())
1280 }
1281
1282 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1283 pub(crate) fn hash_sha3_256(
1284 memory: &mut [u8],
1285 stack: &mut machine::RuntimeStack,
1286 energy: &mut InterpreterEnergy,
1287 ) -> machine::RunResult<()> {
1288 let output_start = unsafe { stack.pop_u32() };
1289 let data_len = unsafe { stack.pop_u32() };
1290 let data_start = unsafe { stack.pop_u32() };
1291 let data_end = data_start as usize + data_len as usize;
1292 ensure!(data_end <= memory.len(), "Illegal memory access.");
1293 let output_end = output_start as usize + 32;
1294 ensure!(output_end <= memory.len(), "Illegal memory access.");
1295 energy.tick_energy(constants::hash_sha3_256_cost(data_len))?;
1297 let hash = sha3::Sha3_256::digest(&memory[data_start as usize..data_end]);
1298 memory[output_start as usize..output_end].copy_from_slice(&hash);
1299 Ok(())
1300 }
1301
1302 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1303 pub(crate) fn hash_keccak_256(
1304 memory: &mut [u8],
1305 stack: &mut machine::RuntimeStack,
1306 energy: &mut InterpreterEnergy,
1307 ) -> machine::RunResult<()> {
1308 let output_start = unsafe { stack.pop_u32() };
1309 let data_len = unsafe { stack.pop_u32() };
1310 let data_start = unsafe { stack.pop_u32() };
1311 let data_end = data_start as usize + data_len as usize;
1312 ensure!(data_end <= memory.len(), "Illegal memory access.");
1313 let output_end = output_start as usize + 32;
1314 ensure!(output_end <= memory.len(), "Illegal memory access.");
1315 energy.tick_energy(constants::hash_keccak_256_cost(data_len))?;
1317 let hash = sha3::Keccak256::digest(&memory[data_start as usize..data_end]);
1318 memory[output_start as usize..output_end].copy_from_slice(&hash);
1319 Ok(())
1320 }
1321
1322 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1323 pub(crate) fn upgrade(
1325 memory: &mut [u8],
1326 stack: &mut machine::RuntimeStack,
1327 energy: &mut InterpreterEnergy,
1328 ) -> machine::RunResult<Option<Interrupt>> {
1329 let module_ref_start = unsafe { stack.pop_u32() } as usize;
1330 let module_ref_end = module_ref_start + 32;
1331 ensure!(module_ref_end <= memory.len(), "Illegal memory access.");
1332 let mut module_reference_bytes = [0u8; 32];
1333 module_reference_bytes.copy_from_slice(&memory[module_ref_start..module_ref_end]);
1334 let module_ref = ModuleReference::from(module_reference_bytes);
1335 energy.tick_energy(constants::INVOKE_BASE_COST)?;
1339 Ok(Some(Interrupt::Upgrade {
1340 module_ref,
1341 }))
1342 }
1343}
1344
1345impl<
1348 'a,
1349 BackingStore: BackingStoreLoad,
1350 ParamType: AsRef<[u8]>,
1351 Ctx: v0::HasInitContext,
1352 A: DebugInfo,
1353 > machine::Host<ProcessedImports> for InitHost<'a, BackingStore, ParamType, Ctx, A>
1354{
1355 type Interrupt = NoInterrupt;
1356
1357 #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
1358 fn tick_initial_memory(&mut self, num_pages: u32) -> machine::RunResult<()> {
1359 self.energy.charge_memory_alloc(num_pages)
1360 }
1361
1362 #[inline(always)]
1363 fn tick_energy(&mut self, energy: u64) -> machine::RunResult<()> {
1364 self.energy.tick_energy(energy)
1365 }
1366
1367 #[inline(always)]
1368 fn track_call(&mut self) -> machine::RunResult<()> {
1369 v0::host::track_call(&mut self.activation_frames)
1370 }
1371
1372 #[inline(always)]
1373 fn track_return(&mut self) { v0::host::track_return(&mut self.activation_frames) }
1374
1375 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1376 fn call(
1377 &mut self,
1378 f: &ProcessedImports,
1379 memory: &mut [u8],
1380 stack: &mut machine::RuntimeStack,
1381 ) -> machine::RunResult<Option<Self::Interrupt>> {
1382 let energy_before = self.energy;
1383 match f.tag {
1384 ImportFunc::ChargeMemoryAlloc => {
1385 v0::host::charge_memory_alloc(stack, &mut self.energy)?
1386 }
1387 ImportFunc::Common(cf) => match cf {
1388 CommonFunc::WriteOutput => host::write_return_value(
1389 memory,
1390 stack,
1391 &mut self.energy,
1392 &mut self.return_value,
1393 self.limit_logs_and_return_values,
1394 ),
1395 CommonFunc::GetParameterSize => host::get_parameter_size(stack, &[&self.parameter]),
1396 CommonFunc::GetParameterSection => {
1397 host::get_parameter_section(memory, stack, &mut self.energy, &[&self.parameter])
1398 }
1399 CommonFunc::GetPolicySection => v0::host::get_policy_section(
1400 memory,
1401 stack,
1402 &mut self.energy,
1403 self.init_ctx.sender_policies(),
1404 ),
1405 CommonFunc::LogEvent => v0::host::log_event(
1406 memory,
1407 stack,
1408 &mut self.energy,
1409 &mut self.logs,
1410 self.limit_logs_and_return_values,
1411 ),
1412 CommonFunc::GetSlotTime => v0::host::get_slot_time(stack, self.init_ctx.metadata()),
1413 CommonFunc::StateLookupEntry => {
1414 host::state_lookup_entry(memory, stack, &mut self.energy, &mut self.state)
1415 }
1416 CommonFunc::StateCreateEntry => {
1417 host::state_create_entry(memory, stack, &mut self.energy, &mut self.state)
1418 }
1419 CommonFunc::StateDeleteEntry => {
1420 host::state_delete_entry(memory, stack, &mut self.energy, &mut self.state)
1421 }
1422 CommonFunc::StateDeletePrefix => {
1423 host::state_delete_prefix(memory, stack, &mut self.energy, &mut self.state)
1424 }
1425 CommonFunc::StateIteratePrefix => {
1426 host::state_iterator(memory, stack, &mut self.energy, &mut self.state)
1427 }
1428 CommonFunc::StateIteratorNext => {
1429 host::state_iterator_next(stack, &mut self.energy, &mut self.state)
1430 }
1431 CommonFunc::StateIteratorDelete => {
1432 host::state_iterator_delete(stack, &mut self.energy, &mut self.state)
1433 }
1434 CommonFunc::StateIteratorKeySize => {
1435 host::state_iterator_key_size(stack, &mut self.energy, &mut self.state)
1436 }
1437 CommonFunc::StateIteratorKeyRead => {
1438 host::state_iterator_key_read(memory, stack, &mut self.energy, &mut self.state)
1439 }
1440 CommonFunc::StateEntryRead => {
1441 host::state_entry_read(memory, stack, &mut self.energy, &mut self.state)
1442 }
1443 CommonFunc::StateEntryWrite => {
1444 host::state_entry_write(memory, stack, &mut self.energy, &mut self.state)
1445 }
1446 CommonFunc::StateEntrySize => {
1447 host::state_entry_size(stack, &mut self.energy, &mut self.state)
1448 }
1449 CommonFunc::StateEntryResize => {
1450 host::state_entry_resize(stack, &mut self.energy, &mut self.state)
1451 }
1452 CommonFunc::VerifyEd25519 => {
1453 host::verify_ed25519_signature(memory, stack, &mut self.energy)
1454 }
1455 CommonFunc::VerifySecp256k1 => {
1456 host::verify_ecdsa_secp256k1_signature(memory, stack, &mut self.energy)
1457 }
1458 CommonFunc::DebugPrint => {
1459 host::debug_print(&mut self.trace, memory, stack, &mut self.energy)
1460 }
1461 CommonFunc::HashSHA2_256 => host::hash_sha2_256(memory, stack, &mut self.energy),
1462 CommonFunc::HashSHA3_256 => host::hash_sha3_256(memory, stack, &mut self.energy),
1463 CommonFunc::HashKeccak256 => host::hash_keccak_256(memory, stack, &mut self.energy),
1464 }?,
1465 ImportFunc::InitOnly(InitOnlyFunc::GetInitOrigin) => {
1466 v0::host::get_init_origin(memory, stack, self.init_ctx.init_origin())?
1467 }
1468 ImportFunc::ReceiveOnly(_) => {
1469 bail!("Not implemented for init {:#?}.", f);
1470 }
1471 }
1472 let energy_after: InterpreterEnergy = self.energy;
1473 self.trace.trace_host_call(f.tag, energy_before.saturating_sub(&energy_after));
1474 Ok(None)
1475 }
1476}
1477
1478pub trait HasReceiveContext: v0::HasReceiveContext {
1480 fn entrypoint(&self) -> ExecResult<EntrypointName>;
1484}
1485
1486impl<X: AsRef<[u8]>> v0::HasReceiveContext for ReceiveContext<X> {
1487 type MetadataType = ChainMetadata;
1488
1489 fn metadata(&self) -> &Self::MetadataType { &self.common.metadata }
1490
1491 fn invoker(&self) -> ExecResult<&AccountAddress> { Ok(&self.common.invoker) }
1492
1493 fn self_address(&self) -> ExecResult<&ContractAddress> { Ok(&self.common.self_address) }
1494
1495 fn self_balance(&self) -> ExecResult<Amount> { Ok(self.common.self_balance) }
1496
1497 fn sender(&self) -> ExecResult<&Address> { Ok(&self.common.sender) }
1498
1499 fn owner(&self) -> ExecResult<&AccountAddress> { Ok(&self.common.owner) }
1500
1501 fn sender_policies(&self) -> ExecResult<&[u8]> { Ok(self.common.sender_policies.as_ref()) }
1502}
1503
1504impl<X: AsRef<[u8]>> HasReceiveContext for ReceiveContext<X> {
1505 #[inline(always)]
1506 fn entrypoint(&self) -> ExecResult<EntrypointName> { Ok(self.entrypoint.as_entrypoint_name()) }
1507}
1508
1509impl<'a, X: HasReceiveContext> HasReceiveContext for &'a X {
1510 #[inline(always)]
1511 fn entrypoint(&self) -> ExecResult<EntrypointName> { (*self).entrypoint() }
1512}
1513
1514impl<
1515 'a,
1516 BackingStore: BackingStoreLoad,
1517 ParamType: AsRef<[u8]>,
1518 Ctx: HasReceiveContext,
1519 A: DebugInfo,
1520 > machine::Host<ProcessedImports> for ReceiveHost<'a, BackingStore, ParamType, Ctx, A>
1521{
1522 type Interrupt = Interrupt;
1523
1524 #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
1525 fn tick_initial_memory(&mut self, num_pages: u32) -> machine::RunResult<()> {
1526 self.energy.charge_memory_alloc(num_pages)
1527 }
1528
1529 #[inline(always)]
1530 fn tick_energy(&mut self, energy: u64) -> machine::RunResult<()> {
1531 self.energy.tick_energy(energy)
1532 }
1533
1534 #[inline(always)]
1535 fn track_call(&mut self) -> machine::RunResult<()> {
1536 v0::host::track_call(&mut self.stateless.activation_frames)
1537 }
1538
1539 #[inline(always)]
1540 fn track_return(&mut self) { v0::host::track_return(&mut self.stateless.activation_frames) }
1541
1542 #[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1543 fn call(
1544 &mut self,
1545 f: &ProcessedImports,
1546 memory: &mut [u8],
1547 stack: &mut machine::RuntimeStack,
1548 ) -> machine::RunResult<Option<Self::Interrupt>> {
1549 let energy_before = self.energy;
1550 match f.tag {
1551 ImportFunc::ChargeMemoryAlloc => {
1552 v0::host::charge_memory_alloc(stack, &mut self.energy)?
1553 }
1554 ImportFunc::Common(cf) => match cf {
1555 CommonFunc::WriteOutput => host::write_return_value(
1556 memory,
1557 stack,
1558 &mut self.energy,
1559 &mut self.stateless.return_value,
1560 self.stateless.params.limit_logs_and_return_values,
1561 ),
1562 CommonFunc::GetParameterSize => {
1563 host::get_parameter_size(stack, &self.stateless.parameters)
1564 }
1565 CommonFunc::GetParameterSection => host::get_parameter_section(
1566 memory,
1567 stack,
1568 &mut self.energy,
1569 &self.stateless.parameters,
1570 ),
1571 CommonFunc::GetPolicySection => v0::host::get_policy_section(
1572 memory,
1573 stack,
1574 &mut self.energy,
1575 self.stateless.receive_ctx.sender_policies(),
1576 ),
1577 CommonFunc::LogEvent => v0::host::log_event(
1578 memory,
1579 stack,
1580 &mut self.energy,
1581 &mut self.stateless.logs,
1582 self.stateless.params.limit_logs_and_return_values,
1583 ),
1584 CommonFunc::GetSlotTime => {
1585 v0::host::get_slot_time(stack, self.stateless.receive_ctx.metadata())
1586 }
1587 CommonFunc::StateLookupEntry => {
1588 host::state_lookup_entry(memory, stack, &mut self.energy, &mut self.state)
1589 }
1590 CommonFunc::StateCreateEntry => {
1591 host::state_create_entry(memory, stack, &mut self.energy, &mut self.state)
1592 }
1593 CommonFunc::StateDeleteEntry => {
1594 host::state_delete_entry(memory, stack, &mut self.energy, &mut self.state)
1595 }
1596 CommonFunc::StateDeletePrefix => {
1597 host::state_delete_prefix(memory, stack, &mut self.energy, &mut self.state)
1598 }
1599 CommonFunc::StateIteratePrefix => {
1600 host::state_iterator(memory, stack, &mut self.energy, &mut self.state)
1601 }
1602 CommonFunc::StateIteratorNext => {
1603 host::state_iterator_next(stack, &mut self.energy, &mut self.state)
1604 }
1605 CommonFunc::StateIteratorDelete => {
1606 host::state_iterator_delete(stack, &mut self.energy, &mut self.state)
1607 }
1608 CommonFunc::StateIteratorKeySize => {
1609 host::state_iterator_key_size(stack, &mut self.energy, &mut self.state)
1610 }
1611 CommonFunc::StateIteratorKeyRead => {
1612 host::state_iterator_key_read(memory, stack, &mut self.energy, &mut self.state)
1613 }
1614 CommonFunc::StateEntryRead => {
1615 host::state_entry_read(memory, stack, &mut self.energy, &mut self.state)
1616 }
1617 CommonFunc::StateEntryWrite => {
1618 host::state_entry_write(memory, stack, &mut self.energy, &mut self.state)
1619 }
1620 CommonFunc::StateEntrySize => {
1621 host::state_entry_size(stack, &mut self.energy, &mut self.state)
1622 }
1623 CommonFunc::StateEntryResize => {
1624 host::state_entry_resize(stack, &mut self.energy, &mut self.state)
1625 }
1626 CommonFunc::VerifyEd25519 => {
1627 host::verify_ed25519_signature(memory, stack, &mut self.energy)
1628 }
1629 CommonFunc::VerifySecp256k1 => {
1630 host::verify_ecdsa_secp256k1_signature(memory, stack, &mut self.energy)
1631 }
1632 CommonFunc::DebugPrint => {
1633 host::debug_print(&mut self.trace, memory, stack, &mut self.energy)
1634 }
1635 CommonFunc::HashSHA2_256 => host::hash_sha2_256(memory, stack, &mut self.energy),
1636 CommonFunc::HashSHA3_256 => host::hash_sha3_256(memory, stack, &mut self.energy),
1637 CommonFunc::HashKeccak256 => host::hash_keccak_256(memory, stack, &mut self.energy),
1638 }?,
1639 ImportFunc::ReceiveOnly(rof) => match rof {
1640 ReceiveOnlyFunc::Invoke => {
1641 let invoke =
1642 host::invoke(self.stateless.params, memory, stack, &mut self.energy);
1643 let energy_after: InterpreterEnergy = self.energy;
1644 self.trace.trace_host_call(f.tag, energy_before.saturating_sub(&energy_after));
1645 return invoke;
1646 }
1647 ReceiveOnlyFunc::GetReceiveInvoker => v0::host::get_receive_invoker(
1648 memory,
1649 stack,
1650 self.stateless.receive_ctx.invoker(),
1651 ),
1652 ReceiveOnlyFunc::GetReceiveSelfAddress => v0::host::get_receive_self_address(
1653 memory,
1654 stack,
1655 self.stateless.receive_ctx.self_address(),
1656 ),
1657 ReceiveOnlyFunc::GetReceiveSelfBalance => v0::host::get_receive_self_balance(
1658 stack,
1659 self.stateless.receive_ctx.self_balance(),
1660 ),
1661 ReceiveOnlyFunc::GetReceiveSender => {
1662 v0::host::get_receive_sender(memory, stack, self.stateless.receive_ctx.sender())
1663 }
1664 ReceiveOnlyFunc::GetReceiveOwner => {
1665 v0::host::get_receive_owner(memory, stack, self.stateless.receive_ctx.owner())
1666 }
1667 ReceiveOnlyFunc::GetReceiveEntrypointSize => host::get_receive_entrypoint_size(
1668 stack,
1669 self.stateless.receive_ctx.entrypoint()?,
1670 ),
1671 ReceiveOnlyFunc::GetReceiveEntryPoint => host::get_receive_entrypoint(
1672 memory,
1673 stack,
1674 self.stateless.receive_ctx.entrypoint()?,
1675 ),
1676 ReceiveOnlyFunc::Upgrade => {
1677 return host::upgrade(memory, stack, &mut self.energy);
1678 }
1679 }?,
1680 ImportFunc::InitOnly(InitOnlyFunc::GetInitOrigin) => {
1681 bail!("Not implemented for receive.");
1682 }
1683 }
1684 let energy_after: InterpreterEnergy = self.energy;
1685 self.trace.trace_host_call(f.tag, energy_before.saturating_sub(&energy_after));
1686 Ok(None)
1687 }
1688}
1689
1690pub type ParameterRef<'a> = &'a [u8];
1692pub type ParameterVec = Vec<u8>;
1698
1699#[derive(Debug)]
1701pub struct InitInvocation<'a> {
1702 pub amount: Amount,
1704 pub init_name: &'a str,
1706 pub parameter: ParameterRef<'a>,
1708 pub energy: InterpreterEnergy,
1710}
1711
1712pub fn invoke_init<BackingStore: BackingStoreLoad, R: RunnableCode, A: DebugInfo>(
1714 artifact: impl Borrow<Artifact<ProcessedImports, R>>,
1715 init_ctx: impl v0::HasInitContext,
1716 init_invocation: InitInvocation,
1717 limit_logs_and_return_values: bool,
1718 mut loader: BackingStore,
1719) -> InvokeResult<InitResult<A>, A> {
1720 let mut initial_state = trie::MutableState::initial_state();
1721 let inner = initial_state.get_inner(&mut loader);
1722 let state_ref = InstanceState::new(loader, inner);
1723 let mut host = InitHost::<_, _, _, A> {
1724 energy: init_invocation.energy,
1725 activation_frames: constants::MAX_ACTIVATION_FRAMES,
1726 logs: v0::Logs::new(),
1727 state: state_ref,
1728 return_value: Vec::new(),
1729 parameter: init_invocation.parameter,
1730 limit_logs_and_return_values,
1731 init_ctx,
1732 trace: A::empty_trace(),
1733 };
1734 let result = artifact.borrow().run(&mut host, init_invocation.init_name, &[Value::I64(
1735 init_invocation.amount.micro_ccd() as i64,
1736 )]);
1737 let return_value = std::mem::take(&mut host.return_value);
1738 let remaining_energy = host.energy.energy;
1739 let logs = std::mem::take(&mut host.logs);
1740 let trace = std::mem::replace(&mut host.trace, A::empty_trace());
1741 drop(host);
1743 match result {
1744 Ok(ExecutionOutcome::Success {
1745 result,
1746 ..
1747 }) => {
1748 if let Some(Value::I32(n)) = result {
1753 if n == 0 {
1754 Ok(InitResult::Success {
1755 logs,
1756 return_value,
1757 remaining_energy: remaining_energy.into(),
1758 state: initial_state,
1759 trace,
1760 })
1761 } else {
1762 let (reason, trace) = reason_from_wasm_error_code(n, trace)?;
1763 Ok(InitResult::Reject {
1764 reason,
1765 return_value,
1766 remaining_energy: remaining_energy.into(),
1767 trace,
1768 })
1769 }
1770 } else {
1771 Err(InvalidReturnCodeError {
1772 value: None,
1773 debug_trace: trace,
1774 })
1775 }
1776 }
1777 Ok(ExecutionOutcome::Interrupted {
1778 reason,
1779 config: _,
1780 }) => match reason {},
1781 Err(error) => {
1782 if error.downcast_ref::<OutOfEnergy>().is_some() {
1783 Ok(InitResult::OutOfEnergy {
1784 trace,
1785 })
1786 } else {
1787 Ok(InitResult::Trap {
1788 error,
1789 remaining_energy: remaining_energy.into(),
1790 trace,
1791 })
1792 }
1793 }
1794 }
1795}
1796
1797#[derive(Debug, Clone, PartialEq, Eq)]
1798pub enum InvokeFailure {
1801 ContractReject {
1804 code: i32,
1805 data: ParameterVec,
1806 },
1807 InsufficientAmount,
1809 NonExistentAccount,
1811 NonExistentContract,
1814 NonExistentEntrypoint,
1816 SendingV0Failed,
1818 RuntimeError,
1820 UpgradeInvalidModuleRef,
1822 UpgradeInvalidContractName,
1825 UpgradeInvalidVersion,
1827 SignatureDataMalformed,
1829 SignatureCheckFailed,
1831}
1832
1833impl InvokeFailure {
1834 pub(crate) fn encode_as_u64<Debug>(
1838 self,
1839 parameters: &mut Vec<ParameterVec>,
1840 ) -> ResumeResult<u64, Debug> {
1841 Ok(match self {
1842 InvokeFailure::ContractReject {
1843 code,
1844 data,
1845 } => {
1846 let len = parameters.len();
1847 if len > 0b0111_1111_1111_1111_1111_1111 {
1848 return Err(ResumeError::TooManyInterrupts);
1849 }
1850 parameters.push(data);
1851 (len as u64) << 40 | (code as u32 as u64)
1852 }
1853 InvokeFailure::InsufficientAmount => 0x01_0000_0000,
1854 InvokeFailure::NonExistentAccount => 0x02_0000_0000,
1855 InvokeFailure::NonExistentContract => 0x03_0000_0000,
1856 InvokeFailure::NonExistentEntrypoint => 0x04_0000_0000,
1857 InvokeFailure::SendingV0Failed => 0x05_0000_0000,
1858 InvokeFailure::RuntimeError => 0x06_0000_0000,
1859 InvokeFailure::UpgradeInvalidModuleRef => 0x07_0000_0000,
1860 InvokeFailure::UpgradeInvalidContractName => 0x08_0000_0000,
1861 InvokeFailure::UpgradeInvalidVersion => 0x09_0000_0000,
1862 InvokeFailure::SignatureDataMalformed => 0x0a_0000_0000,
1863 InvokeFailure::SignatureCheckFailed => 0x0b_0000_0000,
1864 })
1865 }
1866}
1867
1868#[derive(Debug)]
1870pub enum InvokeResponse {
1871 Success {
1873 new_balance: Amount,
1875 data: Option<ParameterVec>,
1877 },
1878 Failure {
1881 kind: InvokeFailure,
1882 },
1883}
1884
1885#[cfg(feature = "enable-ffi")]
1886impl InvokeResponse {
1887 pub(crate) fn try_from_ffi_response(
1889 response_status: u64,
1890 new_balance: Amount,
1891 data: Option<ParameterVec>,
1892 ) -> anyhow::Result<Self> {
1893 let response = if response_status & 0xffff_ff00_0000_0000 == 0xffff_ff00_0000_0000 {
1895 let kind = match response_status & 0x0000_00ff_0000_0000 {
1896 0x0000_0000_0000_0000 => {
1897 if response_status & 0x0000_0000_ffff_ffff == 0 {
1899 bail!("Host violated precondition.")
1901 }
1902 let code = (response_status & 0x0000_0000_ffff_ffff) as u32 as i32;
1903 if let Some(data) = data {
1904 InvokeFailure::ContractReject {
1905 code,
1906 data,
1907 }
1908 } else {
1909 bail!("Return value should be present in case of logic error.")
1910 }
1911 }
1912 0x0000_0001_0000_0000 => InvokeFailure::InsufficientAmount,
1913 0x0000_0002_0000_0000 => InvokeFailure::NonExistentAccount,
1914 0x0000_0003_0000_0000 => InvokeFailure::NonExistentContract,
1915 0x0000_0004_0000_0000 => InvokeFailure::NonExistentEntrypoint,
1916 0x0000_0005_0000_0000 => InvokeFailure::SendingV0Failed,
1917 0x0000_0006_0000_0000 => InvokeFailure::RuntimeError,
1918 0x0000_0007_0000_0000 => InvokeFailure::UpgradeInvalidModuleRef,
1919 0x0000_0008_0000_0000 => InvokeFailure::UpgradeInvalidContractName,
1920 0x0000_0009_0000_0000 => InvokeFailure::UpgradeInvalidVersion,
1921 0x0000_000a_0000_0000 => InvokeFailure::SignatureDataMalformed,
1922 0x0000_000b_0000_0000 => InvokeFailure::SignatureCheckFailed,
1923 x => bail!("Unrecognized error code: {}", x),
1924 };
1925 InvokeResponse::Failure {
1926 kind,
1927 }
1928 } else {
1929 InvokeResponse::Success {
1930 new_balance,
1931 data,
1932 }
1933 };
1934 Ok(response)
1935 }
1936}
1937
1938#[derive(Copy, Clone, Debug)]
1939pub struct InvokeFromArtifactCtx<'a> {
1941 pub artifact: &'a [u8],
1944 pub amount: Amount,
1946 pub parameter: ParameterRef<'a>,
1948 pub energy: InterpreterEnergy,
1950}
1951
1952#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1954pub fn invoke_init_from_artifact<BackingStore: BackingStoreLoad, A: DebugInfo>(
1955 ctx: InvokeFromArtifactCtx,
1956 init_ctx: impl v0::HasInitContext,
1957 init_name: &str,
1958 loader: BackingStore,
1959 limit_logs_and_return_values: bool,
1960) -> ExecResult<InitResult<A>> {
1961 let artifact = utils::parse_artifact(ctx.artifact)?;
1962 let r = invoke_init(
1963 artifact,
1964 init_ctx,
1965 InitInvocation {
1966 amount: ctx.amount,
1967 init_name,
1968 parameter: ctx.parameter,
1969 energy: ctx.energy,
1970 },
1971 limit_logs_and_return_values,
1972 loader,
1973 )?;
1974 Ok(r)
1975}
1976
1977#[derive(Copy, Clone, Debug)]
1978pub struct InvokeFromSourceCtx<'a> {
1980 pub source: &'a [u8],
1982 pub amount: Amount,
1984 pub parameter: ParameterRef<'a>,
1986 pub energy: InterpreterEnergy,
1988 pub support_upgrade: bool,
1993}
1994
1995#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
1997pub fn invoke_init_from_source<BackingStore: BackingStoreLoad, A: DebugInfo>(
1998 ctx: InvokeFromSourceCtx,
1999 init_ctx: impl v0::HasInitContext,
2000 init_name: &str,
2001 loader: BackingStore,
2002 validation_config: ValidationConfig,
2003 limit_logs_and_return_values: bool,
2004) -> ExecResult<InitResult<A>> {
2005 let artifact = utils::instantiate(
2006 validation_config,
2007 &ConcordiumAllowedImports {
2008 support_upgrade: ctx.support_upgrade,
2009 enable_debug: A::ENABLE_DEBUG,
2010 },
2011 ctx.source,
2012 )?
2013 .artifact;
2014 let r = invoke_init(
2015 artifact,
2016 init_ctx,
2017 InitInvocation {
2018 amount: ctx.amount,
2019 init_name,
2020 parameter: ctx.parameter,
2021 energy: ctx.energy,
2022 },
2023 limit_logs_and_return_values,
2024 loader,
2025 )?;
2026 Ok(r)
2027}
2028
2029#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
2032pub fn invoke_init_with_metering_from_source<BackingStore: BackingStoreLoad, A: DebugInfo>(
2033 ctx: InvokeFromSourceCtx,
2034 init_ctx: impl v0::HasInitContext,
2035 init_name: &str,
2036 loader: BackingStore,
2037 validation_config: ValidationConfig,
2038 cost_config: impl CostConfiguration,
2039 limit_logs_and_return_values: bool,
2040) -> ExecResult<InitResult<A>> {
2041 let artifact = utils::instantiate_with_metering(
2042 validation_config,
2043 cost_config,
2044 &ConcordiumAllowedImports {
2045 support_upgrade: ctx.support_upgrade,
2046 enable_debug: A::ENABLE_DEBUG,
2047 },
2048 ctx.source,
2049 )?
2050 .artifact;
2051 let r = invoke_init(
2052 artifact,
2053 init_ctx,
2054 InitInvocation {
2055 amount: ctx.amount,
2056 init_name,
2057 parameter: ctx.parameter,
2058 energy: ctx.energy,
2059 },
2060 limit_logs_and_return_values,
2061 loader,
2062 )?;
2063 Ok(r)
2064}
2065
2066fn process_receive_result<
2067 BackingStore,
2068 Param,
2069 R: RunnableCode,
2070 Art: Into<Arc<Artifact<ProcessedImports, R>>>,
2071 Ctx1,
2072 Ctx2,
2073 A: DebugInfo,
2074>(
2075 artifact: Art,
2076 host: ReceiveHost<'_, BackingStore, Param, Ctx1, A>,
2077 result: machine::RunResult<ExecutionOutcome<Interrupt>>,
2078) -> InvokeResult<ReceiveResult<R, A, Ctx2>, A>
2079where
2080 StateLessReceiveHost<ParameterVec, Ctx2>: From<StateLessReceiveHost<Param, Ctx1>>, {
2081 let mut stateless = host.stateless;
2082 match result {
2083 Ok(ExecutionOutcome::Success {
2084 result,
2085 ..
2086 }) => {
2087 let remaining_energy = host.energy;
2088 if let Some(Value::I32(n)) = result {
2089 if n >= 0 {
2090 Ok(ReceiveResult::Success {
2091 logs: stateless.logs,
2092 state_changed: host.state.changed,
2093 return_value: stateless.return_value,
2094 remaining_energy,
2095 trace: host.trace,
2096 })
2097 } else {
2098 let (reason, trace) = reason_from_wasm_error_code(n, host.trace)?;
2099 Ok(ReceiveResult::Reject {
2100 reason,
2101 return_value: stateless.return_value,
2102 remaining_energy,
2103 trace,
2104 })
2105 }
2106 } else {
2107 Err(InvalidReturnCodeError {
2108 value: None,
2109 debug_trace: host.trace,
2110 })
2111 }
2112 }
2113 Ok(ExecutionOutcome::Interrupted {
2114 reason,
2115 config,
2116 }) => {
2117 let remaining_energy = host.energy;
2118 let logs = if reason.should_clear_logs() {
2122 std::mem::take(&mut stateless.logs)
2123 } else {
2124 v0::Logs::new()
2125 };
2126 let state_changed = host.state.changed;
2127 let trace = host.trace;
2128 let host = SavedHost {
2129 stateless: stateless.into(),
2130 current_generation: host.state.current_generation,
2131 entry_mapping: host.state.entry_mapping,
2132 iterators: host.state.iterators,
2133 };
2134 Ok(ReceiveResult::Interrupt {
2135 remaining_energy,
2136 state_changed,
2137 logs,
2138 config: Box::new(ReceiveInterruptedState {
2139 host,
2140 artifact: artifact.into(),
2141 config,
2142 }),
2143 interrupt: reason,
2144 trace,
2145 })
2146 }
2147 Err(error) => {
2148 if error.downcast_ref::<OutOfEnergy>().is_some() {
2149 Ok(ReceiveResult::OutOfEnergy {
2150 trace: host.trace,
2151 })
2152 } else {
2153 Ok(ReceiveResult::Trap {
2154 error,
2155 remaining_energy: host.energy,
2156 trace: host.trace,
2157 })
2158 }
2159 }
2160 }
2161}
2162
2163#[derive(Debug, Clone, Copy)]
2166pub struct ReceiveParams {
2167 pub max_parameter_size: usize,
2169 pub limit_logs_and_return_values: bool,
2172 pub support_queries: bool,
2175 pub support_account_signature_checks: bool,
2178 pub support_contract_inspection_queries: bool,
2182}
2183
2184impl ReceiveParams {
2185 pub fn new_p4() -> Self {
2187 Self {
2188 max_parameter_size: 1024,
2189 limit_logs_and_return_values: true,
2190 support_queries: false,
2191 support_account_signature_checks: false,
2192 support_contract_inspection_queries: false,
2193 }
2194 }
2195
2196 pub fn new_p5() -> Self {
2198 Self {
2199 max_parameter_size: u16::MAX.into(),
2200 limit_logs_and_return_values: false,
2201 support_queries: true,
2202 support_account_signature_checks: false,
2203 support_contract_inspection_queries: false,
2204 }
2205 }
2206
2207 pub fn new_p6() -> Self {
2209 Self {
2210 max_parameter_size: u16::MAX.into(),
2211 limit_logs_and_return_values: false,
2212 support_queries: true,
2213 support_account_signature_checks: true,
2214 support_contract_inspection_queries: false,
2215 }
2216 }
2217
2218 pub fn new_p7() -> Self {
2220 Self {
2221 max_parameter_size: u16::MAX.into(),
2222 limit_logs_and_return_values: false,
2223 support_queries: true,
2224 support_account_signature_checks: true,
2225 support_contract_inspection_queries: true,
2226 }
2227 }
2228}
2229
2230#[derive(Debug)]
2232pub struct ReceiveInvocation<'a> {
2233 pub amount: Amount,
2235 pub receive_name: ReceiveName<'a>,
2237 pub parameter: ParameterRef<'a>,
2239 pub energy: InterpreterEnergy,
2241}
2242
2243pub fn invoke_receive<
2245 BackingStore: BackingStoreLoad,
2246 R1: RunnableCode,
2247 R2: RunnableCode,
2248 Art: Borrow<Artifact<ProcessedImports, R1>> + Into<Arc<Artifact<ProcessedImports, R2>>>,
2249 Ctx1: HasReceiveContext,
2250 Ctx2: From<Ctx1>,
2251 A: DebugInfo,
2252>(
2253 artifact: Art,
2254 receive_ctx: Ctx1,
2255 receive_invocation: ReceiveInvocation,
2256 instance_state: InstanceState<BackingStore>,
2257 params: ReceiveParams,
2258) -> InvokeResult<ReceiveResult<R2, A, Ctx2>, A> {
2259 let mut host = ReceiveHost {
2260 energy: receive_invocation.energy,
2261 stateless: StateLessReceiveHost {
2262 activation_frames: constants::MAX_ACTIVATION_FRAMES,
2263 logs: v0::Logs::new(),
2264 return_value: Vec::new(),
2265 parameters: vec![receive_invocation.parameter],
2266 receive_ctx,
2267 params,
2268 },
2269 state: instance_state,
2270 trace: A::empty_trace(),
2271 };
2272
2273 let result =
2274 artifact.borrow().run(&mut host, receive_invocation.receive_name.get_chain_name(), &[
2275 Value::I64(receive_invocation.amount.micro_ccd() as i64),
2276 ]);
2277 process_receive_result(artifact, host, result)
2278}
2279
2280pub type ResumeResult<A, Debug> = Result<A, ResumeError<Debug>>;
2281
2282#[derive(Debug, thiserror::Error)]
2283pub enum ResumeError<Debug> {
2284 #[error("Too many interrupts in a contract call.")]
2286 TooManyInterrupts,
2287 #[error("Invalid return value from a contract call: {error:?}")]
2288 InvalidReturn {
2289 #[from]
2290 error: InvalidReturnCodeError<Debug>,
2291 },
2292}
2293
2294impl<R, Debug: DebugInfo, Ctx> From<ResumeError<Debug>> for types::ReceiveResult<R, Debug, Ctx> {
2295 fn from(value: ResumeError<Debug>) -> Self {
2296 match value {
2297 ResumeError::TooManyInterrupts => {
2298 Self::Trap {
2299 error: anyhow::anyhow!("Too many interrupts in a contract call."),
2300 remaining_energy: 0.into(), trace: Debug::empty_trace(), }
2304 }
2305 ResumeError::InvalidReturn {
2306 error,
2307 } => error.into(),
2308 }
2309 }
2310}
2311
2312pub fn resume_receive<BackingStore: BackingStoreLoad, A: DebugInfo>(
2330 interrupted_state: Box<ReceiveInterruptedState<CompiledFunction>>,
2331 response: InvokeResponse, energy: InterpreterEnergy, state_trie: &mut trie::MutableState,
2334 state_updated: bool,
2335 mut backing_store: BackingStore,
2336) -> ResumeResult<ReceiveResult<CompiledFunction, A>, A> {
2337 let inner = state_trie.get_inner(&mut backing_store);
2338 let state = InstanceState::migrate(
2339 state_updated,
2340 interrupted_state.host.current_generation,
2341 interrupted_state.host.entry_mapping,
2342 interrupted_state.host.iterators,
2343 backing_store,
2344 inner,
2345 );
2346 let mut host = ReceiveHost {
2347 stateless: interrupted_state.host.stateless,
2348 energy,
2349 state,
2350 trace: A::empty_trace(),
2351 };
2352 let response = match response {
2353 InvokeResponse::Success {
2354 new_balance,
2355 data,
2356 } => {
2357 host.stateless.receive_ctx.common.self_balance = new_balance;
2358 let tag = if state_updated {
2362 0b1000_0000_0000_0000_0000_0000u64
2363 } else {
2364 0
2365 };
2366 if let Some(data) = data {
2367 let len = host.stateless.parameters.len();
2368 if len > 0b0111_1111_1111_1111_1111_1111 {
2369 return Err(ResumeError::TooManyInterrupts);
2370 }
2371 host.stateless.parameters.push(data);
2372 (len as u64 | tag) << 40
2374 } else {
2375 tag << 40
2380 }
2381 }
2382 InvokeResponse::Failure {
2383 kind,
2384 } => kind.encode_as_u64(&mut host.stateless.parameters)?,
2385 };
2386 let mut config = interrupted_state.config;
2388 config.push_value(response);
2389 let result = interrupted_state.artifact.run_config(&mut host, config);
2390 let r = process_receive_result(interrupted_state.artifact, host, result)?;
2391 Ok(r)
2392}
2393
2394fn reason_from_wasm_error_code<A>(
2397 n: i32,
2398 debug_trace: A,
2399) -> Result<(i32, A), InvalidReturnCodeError<A>> {
2400 if n < 0 {
2401 Ok((n, debug_trace))
2402 } else {
2403 Err(InvalidReturnCodeError {
2404 value: Some(n),
2405 debug_trace,
2406 })
2407 }
2408}
2409
2410#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
2412pub fn invoke_receive_from_artifact<
2413 'a,
2414 BackingStore: BackingStoreLoad,
2415 Ctx1: HasReceiveContext,
2416 Ctx2: From<Ctx1>,
2417 A: DebugInfo,
2418>(
2419 ctx: InvokeFromArtifactCtx<'a>,
2420 receive_ctx: Ctx1,
2421 receive_name: ReceiveName,
2422 instance_state: InstanceState<BackingStore>,
2423 params: ReceiveParams,
2424) -> ExecResult<ReceiveResult<CompiledFunctionBytes<'a>, A, Ctx2>> {
2425 let artifact = utils::parse_artifact(ctx.artifact)?;
2426 let r = invoke_receive(
2427 Arc::new(artifact),
2428 receive_ctx,
2429 ReceiveInvocation {
2430 energy: ctx.energy,
2431 parameter: ctx.parameter,
2432 receive_name,
2433 amount: ctx.amount,
2434 },
2435 instance_state,
2436 params,
2437 )?;
2438 Ok(r)
2439}
2440
2441#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
2443pub fn invoke_receive_from_source<
2444 BackingStore: BackingStoreLoad,
2445 Ctx1: HasReceiveContext,
2446 Ctx2: From<Ctx1>,
2447 A: DebugInfo,
2448>(
2449 validation_config: ValidationConfig,
2450 ctx: InvokeFromSourceCtx,
2451 receive_ctx: Ctx1,
2452 receive_name: ReceiveName,
2453 instance_state: InstanceState<BackingStore>,
2454 params: ReceiveParams,
2455) -> ExecResult<ReceiveResult<CompiledFunction, A, Ctx2>> {
2456 let artifact = utils::instantiate(
2457 validation_config,
2458 &ConcordiumAllowedImports {
2459 support_upgrade: ctx.support_upgrade,
2460 enable_debug: A::ENABLE_DEBUG,
2461 },
2462 ctx.source,
2463 )?
2464 .artifact;
2465 let r = invoke_receive(
2466 Arc::new(artifact),
2467 receive_ctx,
2468 ReceiveInvocation {
2469 amount: ctx.amount,
2470 receive_name,
2471 parameter: ctx.parameter,
2472 energy: ctx.energy,
2473 },
2474 instance_state,
2475 params,
2476 )?;
2477 Ok(r)
2478}
2479
2480#[cfg_attr(not(feature = "fuzz-coverage"), inline)]
2483pub fn invoke_receive_with_metering_from_source<
2484 BackingStore: BackingStoreLoad,
2485 Ctx1: HasReceiveContext,
2486 Ctx2: From<Ctx1>,
2487 A: DebugInfo,
2488>(
2489 validation_config: ValidationConfig,
2490 cost_config: impl CostConfiguration,
2491 ctx: InvokeFromSourceCtx,
2492 receive_ctx: Ctx1,
2493 receive_name: ReceiveName,
2494 instance_state: InstanceState<BackingStore>,
2495 params: ReceiveParams,
2496) -> ExecResult<ReceiveResult<CompiledFunction, A, Ctx2>> {
2497 let artifact = utils::instantiate_with_metering(
2498 validation_config,
2499 cost_config,
2500 &ConcordiumAllowedImports {
2501 support_upgrade: ctx.support_upgrade,
2502 enable_debug: A::ENABLE_DEBUG,
2503 },
2504 ctx.source,
2505 )?
2506 .artifact;
2507 let r = invoke_receive(
2508 Arc::new(artifact),
2509 receive_ctx,
2510 ReceiveInvocation {
2511 amount: ctx.amount,
2512 receive_name,
2513 parameter: ctx.parameter,
2514 energy: ctx.energy,
2515 },
2516 instance_state,
2517 params,
2518 )?;
2519 Ok(r)
2520}