forest/state_manager/
mining.rs1use super::chain_rand::draw_randomness;
5use super::*;
6use crate::beacon::BeaconEntry;
7use crate::db::EthMappingsStore;
8use crate::rpc::types::MiningBaseInfo;
9use crate::shim::randomness::Randomness;
10use crate::shim::runtime::Policy;
11use anyhow::Context as _;
12use fil_actors_shared::v12::runtime::DomainSeparationTag;
13use fvm_ipld_encoding::to_vec;
14use num::BigInt;
15use num_traits::identities::Zero;
16impl<DB> StateManager<DB>
17where
18 DB: Blockstore + Send + Sync + 'static,
19{
20 pub fn eligible_to_mine(
23 &self,
24 address: &Address,
25 base_tipset: &Tipset,
26 lookback_tipset: &Tipset,
27 ) -> anyhow::Result<bool, Error> {
28 let hmp =
29 self.miner_has_min_power(&self.chain_config().policy, address, lookback_tipset)?;
30 let version = self.get_network_version(base_tipset.epoch());
31
32 if version <= NetworkVersion::V3 {
33 return Ok(hmp);
34 }
35
36 if !hmp {
37 return Ok(false);
38 }
39
40 let actor = self
41 .get_actor(&Address::POWER_ACTOR, *base_tipset.parent_state())?
42 .ok_or_else(|| Error::state("Power actor address could not be resolved"))?;
43
44 let power_state = power::State::load(self.blockstore(), actor.code, actor.state)?;
45
46 let actor = self
47 .get_actor(address, *base_tipset.parent_state())?
48 .ok_or_else(|| Error::state("Miner actor address could not be resolved"))?;
49
50 let miner_state = miner::State::load(self.blockstore(), actor.code, actor.state)?;
51
52 let claim = power_state
54 .miner_power(self.blockstore(), address)?
55 .ok_or_else(|| Error::Other("Could not get claim".to_string()))?;
56 if claim.quality_adj_power <= BigInt::zero() {
57 return Ok(false);
58 }
59
60 if !miner_state.fee_debt().is_zero() {
62 return Ok(false);
63 }
64
65 let info = miner_state.info(self.blockstore())?;
67 if base_tipset.epoch() <= info.consensus_fault_elapsed {
68 return Ok(false);
69 }
70
71 Ok(true)
72 }
73
74 pub async fn miner_get_base_info(
75 self: &Arc<Self>,
76 beacon_schedule: &BeaconSchedule,
77 tipset: Tipset,
78 addr: Address,
79 epoch: ChainEpoch,
80 ) -> anyhow::Result<Option<MiningBaseInfo>>
81 where
82 DB: EthMappingsStore,
83 {
84 let prev_beacon = self
85 .chain_store()
86 .chain_index()
87 .latest_beacon_entry(tipset.clone())?;
88
89 let entries: Vec<BeaconEntry> = beacon_schedule
90 .beacon_entries_for_block(
91 self.chain_config().network_version(epoch),
92 epoch,
93 tipset.epoch(),
94 &prev_beacon,
95 )
96 .await?;
97
98 let base = entries.last().unwrap_or(&prev_beacon);
99
100 let (lb_tipset, lb_state_root) = ChainStore::get_lookback_tipset_for_round(
101 self.chain_index(),
102 self.chain_config(),
103 &tipset,
104 epoch,
105 )?;
106
107 let actor = self.get_required_actor(&addr, *tipset.parent_state())?;
112 if self.get_actor(&addr, lb_state_root)?.is_none() {
113 return Ok(None);
114 }
115
116 let miner_state = miner::State::load(self.blockstore(), actor.code, actor.state)?;
117
118 let addr_buf = to_vec(&addr)?;
119 let rand = draw_randomness(
120 base.signature(),
121 DomainSeparationTag::WinningPoStChallengeSeed as i64,
122 epoch,
123 &addr_buf,
124 )?;
125
126 let network_version = self.chain_config().network_version(tipset.epoch());
127 let sectors = self.get_sectors_for_winning_post(
128 &lb_state_root,
129 network_version,
130 &addr,
131 Randomness::new(rand.to_vec()),
132 )?;
133
134 if sectors.is_empty() {
135 return Ok(None);
136 }
137
138 let (miner_power, total_power) = self
139 .get_power(&lb_state_root, Some(&addr))?
140 .context("failed to get power")?;
141
142 let info = miner_state.info(self.blockstore())?;
143
144 let worker_key = self
145 .resolve_to_deterministic_address(info.worker, &tipset)
146 .await?;
147 let eligible = self.eligible_to_mine(&addr, &tipset, &lb_tipset)?;
148
149 Ok(Some(MiningBaseInfo {
150 miner_power: miner_power.quality_adj_power,
151 network_power: total_power.quality_adj_power,
152 sectors,
153 worker_key,
154 sector_size: info.sector_size,
155 prev_beacon_entry: prev_beacon,
156 beacon_entries: entries,
157 eligible_for_mining: eligible,
158 }))
159 }
160
161 pub fn miner_has_min_power(
164 &self,
165 policy: &Policy,
166 addr: &Address,
167 ts: &Tipset,
168 ) -> anyhow::Result<bool> {
169 let actor = self
170 .get_actor(&Address::POWER_ACTOR, *ts.parent_state())?
171 .ok_or_else(|| Error::state("Power actor address could not be resolved"))?;
172 let ps = power::State::load(self.blockstore(), actor.code, actor.state)?;
173
174 ps.miner_nominal_power_meets_consensus_minimum(policy, self.blockstore(), addr)
175 }
176}