1use super::MinerActorStateLoad as _;
5use crate::shim::actors::miner;
6use crate::shim::{
7 actors::{is_account_actor, is_ethaccount_actor, is_placeholder_actor},
8 address::{Address, Payload},
9 randomness::Randomness,
10 sector::{ExtendedSectorInfo, RegisteredPoStProof, RegisteredSealProof},
11 state_tree::ActorState,
12 version::NetworkVersion,
13};
14use crate::state_manager::{StateManager, errors::*};
15use crate::utils::encoding::prover_id_from_u64;
16use cid::Cid;
17use fil_actors_shared::filecoin_proofs_api::post;
18use fil_actors_shared::fvm_ipld_bitfield::BitField;
19use fvm_ipld_blockstore::Blockstore;
20use fvm_ipld_encoding::bytes_32;
21
22impl<DB> StateManager<DB>
23where
24 DB: Blockstore,
25{
26 pub fn get_sectors_for_winning_post(
29 &self,
30 st: &Cid,
31 nv: NetworkVersion,
32 miner_address: &Address,
33 rand: Randomness,
34 ) -> Result<Vec<ExtendedSectorInfo>, anyhow::Error> {
35 let store = self.blockstore();
36
37 let actor = self
38 .get_actor(miner_address, *st)?
39 .ok_or_else(|| Error::state("Miner actor address could not be resolved"))?;
40 let mas = miner::State::load(self.blockstore(), actor.code, actor.state)?;
41
42 let proving_sectors = {
43 let mut proving_sectors = BitField::new();
44
45 if nv < NetworkVersion::V7 {
46 mas.for_each_deadline(&self.chain_config().policy, store, |_, deadline| {
47 let mut fault_sectors = BitField::new();
48 deadline.for_each(store, |_, partition: miner::Partition| {
49 proving_sectors |= partition.all_sectors();
50 fault_sectors |= partition.faulty_sectors();
51 Ok(())
52 })?;
53
54 proving_sectors -= &fault_sectors;
55 Ok(())
56 })?;
57 } else {
58 mas.for_each_deadline(&self.chain_config().policy, store, |_, deadline| {
59 deadline.for_each(store, |_, partition: miner::Partition| {
60 proving_sectors |= &partition.active_sectors();
61 Ok(())
62 })?;
63 Ok(())
64 })?;
65 }
66 proving_sectors
67 };
68
69 let num_prov_sect = proving_sectors.len();
70
71 if num_prov_sect == 0 {
72 return Ok(Vec::new());
73 }
74
75 let info = mas.info(store)?;
76 let spt = RegisteredSealProof::from_sector_size(info.sector_size(), nv);
77
78 let wpt = spt.registered_winning_post_proof()?;
79
80 let m_id = miner_address.id()?;
81
82 let ids = generate_winning_post_sector_challenge(wpt.into(), m_id, rand, num_prov_sect)?;
83
84 let mut iter = proving_sectors.iter();
85
86 let mut selected_sectors = BitField::new();
87 for n in ids {
88 let sno = iter.nth(n as usize).ok_or_else(|| {
89 anyhow::anyhow!(
90 "Error iterating over proving sectors, id {} does not exist",
91 n
92 )
93 })?;
94 selected_sectors.set(sno);
95 }
96
97 let sectors = mas.load_sectors(store, Some(&selected_sectors))?;
98
99 let out = sectors
100 .into_iter()
101 .map(|s_info| ExtendedSectorInfo {
102 proof: s_info.seal_proof.into(),
103 sector_number: s_info.sector_number,
104 sector_key: s_info.sector_key_cid,
105 sealed_cid: s_info.sealed_cid,
106 })
107 .collect();
108
109 Ok(out)
110 }
111}
112
113pub fn is_valid_for_sending(network_version: NetworkVersion, actor: &ActorState) -> bool {
114 if network_version < NetworkVersion::V18 {
128 return is_account_actor(&actor.code);
129 }
130
131 if is_account_actor(&actor.code) || is_ethaccount_actor(&actor.code) {
133 return true;
134 }
135
136 if !is_placeholder_actor(&actor.code)
139 || actor.sequence != 0
140 || actor.delegated_address.is_none()
141 {
142 return false;
143 }
144
145 if let Payload::Delegated(address) = actor
148 .delegated_address
149 .as_ref()
150 .expect("unfallible")
151 .payload()
152 {
153 address.namespace() == Address::ETHEREUM_ACCOUNT_MANAGER_ACTOR.id().unwrap()
154 } else {
155 false
156 }
157}
158
159fn generate_winning_post_sector_challenge(
161 proof: RegisteredPoStProof,
162 prover_id: u64,
163 mut rand: Randomness,
164 eligible_sector_count: u64,
165) -> Result<Vec<u64>, anyhow::Error> {
166 if let Some(b31) = rand.0.get_mut(31) {
168 *b31 &= 0x3f;
169 } else {
170 anyhow::bail!("rand should have at least 32 bytes");
171 }
172
173 post::generate_winning_post_sector_challenge(
174 proof.try_into()?,
175 &bytes_32(&rand.0),
176 eligible_sector_count,
177 prover_id_from_u64(prover_id),
178 )
179}
180
181pub mod state_compute {
182 use crate::{
183 blocks::{FullTipset, Tipset},
184 chain::store::ChainStore,
185 chain_sync::load_full_tipset,
186 db::{
187 MemoryDB,
188 car::{AnyCar, ManyCar},
189 },
190 genesis::read_genesis_header,
191 interpreter::VMTrace,
192 networks::{ChainConfig, NetworkChain},
193 state_manager::{StateManager, StateOutput},
194 utils::net::{DownloadFileOption, download_file_with_cache},
195 };
196 use directories::ProjectDirs;
197 use sonic_rs::JsonValueTrait;
198 use std::{
199 path::{Path, PathBuf},
200 sync::{Arc, LazyLock},
201 time::{Duration, Instant},
202 };
203 use tokio::io::AsyncReadExt;
204 use url::Url;
205
206 const DO_SPACE_ROOT: &str = "https://forest-snapshots.fra1.cdn.digitaloceanspaces.com/";
207
208 #[allow(dead_code)]
209 pub async fn get_state_compute_snapshot(
210 chain: &NetworkChain,
211 epoch: i64,
212 ) -> anyhow::Result<PathBuf> {
213 get_state_snapshot(chain, "state_compute", epoch).await
214 }
215
216 #[allow(dead_code)]
217 async fn get_state_validate_snapshot(
218 chain: &NetworkChain,
219 epoch: i64,
220 ) -> anyhow::Result<PathBuf> {
221 get_state_snapshot(chain, "state_validate", epoch).await
222 }
223
224 #[allow(dead_code)]
225 pub async fn get_state_snapshot(
226 chain: &NetworkChain,
227 bucket: &str,
228 epoch: i64,
229 ) -> anyhow::Result<PathBuf> {
230 let file = format!("{bucket}/{chain}_{epoch}.forest.car.zst");
231 get_state_snapshot_file(&file).await
232 }
233
234 pub async fn get_state_snapshot_file(file: &str) -> anyhow::Result<PathBuf> {
235 static SNAPSHOT_CACHE_DIR: LazyLock<PathBuf> = LazyLock::new(|| {
236 let project_dir = ProjectDirs::from("com", "ChainSafe", "Forest");
237 project_dir
238 .map(|d| d.cache_dir().to_path_buf())
239 .unwrap_or_else(std::env::temp_dir)
240 .join("state_compute_snapshots")
241 });
242
243 let url = Url::parse(&format!("{DO_SPACE_ROOT}{file}"))?;
244 let path = crate::utils::retry(
245 crate::utils::RetryArgs {
246 timeout: Some(Duration::from_secs(30)),
247 max_retries: Some(5),
248 delay: Some(Duration::from_secs(1)),
249 },
250 || {
251 download_file_with_cache(
252 &url,
253 &SNAPSHOT_CACHE_DIR,
254 DownloadFileOption::NonResumable,
255 )
256 },
257 )
258 .await?
259 .path;
260 #[cfg(test)]
261 {
262 use digest::Digest as _;
264 println!(
265 "snapshot: {file}, sha256sum: {}",
266 hex::encode(sha2::Sha256::digest(std::fs::read(&path)?))
267 );
268 }
269 Ok(path)
270 }
271
272 pub async fn prepare_state_compute(
273 chain: &NetworkChain,
274 snapshot: &Path,
275 ) -> anyhow::Result<(Arc<StateManager<ManyCar>>, Tipset, Tipset)> {
276 let snap_car = AnyCar::try_from(snapshot)?;
277 let ts_next = snap_car.heaviest_tipset()?;
278 let db = Arc::new(ManyCar::new(MemoryDB::default()).with_read_only(snap_car)?);
279 let ts = Tipset::load_required(&db, ts_next.parents())?;
280 let chain_config = Arc::new(ChainConfig::from_chain(chain));
281 let genesis_header =
282 read_genesis_header(None, chain_config.genesis_bytes(&db).await?.as_deref(), &db)
283 .await?;
284 let chain_store = Arc::new(ChainStore::new(
285 db.clone(),
286 db.clone(),
287 db.clone(),
288 chain_config,
289 genesis_header,
290 )?);
291 let state_manager = Arc::new(StateManager::new(chain_store.clone())?);
292 Ok((state_manager, ts, ts_next))
293 }
294
295 pub async fn prepare_state_validate(
296 chain: &NetworkChain,
297 snapshot: &Path,
298 ) -> anyhow::Result<(Arc<StateManager<ManyCar>>, FullTipset)> {
299 let (sm, _, ts) = prepare_state_compute(chain, snapshot).await?;
300 let fts = load_full_tipset(sm.chain_store(), ts.key())?;
301 Ok((sm, fts))
302 }
303
304 pub async fn state_compute(
305 state_manager: &Arc<StateManager<ManyCar>>,
306 ts: Tipset,
307 ts_next: &Tipset,
308 ) -> anyhow::Result<()> {
309 let epoch = ts.epoch();
310 let expected_state_root = *ts_next.parent_state();
311 let expected_receipt_root = *ts_next.parent_message_receipts();
312 let start = Instant::now();
313 let StateOutput {
314 state_root,
315 receipt_root,
316 ..
317 } = state_manager
318 .compute_tipset_state(ts, crate::state_manager::NO_CALLBACK, VMTrace::NotTraced)
319 .await?;
320 println!(
321 "epoch: {epoch}, state_root: {state_root}, receipt_root: {receipt_root}, took {}.",
322 humantime::format_duration(start.elapsed())
323 );
324 anyhow::ensure!(
325 state_root == expected_state_root,
326 "state root mismatch, state_root: {state_root}, expected_state_root: {expected_state_root}"
327 );
328 anyhow::ensure!(
329 receipt_root == expected_receipt_root,
330 "receipt root mismatch, receipt_root: {receipt_root}, expected_receipt_root: {expected_receipt_root}"
331 );
332 Ok(())
333 }
334
335 pub async fn list_state_snapshot_files() -> anyhow::Result<Vec<String>> {
336 let url = Url::parse(&format!("{DO_SPACE_ROOT}?format=json&prefix=state_"))?;
337 let mut json_str = String::new();
338 crate::utils::net::reader(url.as_str(), DownloadFileOption::NonResumable, None)
339 .await?
340 .read_to_string(&mut json_str)
341 .await?;
342 let obj: sonic_rs::Object = sonic_rs::from_str(&json_str)?;
343 let files = obj
344 .iter()
345 .filter_map(|(k, v)| {
346 if k == "Contents"
347 && let sonic_rs::ValueRef::Array(arr) = v.as_ref()
348 && let Some(first) = arr.first()
349 && let Some(file) = first.as_str()
350 && file.ends_with(".car.zst")
351 {
352 Some(file.to_string())
353 } else {
354 None
355 }
356 })
357 .collect();
358 Ok(files)
359 }
360
361 #[cfg(test)]
362 mod tests {
363 use super::*;
368 #[cfg(feature = "cargo-test")]
369 use crate::chain_sync::tipset_syncer::validate_tipset;
370
371 #[tokio::test(flavor = "multi_thread")]
372 async fn test_list_state_snapshot_files() {
373 let files = list_state_snapshot_files().await.unwrap();
374 println!("{files:?}");
375 assert!(files.len() > 1);
376 get_state_snapshot_file(&files[0]).await.unwrap();
377 }
378
379 #[cfg(feature = "cargo-test")]
381 #[tokio::test(flavor = "multi_thread")]
382 async fn cargo_test_state_compute_mainnet_5709604() {
383 let chain = NetworkChain::Mainnet;
384 let snapshot = get_state_compute_snapshot(&chain, 5709604).await.unwrap();
385 let (sm, ts, ts_next) = prepare_state_compute(&chain, &snapshot).await.unwrap();
386 state_compute(&sm, ts, &ts_next).await.unwrap();
387 }
388
389 #[cfg(feature = "cargo-test")]
391 #[tokio::test(flavor = "multi_thread")]
392 async fn cargo_test_state_compute_calibnet_3408952() {
393 let chain = NetworkChain::Calibnet;
394 let snapshot = get_state_compute_snapshot(&chain, 3408952).await.unwrap();
395 let (sm, ts, ts_next) = prepare_state_compute(&chain, &snapshot).await.unwrap();
396 state_compute(&sm, ts, &ts_next).await.unwrap();
397 }
398
399 #[cfg(feature = "cargo-test")]
401 #[tokio::test(flavor = "multi_thread")]
402 async fn cargo_test_state_compute_calibnet_16801() {
403 let chain = NetworkChain::Calibnet;
404 let snapshot = get_state_compute_snapshot(&chain, 16801).await.unwrap();
405 let (sm, ts, ts_next) = prepare_state_compute(&chain, &snapshot).await.unwrap();
406 state_compute(&sm, ts, &ts_next).await.unwrap();
407 }
408
409 #[cfg(feature = "cargo-test")]
411 #[tokio::test(flavor = "multi_thread")]
412 async fn cargo_test_state_compute_calibnet_322355() {
413 let chain = NetworkChain::Calibnet;
414 let snapshot = get_state_compute_snapshot(&chain, 322355).await.unwrap();
415 let (sm, ts, ts_next) = prepare_state_compute(&chain, &snapshot).await.unwrap();
416 state_compute(&sm, ts, &ts_next).await.unwrap();
417 }
418
419 #[cfg(feature = "cargo-test")]
421 #[tokio::test(flavor = "multi_thread")]
422 async fn cargo_test_state_compute_calibnet_489095() {
423 let chain = NetworkChain::Calibnet;
424 let snapshot = get_state_compute_snapshot(&chain, 489095).await.unwrap();
425 let (sm, ts, ts_next) = prepare_state_compute(&chain, &snapshot).await.unwrap();
426 state_compute(&sm, ts, &ts_next).await.unwrap();
427 }
428
429 #[cfg(feature = "cargo-test")]
431 #[tokio::test(flavor = "multi_thread")]
432 async fn cargo_test_state_compute_calibnet_1013135() {
433 let chain = NetworkChain::Calibnet;
434 let snapshot = get_state_compute_snapshot(&chain, 1013135).await.unwrap();
435 let (sm, ts, ts_next) = prepare_state_compute(&chain, &snapshot).await.unwrap();
436 state_compute(&sm, ts, &ts_next).await.unwrap();
437 }
438
439 #[cfg(feature = "cargo-test")]
441 #[tokio::test(flavor = "multi_thread")]
442 async fn cargo_test_state_compute_calibnet_1427975() {
443 let chain = NetworkChain::Calibnet;
444 let snapshot = get_state_compute_snapshot(&chain, 1427975).await.unwrap();
445 let (sm, ts, ts_next) = prepare_state_compute(&chain, &snapshot).await.unwrap();
446 state_compute(&sm, ts, &ts_next).await.unwrap();
447 }
448
449 #[cfg(feature = "cargo-test")]
451 #[tokio::test(flavor = "multi_thread")]
452 async fn cargo_test_state_compute_calibnet_1779095() {
453 let chain = NetworkChain::Calibnet;
454 let snapshot = get_state_compute_snapshot(&chain, 1779095).await.unwrap();
455 let (sm, ts, ts_next) = prepare_state_compute(&chain, &snapshot).await.unwrap();
456 state_compute(&sm, ts, &ts_next).await.unwrap();
457 }
458
459 #[cfg(feature = "cargo-test")]
461 #[tokio::test(flavor = "multi_thread")]
462 async fn cargo_test_state_compute_calibnet_2078795() {
463 let chain = NetworkChain::Calibnet;
464 let snapshot = get_state_compute_snapshot(&chain, 2078795).await.unwrap();
465 let (sm, ts, ts_next) = prepare_state_compute(&chain, &snapshot).await.unwrap();
466 state_compute(&sm, ts, &ts_next).await.unwrap();
467 }
468
469 #[cfg(feature = "cargo-test")]
471 #[tokio::test(flavor = "multi_thread")]
472 async fn cargo_test_state_compute_calibnet_2523455() {
473 let chain = NetworkChain::Calibnet;
474 let snapshot = get_state_compute_snapshot(&chain, 2523455).await.unwrap();
475 let (sm, ts, ts_next) = prepare_state_compute(&chain, &snapshot).await.unwrap();
476 state_compute(&sm, ts, &ts_next).await.unwrap();
477 }
478
479 #[cfg(feature = "cargo-test")]
481 #[tokio::test(flavor = "multi_thread")]
482 async fn cargo_test_state_compute_calibnet_3007295() {
483 let chain = NetworkChain::Calibnet;
484 let snapshot = get_state_compute_snapshot(&chain, 3007295).await.unwrap();
485 let (sm, ts, ts_next) = prepare_state_compute(&chain, &snapshot).await.unwrap();
486 state_compute(&sm, ts, &ts_next).await.unwrap();
487 }
488
489 #[cfg(feature = "cargo-test")]
490 #[tokio::test(flavor = "multi_thread")]
491 async fn cargo_test_state_validate_mainnet_5688000() {
492 let chain = NetworkChain::Mainnet;
493 let snapshot = get_state_validate_snapshot(&chain, 5688000).await.unwrap();
494 let (sm, fts) = prepare_state_validate(&chain, &snapshot).await.unwrap();
495 validate_tipset(&sm, fts, None).await.unwrap();
496 }
497
498 #[cfg(feature = "cargo-test")]
500 #[tokio::test(flavor = "multi_thread")]
501 async fn cargo_test_state_validate_calibnet_16802() {
502 let chain = NetworkChain::Calibnet;
503 let snapshot = get_state_validate_snapshot(&chain, 16802).await.unwrap();
504 let (sm, fts) = prepare_state_validate(&chain, &snapshot).await.unwrap();
505 validate_tipset(&sm, fts, None).await.unwrap();
506 }
507
508 #[cfg(feature = "cargo-test")]
510 #[tokio::test(flavor = "multi_thread")]
511 async fn cargo_test_state_validate_calibnet_322356() {
512 let chain = NetworkChain::Calibnet;
513 let snapshot = get_state_validate_snapshot(&chain, 322356).await.unwrap();
514 let (sm, fts) = prepare_state_validate(&chain, &snapshot).await.unwrap();
515 validate_tipset(&sm, fts, None).await.unwrap();
516 }
517 }
518}
519
520#[cfg(test)]
521mod test {
522 use crate::shim::{address::Address, econ::TokenAmount, state_tree::ActorState};
523 use cid::Cid;
524
525 use super::*;
526
527 #[test]
528 fn is_valid_for_sending_test() {
529 let create_actor = |code: &Cid, sequence: u64, delegated_address: Option<Address>| {
530 ActorState::new(
531 code.to_owned(),
532 Cid::try_from("bafk2bzaceavfgpiw6whqigmskk74z4blm22nwjfnzxb4unlqz2e4wgcthulhu")
534 .unwrap(),
535 TokenAmount::default(),
536 sequence,
537 delegated_address,
538 )
539 };
540
541 let account_actor_cid =
543 Cid::try_from("bafk2bzaceavfgpiw6whqigmskk74z4blm22nwjfnzxb4unlqz2e4wg3c5ujpw")
544 .unwrap();
545 let ethaccount_actor_cid =
546 Cid::try_from("bafk2bzacebiyrhz32xwxi6xql67aaq5nrzeelzas472kuwjqmdmgwotpkj35e")
547 .unwrap();
548 let placeholder_actor_cid =
549 Cid::try_from("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro")
550 .unwrap();
551
552 let actor = create_actor(&account_actor_cid, 0, None);
554 assert!(is_valid_for_sending(NetworkVersion::V17, &actor));
555
556 let actor = create_actor(ðaccount_actor_cid, 0, None);
558 assert!(!is_valid_for_sending(NetworkVersion::V17, &actor));
559
560 assert!(is_valid_for_sending(NetworkVersion::V18, &actor));
562
563 let actor = create_actor(&placeholder_actor_cid, 0, None);
565 assert!(!is_valid_for_sending(NetworkVersion::V18, &actor));
566
567 let delegated_address = Address::new_delegated(
569 Address::ETHEREUM_ACCOUNT_MANAGER_ACTOR.id().unwrap(),
570 &[0; 20],
571 )
572 .ok();
573 let actor = create_actor(&placeholder_actor_cid, 0, delegated_address);
574 assert!(is_valid_for_sending(NetworkVersion::V18, &actor));
575
576 let actor = create_actor(&placeholder_actor_cid, 1, delegated_address);
578 assert!(!is_valid_for_sending(NetworkVersion::V18, &actor));
579
580 let delegated_address =
582 Address::new_delegated(Address::CHAOS_ACTOR.id().unwrap(), &[0; 20]).ok();
583 let actor = create_actor(&placeholder_actor_cid, 0, delegated_address);
584 assert!(!is_valid_for_sending(NetworkVersion::V18, &actor));
585 }
586}
587
588pub mod structured {
590 use crate::{
591 rpc::state::{ActorTrace, ExecutionTrace, GasTrace, MessageTrace, ReturnTrace},
592 shim::kernel::ErrorNumber,
593 };
594 use std::collections::VecDeque;
595
596 use crate::shim::{
597 address::Address,
598 error::ExitCode,
599 gas::GasCharge,
600 kernel::SyscallError,
601 trace::{Call, CallReturn, ExecutionEvent},
602 };
603 use fvm_ipld_encoding::{RawBytes, ipld_block::IpldBlock};
604 use itertools::Either;
605
606 enum CallTreeReturn {
607 Return(CallReturn),
608 Abort(ExitCode),
609 Error(SyscallError),
610 }
611
612 #[derive(Debug, thiserror::Error)]
613 pub enum BuildExecutionTraceError {
614 #[error(
615 "every ExecutionEvent::Return | ExecutionEvent::CallError should be preceded by an ExecutionEvent::Call, but this one wasn't"
616 )]
617 UnexpectedReturn,
618 #[error(
619 "every ExecutionEvent::Call should have a corresponding ExecutionEvent::Return, but this one didn't"
620 )]
621 NoReturn,
622 #[error("unrecognised ExecutionEvent variant: {0:?}")]
623 UnrecognisedEvent(Box<dyn std::fmt::Debug + Send + Sync + 'static>),
624 }
625
626 pub fn parse_events(
655 events: Vec<ExecutionEvent>,
656 ) -> anyhow::Result<Option<ExecutionTrace>, BuildExecutionTraceError> {
657 let mut events = VecDeque::from(events);
658 let mut front_load_me = vec![];
659 let mut call_trees = vec![];
660
661 while let Some(event) = events.pop_front() {
663 match event {
664 ExecutionEvent::GasCharge(gc) => front_load_me.push(gc),
665 ExecutionEvent::Call(call) => call_trees.push(ExecutionTrace::parse(call, {
666 for gc in front_load_me.drain(..).rev() {
671 events.push_front(ExecutionEvent::GasCharge(gc))
672 }
673 &mut events
674 })?),
675 ExecutionEvent::CallReturn(_)
676 | ExecutionEvent::CallAbort(_)
677 | ExecutionEvent::CallError(_) => {
678 return Err(BuildExecutionTraceError::UnexpectedReturn);
679 }
680 ExecutionEvent::Log(_ignored) => {}
681 ExecutionEvent::InvokeActor(_cid) => {}
682 ExecutionEvent::Ipld { .. } => {}
683 ExecutionEvent::Unknown(u) => {
684 return Err(BuildExecutionTraceError::UnrecognisedEvent(Box::new(u)));
685 }
686 }
687 }
688
689 if !front_load_me.is_empty() {
690 tracing::warn!(
691 "vm tracing: ignoring {} trailing gas charges",
692 front_load_me.len()
693 );
694 }
695
696 match call_trees.len() {
697 0 => Ok(None),
698 1 => Ok(Some(call_trees.remove(0))),
699 many => {
700 tracing::warn!(
701 "vm tracing: ignoring {} call trees at the root level",
702 many - 1
703 );
704 Ok(Some(call_trees.remove(0)))
705 }
706 }
707 }
708
709 impl ExecutionTrace {
710 fn parse(
722 call: Call,
723 events: &mut VecDeque<ExecutionEvent>,
724 ) -> Result<ExecutionTrace, BuildExecutionTraceError> {
725 let mut gas_charges = vec![];
726 let mut subcalls = vec![];
727 let mut actor_trace = None;
728
729 while let Some(event) = events.pop_front() {
731 let found_return = match event {
732 ExecutionEvent::GasCharge(gc) => {
733 gas_charges.push(to_gas_trace(gc));
734 None
735 }
736 ExecutionEvent::Call(call) => {
737 subcalls.push(Self::parse(call, events)?);
738 None
739 }
740 ExecutionEvent::CallReturn(ret) => Some(CallTreeReturn::Return(ret)),
741 ExecutionEvent::CallAbort(ab) => Some(CallTreeReturn::Abort(ab)),
742 ExecutionEvent::CallError(e) => Some(CallTreeReturn::Error(e)),
743 ExecutionEvent::Log(_ignored) => None,
744 ExecutionEvent::InvokeActor(cid) => {
745 actor_trace = match cid {
746 Either::Left(_cid) => None,
747 Either::Right(actor) => Some(ActorTrace {
748 id: actor.id,
749 state: actor.state,
750 }),
751 };
752 None
753 }
754 ExecutionEvent::Ipld { .. } => None,
755 ExecutionEvent::Unknown(u) => {
759 return Err(BuildExecutionTraceError::UnrecognisedEvent(Box::new(u)));
760 }
761 };
762
763 if let Some(ret) = found_return {
765 return Ok(ExecutionTrace {
766 msg: to_message_trace(call),
767 msg_rct: to_return_trace(ret),
768 gas_charges,
769 subcalls,
770 invoked_actor: actor_trace,
771 });
772 }
773 }
774
775 Err(BuildExecutionTraceError::NoReturn)
776 }
777 }
778
779 fn to_message_trace(call: Call) -> MessageTrace {
780 let (bytes, codec) = to_bytes_codec(call.params);
781 MessageTrace {
782 from: Address::new_id(call.from),
783 to: call.to,
784 value: call.value,
785 method: call.method_num,
786 params: bytes,
787 params_codec: codec,
788 gas_limit: call.gas_limit,
789 read_only: call.read_only,
790 }
791 }
792
793 fn to_return_trace(ret: CallTreeReturn) -> ReturnTrace {
794 match ret {
795 CallTreeReturn::Return(return_code) => {
796 let exit_code = return_code.exit_code.unwrap_or(0.into());
797 let (bytes, codec) = to_bytes_codec(return_code.data);
798 ReturnTrace {
799 exit_code,
800 r#return: bytes,
801 return_codec: codec,
802 }
803 }
804 CallTreeReturn::Abort(exit_code) => ReturnTrace {
805 exit_code,
806 r#return: RawBytes::default(),
807 return_codec: 0,
808 },
809 CallTreeReturn::Error(syscall_error) => match syscall_error.number {
810 ErrorNumber::InsufficientFunds => ReturnTrace {
811 exit_code: ExitCode::from(6),
812 r#return: RawBytes::default(),
813 return_codec: 0,
814 },
815 _ => ReturnTrace {
816 exit_code: ExitCode::from(0),
817 r#return: RawBytes::default(),
818 return_codec: 0,
819 },
820 },
821 }
822 }
823
824 fn to_bytes_codec(data: Either<RawBytes, Option<IpldBlock>>) -> (RawBytes, u64) {
825 match data {
826 Either::Left(l) => (l, 0),
827 Either::Right(r) => match r {
828 Some(b) => (RawBytes::from(b.data), b.codec),
829 None => (RawBytes::default(), 0),
830 },
831 }
832 }
833
834 fn to_gas_trace(gc: GasCharge) -> GasTrace {
835 GasTrace {
836 name: gc.name().into(),
837 total_gas: gc.total().round_up(),
838 compute_gas: gc.compute_gas().round_up(),
839 storage_gas: gc.other_gas().round_up(),
840 time_taken: gc.elapsed().as_nanos(),
841 }
842 }
843}