1use std::convert::{TryFrom, TryInto};
4use std::path::PathBuf;
5
6use anyhow::{Context as _, anyhow};
7use cid::Cid;
8use fvm_ipld_blockstore::Blockstore;
9use fvm_ipld_encoding::{CBOR, IPLD_RAW};
10use fvm_shared::crypto::signature;
11use fvm_shared::error::ErrorNumber;
12use fvm_shared::event::{ActorEvent, Entry, Flags};
13use fvm_shared::sys::out::vm::ContextFlags;
14use fvm_shared::upgrade::UpgradeInfo;
15use multihash_codetable::MultihashDigest;
16
17use super::blocks::{Block, BlockRegistry};
18use super::error::Result;
19use super::hash::SupportedHashes;
20use super::*;
21use crate::call_manager::{
22 CallManager, Entrypoint, INVOKE_FUNC_NAME, InvocationResult, NO_DATA_BLOCK_ID,
23 UPGRADE_FUNC_NAME,
24};
25use crate::externs::{Chain, Rand};
26use crate::gas::GasTimer;
27use crate::init_actor::INIT_ACTOR_ID;
28use crate::machine::{BURNT_FUNDS_ACTOR_ID, MachineContext, NetworkConfig};
29use crate::state_tree::ActorState;
30use crate::trace::IpldOperation;
31use crate::{ipld, syscall_error};
32
33const BLAKE2B_256: u64 = 0xb220;
34const ENV_ARTIFACT_DIR: &str = "FVM_STORE_ARTIFACT_DIR";
35const MAX_ARTIFACT_NAME_LEN: usize = 256;
36
37#[cfg(feature = "testing")]
38const TEST_ACTOR_ALLOWED_TO_CALL_CREATE_ACTOR: ActorID = 98;
39
40pub struct DefaultKernel<C> {
42 pub caller: ActorID,
45 pub actor_id: ActorID,
46 pub method: MethodNum,
47 pub value_received: TokenAmount,
48 pub read_only: bool,
49
50 pub call_manager: C,
53 pub blocks: BlockRegistry,
58}
59
60impl<C> Kernel for DefaultKernel<C>
63where
64 C: CallManager,
65{
66 type CallManager = C;
67 type Limiter = <<C as CallManager>::Machine as Machine>::Limiter;
68
69 fn into_inner(self) -> (Self::CallManager, BlockRegistry)
70 where
71 Self: Sized,
72 {
73 (self.call_manager, self.blocks)
74 }
75
76 fn new(
77 mgr: C,
78 blocks: BlockRegistry,
79 caller: ActorID,
80 actor_id: ActorID,
81 method: MethodNum,
82 value_received: TokenAmount,
83 read_only: bool,
84 ) -> Self {
85 DefaultKernel {
86 call_manager: mgr,
87 blocks,
88 caller,
89 actor_id,
90 method,
91 value_received,
92 read_only,
93 }
94 }
95
96 fn machine(&self) -> &<Self::CallManager as CallManager>::Machine {
97 self.call_manager.machine()
98 }
99
100 fn limiter_mut(&mut self) -> &mut Self::Limiter {
101 self.call_manager.limiter_mut()
102 }
103
104 fn gas_available(&self) -> Gas {
105 self.call_manager.gas_tracker().gas_available()
106 }
107
108 fn charge_gas(&self, name: &str, compute: Gas) -> Result<GasTimer> {
109 self.call_manager.gas_tracker().charge_gas(name, compute)
110 }
111}
112
113impl<C> DefaultKernel<C>
114where
115 C: CallManager,
116{
117 fn get_self(&self) -> Result<Option<ActorState>> {
119 self.call_manager.get_actor(self.actor_id)
120 }
121}
122
123impl<K> SendOps<K> for DefaultKernel<K::CallManager>
124where
125 K: Kernel,
126{
127 fn send(
128 &mut self,
129 recipient: &Address,
130 method: MethodNum,
131 params_id: BlockId,
132 value: &TokenAmount,
133 gas_limit: Option<Gas>,
134 flags: SendFlags,
135 ) -> Result<CallResult> {
136 let from = self.actor_id;
137 let read_only = self.read_only || flags.read_only();
138
139 if read_only && !value.is_zero() {
140 return Err(syscall_error!(ReadOnly; "cannot transfer value when read-only").into());
141 }
142
143 let params = if params_id == NO_DATA_BLOCK_ID {
145 None
146 } else {
147 Some(self.blocks.get(params_id)?.clone())
148 };
149
150 if self.blocks.is_full() {
152 return Err(syscall_error!(LimitExceeded; "cannot store return block").into());
153 }
154
155 let result = self.call_manager.with_transaction(|cm| {
157 cm.call_actor::<K>(
158 from,
159 *recipient,
160 Entrypoint::Invoke(method),
161 params,
162 value,
163 gas_limit,
164 read_only,
165 )
166 })?;
167
168 Ok(match result {
170 InvocationResult {
171 exit_code,
172 value: Some(blk),
173 } => {
174 let block_stat = blk.stat();
175 let block_id = self
180 .blocks
181 .put_reachable(blk)
182 .or_fatal()
183 .context("failed to store a valid return value")?;
184 CallResult {
185 block_id,
186 block_stat,
187 exit_code,
188 }
189 }
190 InvocationResult {
191 exit_code,
192 value: None,
193 } => CallResult {
194 block_id: NO_DATA_BLOCK_ID,
195 block_stat: BlockStat { codec: 0, size: 0 },
196 exit_code,
197 },
198 })
199 }
200}
201
202impl<K> UpgradeOps<K> for DefaultKernel<K::CallManager>
203where
204 K: Kernel,
205{
206 fn upgrade_actor(&mut self, new_code_cid: Cid, params_id: BlockId) -> Result<CallResult> {
207 if self.read_only {
208 return Err(
209 syscall_error!(ReadOnly, "upgrade_actor cannot be called while read-only").into(),
210 );
211 }
212
213 let mut iter = self.call_manager.get_call_stack().iter();
219 let position = iter.position(|&tuple| tuple == (self.actor_id, INVOKE_FUNC_NAME));
220 if position.is_some() {
221 for tuple in iter {
222 if tuple.0 != self.actor_id || tuple.1 != UPGRADE_FUNC_NAME {
223 return Err(syscall_error!(
224 Forbidden,
225 "calling upgrade on actor already on call stack is forbidden"
226 )
227 .into());
228 }
229 }
230 }
231
232 let params = if params_id == NO_DATA_BLOCK_ID {
234 None
235 } else {
236 Some(self.blocks.get(params_id)?.clone())
237 };
238
239 if self.blocks.is_full() {
241 return Err(syscall_error!(LimitExceeded; "cannot store return block").into());
242 }
243
244 let result = self.call_manager.with_transaction(|cm| {
245 let state = cm
246 .get_actor(self.actor_id)?
247 .ok_or_else(|| syscall_error!(IllegalOperation; "actor deleted"))?;
248
249 let code = state.code;
253
254 cm.set_actor(
256 self.actor_id,
257 ActorState::new(
258 new_code_cid,
259 state.state,
260 state.balance,
261 state.sequence,
262 None,
263 ),
264 )?;
265
266 let result = cm.call_actor::<K>(
268 self.caller,
269 Address::new_id(self.actor_id),
270 Entrypoint::Upgrade(UpgradeInfo { old_code_cid: code }),
271 params,
272 &TokenAmount::from_whole(0),
273 None,
274 false,
275 )?;
276
277 Ok(result)
278 });
279
280 match result {
281 Ok(InvocationResult { exit_code, value }) => {
282 let (block_stat, block_id) = match value {
283 None => (BlockStat { codec: 0, size: 0 }, NO_DATA_BLOCK_ID),
284 Some(block) => (block.stat(), self.blocks.put_reachable(block)?),
285 };
286 Ok(CallResult {
287 block_id,
288 block_stat,
289 exit_code,
290 })
291 }
292 Err(err) => Err(err),
293 }
294 }
295}
296
297impl<C> SelfOps for DefaultKernel<C>
298where
299 C: CallManager,
300{
301 fn root(&mut self) -> Result<Cid> {
302 let t = self
303 .call_manager
304 .charge_gas(self.call_manager.price_list().on_get_root())?;
305
306 let cid = self
308 .get_self()?
309 .context("state root requested after actor deletion")
310 .or_error(ErrorNumber::IllegalOperation)?
311 .state;
312
313 self.blocks.mark_reachable(&cid);
314
315 t.stop();
316
317 Ok(cid)
318 }
319
320 fn set_root(&mut self, new: Cid) -> Result<()> {
321 if self.read_only {
322 return Err(
323 syscall_error!(ReadOnly; "cannot update the state-root while read-only").into(),
324 );
325 }
326
327 let _ = self
328 .call_manager
329 .charge_gas(self.call_manager.price_list().on_set_root())?;
330
331 if !self.blocks.is_reachable(&new) {
332 return Err(syscall_error!(NotFound; "new root cid not reachable: {new}").into());
333 }
334
335 let mut state = self
336 .call_manager
337 .get_actor(self.actor_id)?
338 .ok_or_else(|| syscall_error!(IllegalOperation; "actor deleted"))?;
339 state.state = new;
340 self.call_manager.set_actor(self.actor_id, state)?;
341 Ok(())
342 }
343
344 fn current_balance(&self) -> Result<TokenAmount> {
345 let t = self
346 .call_manager
347 .charge_gas(self.call_manager.price_list().on_self_balance())?;
348
349 t.record(Ok(self.get_self()?.map(|a| a.balance).unwrap_or_default()))
351 }
352
353 fn self_destruct(&mut self, burn_unspent: bool) -> Result<()> {
354 if self.read_only {
355 return Err(syscall_error!(ReadOnly; "cannot self-destruct when read-only").into());
356 }
357
358 let t = self
361 .call_manager
362 .charge_gas(self.call_manager.price_list().on_delete_actor())?;
363
364 let balance = self.current_balance()?;
372 if !balance.is_zero() {
373 if !burn_unspent {
374 return Err(
375 syscall_error!(IllegalOperation; "self-destruct with unspent funds").into(),
376 );
377 }
378 self.call_manager
379 .transfer(self.actor_id, BURNT_FUNDS_ACTOR_ID, &balance)
380 .or_fatal()?;
381 }
382
383 t.record(self.call_manager.delete_actor(self.actor_id))
385 }
386}
387
388impl<C> IpldBlockOps for DefaultKernel<C>
389where
390 C: CallManager,
391{
392 fn block_open(&mut self, cid: &Cid) -> Result<(BlockId, BlockStat)> {
393 let t = self
394 .call_manager
395 .charge_gas(self.call_manager.price_list().on_block_open_base())?;
396
397 if !self.blocks.is_reachable(cid) {
398 return Err(syscall_error!(NotFound; "block not reachable: {cid}").into());
399 }
400
401 let data = self
402 .call_manager
403 .blockstore()
404 .get(cid)
405 .and_then(|b| b.ok_or_else(|| anyhow!("missing reachable state: {}", cid)))
407 .or_fatal()?;
410
411 t.stop();
412
413 self.call_manager
414 .trace_ipld(IpldOperation::Get, *cid, data.len());
415
416 let children = ipld::scan_for_reachable_links(
418 cid.codec(),
419 &data,
420 self.call_manager.price_list(),
421 self.call_manager.gas_tracker(),
422 )?;
423
424 let t = self.call_manager.charge_gas(
425 self.call_manager
426 .price_list()
427 .on_block_open(data.len(), children.len()),
428 )?;
429
430 let block = Block::new(cid.codec(), data, children);
431 let stat = block.stat();
432 let id = self.blocks.put_reachable(block)?;
433 t.stop();
434 Ok((id, stat))
435 }
436
437 fn block_create(&mut self, codec: u64, data: &[u8]) -> Result<BlockId> {
438 if data.len() > self.machine().context().max_block_size {
439 return Err(syscall_error!(LimitExceeded; "blocks may not be larger than 1MiB").into());
440 }
441
442 if !ipld::ALLOWED_CODECS.contains(&codec) {
443 return Err(syscall_error!(IllegalCodec; "codec {} not allowed", codec).into());
444 }
445
446 let children = ipld::scan_for_reachable_links(
447 codec,
448 data,
449 self.call_manager.price_list(),
450 self.call_manager.gas_tracker(),
451 )?;
452
453 let t = self.call_manager.charge_gas(
454 self.call_manager
455 .price_list()
456 .on_block_create(data.len(), children.len()),
457 )?;
458
459 let blk = Block::new(codec, data, children);
460
461 t.record(Ok(self.blocks.put_check_reachable(blk)?))
462 }
463
464 fn block_link(&mut self, id: BlockId, hash_fun: u64, hash_len: u32) -> Result<Cid> {
465 if hash_fun != BLAKE2B_256 || hash_len != 32 {
466 return Err(syscall_error!(IllegalCid; "cids must be 32-byte blake2b").into());
467 }
468 let start = GasTimer::start();
469 let block = self.blocks.get(id)?;
470 let code = SupportedHashes::try_from(hash_fun)
471 .map_err(|_| syscall_error!(IllegalCid; "invalid CID codec"))?;
472
473 let t = self.call_manager.charge_gas(
474 self.call_manager
475 .price_list()
476 .on_block_link(code, block.size() as usize),
477 )?;
478
479 let hash = code.digest(block.data());
480 if u32::from(hash.size()) < hash_len {
481 return Err(syscall_error!(IllegalCid; "invalid hash length: {}", hash_len).into());
482 }
483 let k = Cid::new_v1(block.codec(), hash.truncate(hash_len as u8));
484 self.call_manager
485 .blockstore()
486 .put_keyed(&k, block.data())
487 .or_fatal()?;
490 let size = block.data().len();
491 self.blocks.mark_reachable(&k);
492
493 t.stop_with(start);
494
495 self.call_manager.trace_ipld(IpldOperation::Put, k, size);
496
497 Ok(k)
498 }
499
500 fn block_read(&self, id: BlockId, offset: u32, buf: &mut [u8]) -> Result<i32> {
501 let tstart = GasTimer::start();
502 let end = i32::try_from((offset as u64) + (buf.len() as u64))
508 .map_err(|_| syscall_error!(IllegalArgument; "offset plus buffer length did not fit into an i32"))?;
509
510 let block = self.blocks.get(id)?;
512 let data = block.data();
513
514 let start = offset as usize;
516
517 let to_read = std::cmp::min(data.len().saturating_sub(start), buf.len());
519
520 let t = self
522 .call_manager
523 .charge_gas(self.call_manager.price_list().on_block_read(to_read))?;
524
525 if to_read != 0 {
528 buf[..to_read].copy_from_slice(&data[start..(start + to_read)]);
529 }
530 t.stop_with(tstart);
531 Ok((data.len() as i32) - end)
533 }
534
535 fn block_stat(&self, id: BlockId) -> Result<BlockStat> {
536 let t = self
537 .call_manager
538 .charge_gas(self.call_manager.price_list().on_block_stat())?;
539
540 t.record(Ok(self.blocks.stat(id)?))
541 }
542}
543
544impl<C> MessageOps for DefaultKernel<C>
545where
546 C: CallManager,
547{
548 fn msg_context(&self) -> Result<MessageContext> {
549 let t = self
550 .call_manager
551 .charge_gas(self.call_manager.price_list().on_message_context())?;
552
553 let ctx = MessageContext {
554 caller: self.caller,
555 origin: self.call_manager.origin(),
556 receiver: self.actor_id,
557 method_number: self.method,
558 value_received: (&self.value_received)
559 .try_into()
560 .or_fatal()
561 .context("invalid token amount")?,
562 gas_premium: self
563 .call_manager
564 .gas_premium()
565 .try_into()
566 .or_fatal()
567 .context("invalid gas premium")?,
568 flags: if self.read_only {
569 ContextFlags::READ_ONLY
570 } else {
571 ContextFlags::empty()
572 },
573 nonce: self.call_manager.nonce(),
574 };
575 t.stop();
576 Ok(ctx)
577 }
578}
579
580impl<C> CryptoOps for DefaultKernel<C>
581where
582 C: CallManager,
583{
584 #[cfg(feature = "verify-signature")]
585 fn verify_signature(
586 &self,
587 sig_type: SignatureType,
588 signature: &[u8],
589 signer: &Address,
590 plaintext: &[u8],
591 ) -> Result<bool> {
592 use fvm_shared::address::Payload;
593 use std::panic::{self, UnwindSafe};
594
595 fn catch_and_log_panic<F: FnOnce() -> Result<R> + UnwindSafe, R>(
596 context: &str,
597 f: F,
598 ) -> Result<R> {
599 match panic::catch_unwind(f) {
600 Ok(v) => v,
601 Err(e) => {
602 log::error!("caught panic when {}: {:?}", context, e);
603 Err(
604 syscall_error!(IllegalArgument; "caught panic when {}: {:?}", context, e)
605 .into(),
606 )
607 }
608 }
609 }
610
611 let t = self.call_manager.charge_gas(
612 self.call_manager
613 .price_list()
614 .on_verify_signature(sig_type, plaintext.len()),
615 )?;
616
617 let signing_addr = match signer.payload() {
620 Payload::BLS(_) | Payload::Secp256k1(_) => *signer,
621 _ => {
623 return Err(syscall_error!(IllegalArgument; "address protocol {} not supported", signer.protocol()).into());
624 }
625 };
626
627 t.record(catch_and_log_panic("verifying signature", || {
630 Ok(signature::verify(sig_type, signature, plaintext, &signing_addr).is_ok())
631 }))
632 }
633
634 fn verify_bls_aggregate(
635 &self,
636 aggregate_sig: &[u8; signature::BLS_SIG_LEN],
637 pub_keys: &[[u8; signature::BLS_PUB_LEN]],
638 plaintexts_concat: &[u8],
639 plaintext_lens: &[u32],
640 ) -> Result<bool> {
641 let num_signers = pub_keys.len();
642
643 if num_signers != plaintext_lens.len() {
644 return Err(syscall_error!(
645 IllegalArgument;
646 "unequal numbers of bls public keys and plaintexts"
647 )
648 .into());
649 }
650
651 let t = self.call_manager.charge_gas(
652 self.call_manager
653 .price_list()
654 .on_verify_aggregate_signature(num_signers, plaintexts_concat.len()),
655 )?;
656
657 let mut offset: usize = 0;
658 let plaintexts = plaintext_lens
659 .iter()
660 .map(|&len| {
661 let start = offset;
662 offset = start
663 .checked_add(len as usize)
664 .context("invalid bls plaintext length")
665 .or_illegal_argument()?;
666 plaintexts_concat
667 .get(start..offset)
668 .context("bls signature plaintext out of bounds")
669 .or_illegal_argument()
670 })
671 .collect::<Result<Vec<_>>>()?;
672 if offset != plaintexts_concat.len() {
673 return Err(
674 syscall_error!(IllegalArgument; "plaintexts buffer length doesn't match").into(),
675 );
676 }
677
678 t.record(
679 signature::ops::verify_bls_aggregate(aggregate_sig, pub_keys, &plaintexts)
680 .or(Ok(false)),
681 )
682 }
683
684 fn recover_secp_public_key(
685 &self,
686 hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE],
687 signature: &[u8; SECP_SIG_LEN],
688 ) -> Result<[u8; SECP_PUB_LEN]> {
689 let t = self
690 .call_manager
691 .charge_gas(self.call_manager.price_list().on_recover_secp_public_key())?;
692
693 t.record(
694 signature::ops::recover_secp_public_key(hash, signature)
695 .or_illegal_argument()
696 .context("public key recovery failed"),
697 )
698 }
699
700 fn hash(&self, code: u64, data: &[u8]) -> Result<Multihash> {
701 let hasher = SupportedHashes::try_from(code)
702 .map_err(|err| syscall_error!(IllegalArgument; "unsupported hash code {}", err.0))?;
703
704 let t = self.call_manager.charge_gas(
705 self.call_manager
706 .price_list()
707 .on_hashing(hasher, data.len()),
708 )?;
709
710 t.record(Ok(hasher.digest(data)))
711 }
712}
713
714impl<C> NetworkOps for DefaultKernel<C>
715where
716 C: CallManager,
717{
718 fn network_context(&self) -> Result<NetworkContext> {
719 let t = self
720 .call_manager
721 .charge_gas(self.call_manager.price_list().on_network_context())?;
722
723 let MachineContext {
724 epoch,
725 timestamp,
726 base_fee,
727 network:
728 NetworkConfig {
729 network_version,
730 chain_id,
731 ..
732 },
733 ..
734 } = self.call_manager.context();
735
736 let ctx = NetworkContext {
737 chain_id: (*chain_id).into(),
738 epoch: *epoch,
739 network_version: *network_version,
740 timestamp: *timestamp,
741 base_fee: base_fee
742 .try_into()
743 .or_fatal()
744 .context("base-fee exceeds u128 limit")?,
745 };
746
747 t.stop();
748 Ok(ctx)
749 }
750
751 fn tipset_cid(&self, epoch: ChainEpoch) -> Result<Cid> {
752 use std::cmp::Ordering::*;
753
754 if epoch < 0 {
755 return Err(syscall_error!(IllegalArgument; "epoch is negative").into());
756 }
757 let offset = self.call_manager.context().epoch - epoch;
758
759 match offset.cmp(&0) {
761 Less => return Err(syscall_error!(IllegalArgument; "epoch {} is in the future", epoch).into()),
762 Equal => return Err(syscall_error!(IllegalArgument; "cannot lookup the tipset cid for the current epoch").into()),
763 Greater => {}
764 }
765
766 self.call_manager
767 .charge_gas(self.call_manager.price_list().on_tipset_cid(offset))?;
768
769 self.call_manager.externs().get_tipset_cid(epoch).or_fatal()
770 }
771}
772
773impl<C> RandomnessOps for DefaultKernel<C>
774where
775 C: CallManager,
776{
777 fn get_randomness_from_tickets(
778 &self,
779 rand_epoch: ChainEpoch,
780 ) -> Result<[u8; RANDOMNESS_LENGTH]> {
781 let lookback = self
782 .call_manager
783 .context()
784 .epoch
785 .checked_sub(rand_epoch)
786 .ok_or_else(|| syscall_error!(IllegalArgument; "randomness epoch {} is in the future", rand_epoch)
787 )?;
788
789 let t = self
790 .call_manager
791 .charge_gas(self.call_manager.price_list().on_get_randomness(lookback))?;
792
793 t.record(
794 self.call_manager
795 .externs()
796 .get_chain_randomness(rand_epoch)
797 .or_illegal_argument(),
798 )
799 }
800
801 fn get_randomness_from_beacon(
802 &self,
803 rand_epoch: ChainEpoch,
804 ) -> Result<[u8; RANDOMNESS_LENGTH]> {
805 let lookback = self
806 .call_manager
807 .context()
808 .epoch
809 .checked_sub(rand_epoch)
810 .ok_or_else(|| syscall_error!(IllegalArgument; "randomness epoch {} is in the future", rand_epoch))?;
811
812 let t = self
813 .call_manager
814 .charge_gas(self.call_manager.price_list().on_get_randomness(lookback))?;
815
816 t.record(
817 self.call_manager
818 .externs()
819 .get_beacon_randomness(rand_epoch)
820 .or_illegal_argument(),
821 )
822 }
823}
824
825impl<C> ActorOps for DefaultKernel<C>
826where
827 C: CallManager,
828{
829 fn resolve_address(&self, address: &Address) -> Result<ActorID> {
830 let t = self
831 .call_manager
832 .charge_gas(self.call_manager.price_list().on_resolve_address())?;
833
834 t.record(Ok(self
835 .call_manager
836 .resolve_address(address)?
837 .ok_or_else(|| syscall_error!(NotFound; "actor not found"))?))
838 }
839
840 fn get_actor_code_cid(&self, id: ActorID) -> Result<Cid> {
841 let t = self
842 .call_manager
843 .charge_gas(self.call_manager.price_list().on_get_actor_code_cid())?;
844
845 t.record(Ok(self
846 .call_manager
847 .get_actor(id)?
848 .ok_or_else(|| syscall_error!(NotFound; "actor not found"))?
849 .code))
850 }
851
852 fn next_actor_address(&self) -> Result<Address> {
853 Ok(self.call_manager.next_actor_address())
854 }
855
856 fn create_actor(
857 &mut self,
858 code_id: Cid,
859 actor_id: ActorID,
860 delegated_address: Option<Address>,
861 ) -> Result<()> {
862 let is_allowed_to_create_actor = self.actor_id == INIT_ACTOR_ID;
863
864 #[cfg(feature = "testing")]
865 let is_allowed_to_create_actor =
866 is_allowed_to_create_actor || self.actor_id == TEST_ACTOR_ALLOWED_TO_CALL_CREATE_ACTOR;
867
868 if !is_allowed_to_create_actor {
869 return Err(syscall_error!(
870 Forbidden,
871 "create_actor is restricted to InitActor. Called by {}",
872 self.actor_id
873 )
874 .into());
875 }
876
877 if self.read_only {
878 return Err(
879 syscall_error!(ReadOnly, "create_actor cannot be called while read-only").into(),
880 );
881 }
882
883 self.call_manager
884 .create_actor(code_id, actor_id, delegated_address)
885 }
886
887 fn install_actor(&mut self, code_id: Cid) -> Result<()> {
888 let start = GasTimer::start();
889
890 let size = self
891 .call_manager
892 .engine()
893 .preload_all(self.call_manager.blockstore(), &[code_id])
894 .context("failed to install actor")
895 .or_illegal_argument()?;
896
897 let t = self
898 .call_manager
899 .charge_gas(self.call_manager.price_list().on_install_actor(size))?;
900 t.stop_with(start);
901
902 self.call_manager
903 .trace_ipld(IpldOperation::Get, code_id, size);
904
905 Ok(())
906 }
907
908 fn get_builtin_actor_type(&self, code_cid: &Cid) -> Result<u32> {
909 let t = self
910 .call_manager
911 .charge_gas(self.call_manager.price_list().on_get_builtin_actor_type())?;
912
913 let id = self
914 .call_manager
915 .machine()
916 .builtin_actors()
917 .id_by_code(code_cid);
918
919 t.stop();
920 Ok(id)
921 }
922
923 fn get_code_cid_for_type(&self, typ: u32) -> Result<Cid> {
924 let t = self
925 .call_manager
926 .charge_gas(self.call_manager.price_list().on_get_code_cid_for_type())?;
927
928 t.record(
929 self.call_manager
930 .machine()
931 .builtin_actors()
932 .code_by_id(typ)
933 .cloned()
934 .context("tried to resolve CID of unrecognized actor type")
935 .or_illegal_argument(),
936 )
937 }
938
939 fn balance_of(&self, actor_id: ActorID) -> Result<TokenAmount> {
940 let t = self
941 .call_manager
942 .charge_gas(self.call_manager.price_list().on_balance_of())?;
943
944 Ok(t.record(self.call_manager.get_actor(actor_id))?
945 .ok_or_else(|| syscall_error!(NotFound; "actor not found"))?
946 .balance)
947 }
948
949 fn lookup_delegated_address(&self, actor_id: ActorID) -> Result<Option<Address>> {
950 let t = self
951 .call_manager
952 .charge_gas(self.call_manager.price_list().on_lookup_delegated_address())?;
953
954 Ok(t.record(self.call_manager.get_actor(actor_id))?
955 .ok_or_else(|| syscall_error!(NotFound; "actor not found"))?
956 .delegated_address)
957 }
958}
959
960impl<C> DebugOps for DefaultKernel<C>
961where
962 C: CallManager,
963{
964 fn log(&mut self, msg: String) {
965 self.call_manager.log(msg)
966 }
967
968 fn debug_enabled(&self) -> bool {
969 self.call_manager.context().actor_debugging
970 }
971
972 fn store_artifact(&self, name: &str, data: &[u8]) -> Result<()> {
973 {
975 if name.len() > MAX_ARTIFACT_NAME_LEN {
976 Err("debug artifact name should not exceed 256 bytes")
977 } else if name.chars().any(std::path::is_separator) {
978 Err("debug artifact name should not include any path separators")
979 } else if name
980 .chars()
981 .next()
982 .ok_or("debug artifact name should be at least one character")
983 .or_error(fvm_shared::error::ErrorNumber::IllegalArgument)?
984 == '.'
985 {
986 Err("debug artifact name should not start with a decimal '.'")
987 } else {
988 Ok(())
989 }
990 }
991 .or_error(fvm_shared::error::ErrorNumber::IllegalArgument)?;
992
993 if let Ok(dir) = std::env::var(ENV_ARTIFACT_DIR).as_deref() {
995 let dir: PathBuf = [
996 dir,
997 self.call_manager.machine().machine_id(),
998 &self.call_manager.origin().to_string(),
999 &self.call_manager.nonce().to_string(),
1000 &self.actor_id.to_string(),
1001 &self.call_manager.invocation_count().to_string(),
1002 ]
1003 .iter()
1004 .collect();
1005
1006 if let Err(e) = std::fs::create_dir_all(dir.clone()) {
1007 log::error!("failed to make directory to store debug artifacts {}", e);
1008 } else if let Err(e) = std::fs::write(dir.join(name), data) {
1009 log::error!("failed to store debug artifact {}", e)
1010 } else {
1011 log::info!("wrote artifact: {} to {:?}", name, dir);
1012 }
1013 } else {
1014 log::error!(
1015 "store_artifact was ignored, env var {} was not set",
1016 ENV_ARTIFACT_DIR
1017 )
1018 }
1019 Ok(())
1020 }
1021}
1022
1023impl<C> EventOps for DefaultKernel<C>
1024where
1025 C: CallManager,
1026{
1027 fn emit_event(
1028 &mut self,
1029 event_headers: &[fvm_shared::sys::EventEntry],
1030 event_keys: &[u8],
1031 event_values: &[u8],
1032 ) -> Result<()> {
1033 const MAX_NR_ENTRIES: usize = 255;
1034 const MAX_KEY_LEN: usize = 31;
1035 const MAX_TOTAL_VALUES_LEN: usize = 8 << 10;
1036
1037 if self.read_only {
1038 return Err(syscall_error!(ReadOnly; "cannot emit events while read-only").into());
1039 }
1040
1041 let t = self
1042 .call_manager
1043 .charge_gas(self.call_manager.price_list().on_actor_event(
1044 event_headers.len(),
1045 event_keys.len(),
1046 event_values.len(),
1047 ))?;
1048
1049 if event_headers.len() > MAX_NR_ENTRIES {
1050 return Err(syscall_error!(LimitExceeded; "event exceeded max entries: {} > {MAX_NR_ENTRIES}", event_headers.len()).into());
1051 }
1052
1053 if event_values.len() > MAX_TOTAL_VALUES_LEN {
1054 return Err(syscall_error!(LimitExceeded; "total event value lengths exceeded the max size: {} > {MAX_TOTAL_VALUES_LEN}", event_values.len()).into());
1055 }
1056
1057 let event_keys = std::str::from_utf8(event_keys)
1059 .context("invalid event key")
1060 .or_illegal_argument()?;
1061
1062 let mut key_offset: usize = 0;
1063 let mut val_offset: usize = 0;
1064
1065 let mut entries: Vec<Entry> = Vec::with_capacity(event_headers.len());
1066 for header in event_headers {
1067 let flags = header.flags;
1069 if Flags::from_bits(flags.bits()).is_none() {
1070 return Err(
1071 syscall_error!(IllegalArgument; "event flags are invalid: {}", flags.bits())
1072 .into(),
1073 );
1074 }
1075
1076 if header.key_len > MAX_KEY_LEN as u32 {
1077 let tmp = header.key_len;
1078 return Err(syscall_error!(LimitExceeded; "event key exceeded max size: {} > {MAX_KEY_LEN}", tmp).into());
1079 }
1080
1081 if header.val_len > MAX_TOTAL_VALUES_LEN as u32 {
1084 return Err(
1085 syscall_error!(IllegalArgument; "event entry value out of range").into(),
1086 );
1087 }
1088
1089 let key = &event_keys
1091 .get(key_offset..key_offset + header.key_len as usize)
1092 .context("event entry key out of range")
1093 .or_illegal_argument()?;
1094
1095 let value = &event_values
1096 .get(val_offset..val_offset + header.val_len as usize)
1097 .context("event entry value out of range")
1098 .or_illegal_argument()?;
1099
1100 if header.codec != IPLD_RAW && header.codec != CBOR {
1102 let tmp = header.codec;
1103 return Err(
1104 syscall_error!(IllegalCodec; "event codec must be IPLD_RAW or CBOR, was: {}", tmp)
1105 .into(),
1106 );
1107 }
1108
1109 let entry = Entry {
1111 flags: header.flags,
1112 key: key.to_string(),
1113 codec: header.codec,
1114 value: value.to_vec(),
1115 };
1116
1117 key_offset += header.key_len as usize;
1119 val_offset += header.val_len as usize;
1120
1121 entries.push(entry);
1122 }
1123
1124 if key_offset != event_keys.len() {
1125 return Err(syscall_error!(IllegalArgument;
1126 "event key buffer length is too large: {} < {}",
1127 key_offset,
1128 event_keys.len()
1129 )
1130 .into());
1131 }
1132
1133 if val_offset != event_values.len() {
1134 return Err(syscall_error!(IllegalArgument;
1135 "event value buffer length is too large: {} < {}",
1136 val_offset,
1137 event_values.len()
1138 )
1139 .into());
1140 }
1141
1142 let actor_evt = ActorEvent::from(entries);
1143
1144 let stamped_evt = StampedEvent::new(self.actor_id, actor_evt);
1145 #[cfg(feature = "gas_calibration")]
1147 let _ = fvm_ipld_encoding::to_vec(&stamped_evt).unwrap();
1148
1149 self.call_manager.append_event(stamped_evt);
1150
1151 t.stop();
1152
1153 Ok(())
1154 }
1155}