1use crate::shim::actors::miner;
5use crate::shim::{
6 actors::{is_account_actor, is_ethaccount_actor, is_placeholder_actor},
7 address::{Address, Payload},
8 randomness::Randomness,
9 sector::{ExtendedSectorInfo, RegisteredPoStProof, RegisteredSealProof},
10 state_tree::ActorState,
11 version::NetworkVersion,
12};
13use crate::utils::encoding::prover_id_from_u64;
14use cid::Cid;
15use fil_actors_shared::filecoin_proofs_api::post;
16use fil_actors_shared::fvm_ipld_bitfield::BitField;
17use fvm_ipld_blockstore::Blockstore;
18use fvm_ipld_encoding::bytes_32;
19
20use crate::state_manager::{StateManager, errors::*};
21
22use super::MinerActorStateLoad as _;
23
24impl<DB> StateManager<DB>
25where
26 DB: Blockstore,
27{
28 pub fn get_sectors_for_winning_post(
31 &self,
32 st: &Cid,
33 nv: NetworkVersion,
34 miner_address: &Address,
35 rand: Randomness,
36 ) -> Result<Vec<ExtendedSectorInfo>, anyhow::Error> {
37 let store = self.blockstore();
38
39 let actor = self
40 .get_actor(miner_address, *st)?
41 .ok_or_else(|| Error::state("Miner actor address could not be resolved"))?;
42 let mas = miner::State::load(self.blockstore(), actor.code, actor.state)?;
43
44 let proving_sectors = {
45 let mut proving_sectors = BitField::new();
46
47 if nv < NetworkVersion::V7 {
48 mas.for_each_deadline(&self.chain_config().policy, store, |_, deadline| {
49 let mut fault_sectors = BitField::new();
50 deadline.for_each(store, |_, partition: miner::Partition| {
51 proving_sectors |= partition.all_sectors();
52 fault_sectors |= partition.faulty_sectors();
53 Ok(())
54 })?;
55
56 proving_sectors -= &fault_sectors;
57 Ok(())
58 })?;
59 } else {
60 mas.for_each_deadline(&self.chain_config().policy, store, |_, deadline| {
61 deadline.for_each(store, |_, partition: miner::Partition| {
62 proving_sectors |= &partition.active_sectors();
63 Ok(())
64 })?;
65 Ok(())
66 })?;
67 }
68 proving_sectors
69 };
70
71 let num_prov_sect = proving_sectors.len();
72
73 if num_prov_sect == 0 {
74 return Ok(Vec::new());
75 }
76
77 let info = mas.info(store)?;
78 let spt = RegisteredSealProof::from_sector_size(info.sector_size().into(), nv);
79
80 let wpt = spt.registered_winning_post_proof()?;
81
82 let m_id = miner_address.id()?;
83
84 let ids = generate_winning_post_sector_challenge(wpt.into(), m_id, rand, num_prov_sect)?;
85
86 let mut iter = proving_sectors.iter();
87
88 let mut selected_sectors = BitField::new();
89 for n in ids {
90 let sno = iter.nth(n as usize).ok_or_else(|| {
91 anyhow::anyhow!(
92 "Error iterating over proving sectors, id {} does not exist",
93 n
94 )
95 })?;
96 selected_sectors.set(sno);
97 }
98
99 let sectors = mas.load_sectors(store, Some(&selected_sectors))?;
100
101 let out = sectors
102 .into_iter()
103 .map(|s_info| ExtendedSectorInfo {
104 proof: s_info.seal_proof.into(),
105 sector_number: s_info.sector_number,
106 sector_key: s_info.sector_key_cid,
107 sealed_cid: s_info.sealed_cid,
108 })
109 .collect();
110
111 Ok(out)
112 }
113}
114
115pub fn is_valid_for_sending(network_version: NetworkVersion, actor: &ActorState) -> bool {
116 if network_version < NetworkVersion::V18 {
130 return is_account_actor(&actor.code);
131 }
132
133 if is_account_actor(&actor.code) || is_ethaccount_actor(&actor.code) {
135 return true;
136 }
137
138 if !is_placeholder_actor(&actor.code)
141 || actor.sequence != 0
142 || actor.delegated_address.is_none()
143 {
144 return false;
145 }
146
147 if let Payload::Delegated(address) = actor
150 .delegated_address
151 .as_ref()
152 .expect("unfallible")
153 .payload()
154 {
155 address.namespace() == Address::ETHEREUM_ACCOUNT_MANAGER_ACTOR.id().unwrap()
156 } else {
157 false
158 }
159}
160
161fn generate_winning_post_sector_challenge(
163 proof: RegisteredPoStProof,
164 prover_id: u64,
165 mut rand: Randomness,
166 eligible_sector_count: u64,
167) -> Result<Vec<u64>, anyhow::Error> {
168 if let Some(b31) = rand.0.get_mut(31) {
170 *b31 &= 0x3f;
171 } else {
172 anyhow::bail!("rand should have at least 32 bytes");
173 }
174
175 post::generate_winning_post_sector_challenge(
176 proof.try_into()?,
177 &bytes_32(&rand.0),
178 eligible_sector_count,
179 prover_id_from_u64(prover_id),
180 )
181}
182
183#[cfg(test)]
184mod test {
185 use crate::shim::{address::Address, econ::TokenAmount, state_tree::ActorState};
186 use cid::Cid;
187
188 use super::*;
189
190 #[test]
191 fn is_valid_for_sending_test() {
192 let create_actor = |code: &Cid, sequence: u64, delegated_address: Option<Address>| {
193 ActorState::new(
194 code.to_owned(),
195 Cid::try_from("bafk2bzaceavfgpiw6whqigmskk74z4blm22nwjfnzxb4unlqz2e4wgcthulhu")
197 .unwrap(),
198 TokenAmount::default(),
199 sequence,
200 delegated_address,
201 )
202 };
203
204 let account_actor_cid =
206 Cid::try_from("bafk2bzaceavfgpiw6whqigmskk74z4blm22nwjfnzxb4unlqz2e4wg3c5ujpw")
207 .unwrap();
208 let ethaccount_actor_cid =
209 Cid::try_from("bafk2bzacebiyrhz32xwxi6xql67aaq5nrzeelzas472kuwjqmdmgwotpkj35e")
210 .unwrap();
211 let placeholder_actor_cid =
212 Cid::try_from("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro")
213 .unwrap();
214
215 let actor = create_actor(&account_actor_cid, 0, None);
217 assert!(is_valid_for_sending(NetworkVersion::V17, &actor));
218
219 let actor = create_actor(ðaccount_actor_cid, 0, None);
221 assert!(!is_valid_for_sending(NetworkVersion::V17, &actor));
222
223 assert!(is_valid_for_sending(NetworkVersion::V18, &actor));
225
226 let actor = create_actor(&placeholder_actor_cid, 0, None);
228 assert!(!is_valid_for_sending(NetworkVersion::V18, &actor));
229
230 let delegated_address = Address::new_delegated(
232 Address::ETHEREUM_ACCOUNT_MANAGER_ACTOR.id().unwrap(),
233 &[0; 20],
234 )
235 .ok();
236 let actor = create_actor(&placeholder_actor_cid, 0, delegated_address);
237 assert!(is_valid_for_sending(NetworkVersion::V18, &actor));
238
239 let actor = create_actor(&placeholder_actor_cid, 1, delegated_address);
241 assert!(!is_valid_for_sending(NetworkVersion::V18, &actor));
242
243 let delegated_address =
245 Address::new_delegated(Address::CHAOS_ACTOR.id().unwrap(), &[0; 20]).ok();
246 let actor = create_actor(&placeholder_actor_cid, 0, delegated_address);
247 assert!(!is_valid_for_sending(NetworkVersion::V18, &actor));
248 }
249}
250
251pub mod structured {
253 use crate::{
254 rpc::state::{ActorTrace, ExecutionTrace, GasTrace, MessageTrace, ReturnTrace},
255 shim::kernel::ErrorNumber,
256 };
257 use std::collections::VecDeque;
258
259 use crate::shim::{
260 address::Address,
261 error::ExitCode,
262 gas::GasCharge,
263 kernel::SyscallError,
264 trace::{Call, CallReturn, ExecutionEvent},
265 };
266 use fvm_ipld_encoding::{RawBytes, ipld_block::IpldBlock};
267 use itertools::Either;
268
269 enum CallTreeReturn {
270 Return(CallReturn),
271 Abort(ExitCode),
272 Error(SyscallError),
273 }
274
275 #[derive(Debug, thiserror::Error)]
276 pub enum BuildExecutionTraceError {
277 #[error(
278 "every ExecutionEvent::Return | ExecutionEvent::CallError should be preceded by an ExecutionEvent::Call, but this one wasn't"
279 )]
280 UnexpectedReturn,
281 #[error(
282 "every ExecutionEvent::Call should have a corresponding ExecutionEvent::Return, but this one didn't"
283 )]
284 NoReturn,
285 #[error("unrecognised ExecutionEvent variant: {0:?}")]
286 UnrecognisedEvent(Box<dyn std::fmt::Debug + Send + Sync + 'static>),
287 }
288
289 pub fn parse_events(
318 events: Vec<ExecutionEvent>,
319 ) -> anyhow::Result<Option<ExecutionTrace>, BuildExecutionTraceError> {
320 let mut events = VecDeque::from(events);
321 let mut front_load_me = vec![];
322 let mut call_trees = vec![];
323
324 while let Some(event) = events.pop_front() {
326 match event {
327 ExecutionEvent::GasCharge(gc) => front_load_me.push(gc),
328 ExecutionEvent::Call(call) => call_trees.push(ExecutionTrace::parse(call, {
329 for gc in front_load_me.drain(..).rev() {
334 events.push_front(ExecutionEvent::GasCharge(gc))
335 }
336 &mut events
337 })?),
338 ExecutionEvent::CallReturn(_)
339 | ExecutionEvent::CallAbort(_)
340 | ExecutionEvent::CallError(_) => {
341 return Err(BuildExecutionTraceError::UnexpectedReturn);
342 }
343 ExecutionEvent::Log(_ignored) => {}
344 ExecutionEvent::InvokeActor(_cid) => {}
345 ExecutionEvent::Ipld { .. } => {}
346 ExecutionEvent::Unknown(u) => {
347 return Err(BuildExecutionTraceError::UnrecognisedEvent(Box::new(u)));
348 }
349 }
350 }
351
352 if !front_load_me.is_empty() {
353 tracing::warn!(
354 "vm tracing: ignoring {} trailing gas charges",
355 front_load_me.len()
356 );
357 }
358
359 match call_trees.len() {
360 0 => Ok(None),
361 1 => Ok(Some(call_trees.remove(0))),
362 many => {
363 tracing::warn!(
364 "vm tracing: ignoring {} call trees at the root level",
365 many - 1
366 );
367 Ok(Some(call_trees.remove(0)))
368 }
369 }
370 }
371
372 impl ExecutionTrace {
373 fn parse(
385 call: Call,
386 events: &mut VecDeque<ExecutionEvent>,
387 ) -> Result<ExecutionTrace, BuildExecutionTraceError> {
388 let mut gas_charges = vec![];
389 let mut subcalls = vec![];
390 let mut actor_trace = None;
391
392 while let Some(event) = events.pop_front() {
394 let found_return = match event {
395 ExecutionEvent::GasCharge(gc) => {
396 gas_charges.push(to_gas_trace(gc));
397 None
398 }
399 ExecutionEvent::Call(call) => {
400 subcalls.push(Self::parse(call, events)?);
401 None
402 }
403 ExecutionEvent::CallReturn(ret) => Some(CallTreeReturn::Return(ret)),
404 ExecutionEvent::CallAbort(ab) => Some(CallTreeReturn::Abort(ab)),
405 ExecutionEvent::CallError(e) => Some(CallTreeReturn::Error(e)),
406 ExecutionEvent::Log(_ignored) => None,
407 ExecutionEvent::InvokeActor(cid) => {
408 actor_trace = match cid {
409 Either::Left(_cid) => None,
410 Either::Right(actor) => Some(ActorTrace {
411 id: actor.id,
412 state: actor.state,
413 }),
414 };
415 None
416 }
417 ExecutionEvent::Ipld { .. } => None,
418 ExecutionEvent::Unknown(u) => {
422 return Err(BuildExecutionTraceError::UnrecognisedEvent(Box::new(u)));
423 }
424 };
425
426 if let Some(ret) = found_return {
428 return Ok(ExecutionTrace {
429 msg: to_message_trace(call),
430 msg_rct: to_return_trace(ret),
431 gas_charges,
432 subcalls,
433 invoked_actor: actor_trace,
434 });
435 }
436 }
437
438 Err(BuildExecutionTraceError::NoReturn)
439 }
440 }
441
442 fn to_message_trace(call: Call) -> MessageTrace {
443 let (bytes, codec) = to_bytes_codec(call.params);
444 MessageTrace {
445 from: Address::new_id(call.from),
446 to: call.to,
447 value: call.value,
448 method: call.method_num,
449 params: bytes,
450 params_codec: codec,
451 gas_limit: call.gas_limit,
452 read_only: call.read_only,
453 }
454 }
455
456 fn to_return_trace(ret: CallTreeReturn) -> ReturnTrace {
457 match ret {
458 CallTreeReturn::Return(return_code) => {
459 let exit_code = return_code.exit_code.unwrap_or(0.into());
460 let (bytes, codec) = to_bytes_codec(return_code.data);
461 ReturnTrace {
462 exit_code,
463 r#return: bytes,
464 return_codec: codec,
465 }
466 }
467 CallTreeReturn::Abort(exit_code) => ReturnTrace {
468 exit_code,
469 r#return: RawBytes::default(),
470 return_codec: 0,
471 },
472 CallTreeReturn::Error(syscall_error) => match syscall_error.number {
473 ErrorNumber::InsufficientFunds => ReturnTrace {
474 exit_code: ExitCode::from(6),
475 r#return: RawBytes::default(),
476 return_codec: 0,
477 },
478 _ => ReturnTrace {
479 exit_code: ExitCode::from(0),
480 r#return: RawBytes::default(),
481 return_codec: 0,
482 },
483 },
484 }
485 }
486
487 fn to_bytes_codec(data: Either<RawBytes, Option<IpldBlock>>) -> (RawBytes, u64) {
488 match data {
489 Either::Left(l) => (l, 0),
490 Either::Right(r) => match r {
491 Some(b) => (RawBytes::from(b.data), b.codec),
492 None => (RawBytes::default(), 0),
493 },
494 }
495 }
496
497 fn to_gas_trace(gc: GasCharge) -> GasTrace {
498 GasTrace {
499 name: gc.name().into(),
500 total_gas: gc.total().round_up(),
501 compute_gas: gc.compute_gas().round_up(),
502 storage_gas: gc.other_gas().round_up(),
503 time_taken: gc.elapsed().as_nanos(),
504 }
505 }
506}