1use crate::beacon::BeaconEntry;
5use crate::blocks::{CachingBlockHeader, Ticket, TipsetKey};
6use crate::blocks::{ElectionProof, RawBlockHeader};
7
8use crate::chain::{ChainStore, compute_base_fee};
9
10use crate::db::EthMappingsStore;
11use crate::fil_cns::weight;
12use crate::interpreter::VMTrace;
13use crate::key_management::{Key, KeyStore};
14use crate::lotus_json::lotus_json_with_self;
15
16use crate::lotus_json::LotusJson;
17use crate::message::SignedMessage;
18use crate::networks::Height;
19
20use crate::rpc::reflect::Permission;
21use crate::rpc::types::{ApiTipsetKey, MiningBaseInfo};
22use crate::rpc::{ApiPaths, Ctx, RpcMethod, ServerError};
23use crate::shim::address::Address;
24use crate::shim::clock::ChainEpoch;
25use crate::shim::crypto::{Signature, SignatureType};
26use crate::state_manager::ExecutedTipset;
27use enumflags2::BitFlags;
28
29use crate::shim::sector::PoStProof;
30use crate::utils::db::CborStoreExt;
31
32use crate::shim::crypto::BLS_SIG_LEN;
33use anyhow::{Context as _, Result};
34use bls_signatures::Serialize as _;
35use cid::Cid;
36use fil_actors_shared::fvm_ipld_amt::Amtv0 as Amt;
37use fvm_ipld_blockstore::Blockstore;
38use fvm_ipld_encoding::tuple::*;
39use group::prime::PrimeCurveAffine as _;
40use itertools::Itertools;
41use parking_lot::RwLock;
42use schemars::JsonSchema;
43use serde::{Deserialize, Serialize};
44
45use std::sync::Arc;
46
47#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, JsonSchema)]
48#[serde(rename_all = "PascalCase")]
49pub struct BlockTemplate {
50 #[schemars(with = "LotusJson<Address>")]
51 #[serde(with = "crate::lotus_json")]
52 pub miner: Address,
53 #[schemars(with = "LotusJson<TipsetKey>")]
54 #[serde(with = "crate::lotus_json")]
55 pub parents: TipsetKey,
56 #[schemars(with = "LotusJson<Ticket>")]
57 #[serde(with = "crate::lotus_json")]
58 pub ticket: Ticket,
59 #[schemars(with = "LotusJson<ElectionProof>")]
60 #[serde(with = "crate::lotus_json")]
61 pub eproof: ElectionProof,
62 #[schemars(with = "LotusJson<Vec<BeaconEntry>>")]
63 #[serde(with = "crate::lotus_json")]
64 pub beacon_values: Vec<BeaconEntry>,
65 #[schemars(with = "LotusJson<Vec<SignedMessage>>")]
66 #[serde(with = "crate::lotus_json")]
67 pub messages: Vec<SignedMessage>,
68 #[schemars(with = "LotusJson<ChainEpoch>")]
69 #[serde(with = "crate::lotus_json")]
70 pub epoch: ChainEpoch,
71 pub timestamp: u64,
72 #[schemars(with = "LotusJson<Vec<PoStProof>>")]
73 #[serde(rename = "WinningPoStProof", with = "crate::lotus_json")]
74 pub winning_post_proof: Vec<PoStProof>,
75}
76
77lotus_json_with_self!(BlockTemplate);
78
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)]
80#[serde(rename_all = "PascalCase")]
81pub struct BlockMessage {
82 #[schemars(with = "LotusJson<CachingBlockHeader>")]
83 #[serde(with = "crate::lotus_json")]
84 header: CachingBlockHeader,
85 #[schemars(with = "LotusJson<Vec<Cid>>")]
86 #[serde(with = "crate::lotus_json")]
87 bls_messages: Vec<Cid>,
88 #[schemars(with = "LotusJson<Vec<Cid>>")]
89 #[serde(with = "crate::lotus_json")]
90 secpk_messages: Vec<Cid>,
91}
92
93lotus_json_with_self!(BlockMessage);
94
95#[derive(Serialize_tuple)]
96struct MessageMeta {
97 bls_messages: Cid,
98 secpk_messages: Cid,
99}
100
101pub enum MinerCreateBlock {}
102impl RpcMethod<1> for MinerCreateBlock {
103 const NAME: &'static str = "Filecoin.MinerCreateBlock";
104 const PARAM_NAMES: [&'static str; 1] = ["blockTemplate"];
105 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
106 const PERMISSION: Permission = Permission::Write;
107 const DESCRIPTION: Option<&'static str> = Some(
108 "Fills and signs a block template on behalf of the given miner, returning a suitable block header.",
109 );
110
111 type Params = (BlockTemplate,);
112 type Ok = BlockMessage;
113
114 async fn handle(
115 ctx: Ctx<impl Blockstore + EthMappingsStore + Send + Sync + 'static>,
116 (block_template,): Self::Params,
117 _: &http::Extensions,
118 ) -> Result<Self::Ok, ServerError> {
119 let store = ctx.store();
120 let parent_tipset = ctx
121 .chain_index()
122 .load_required_tipset(&block_template.parents)?;
123
124 let lookback_state = ChainStore::get_lookback_tipset_for_round(
125 ctx.chain_index(),
126 ctx.chain_config(),
127 &parent_tipset,
128 block_template.epoch,
129 )
130 .map(|(_, s)| Arc::new(s))?;
131
132 let worker = ctx
133 .state_manager
134 .get_miner_work_addr(*lookback_state, &block_template.miner)?;
135
136 let parent_weight = weight(store, &parent_tipset)?;
137 let heights = &ctx.chain_config().height_infos;
138 let parent_base_fee = compute_base_fee(
139 store,
140 &parent_tipset,
141 heights
142 .get(&Height::Smoke)
143 .context("Missing Smoke height")?
144 .epoch,
145 heights
146 .get(&Height::FireHorse)
147 .context("Missing FireHorse height")?
148 .epoch,
149 )?;
150 let ExecutedTipset {
151 state_root,
152 receipt_root,
153 ..
154 } = ctx
155 .state_manager
156 .compute_tipset_state(
157 parent_tipset,
158 crate::state_manager::NO_CALLBACK,
159 VMTrace::NotTraced,
160 )
161 .await?;
162
163 let network_version = ctx.state_manager.get_network_version(block_template.epoch);
164
165 let mut bls_messages = Vec::new();
166 let mut secpk_messages = Vec::new();
167 let mut bls_msg_cids = Vec::new();
168 let mut secpk_msg_cids = Vec::new();
169 let mut bls_sigs = Vec::new();
170
171 for msg in block_template.messages {
172 match msg.signature().signature_type() {
173 SignatureType::Bls => {
174 let cid = ctx.store().put_cbor_default(&msg.message)?;
175 bls_msg_cids.push(cid);
176 bls_sigs.push(msg.signature);
177 bls_messages.push(msg.message);
178 }
179 SignatureType::Secp256k1 | SignatureType::Delegated => {
180 if msg.signature.is_valid_secpk_sig_type(network_version) {
181 let cid = ctx.store().put_cbor_default(&msg)?;
182 secpk_msg_cids.push(cid);
183 secpk_messages.push(msg);
184 } else {
185 Err(anyhow::anyhow!(
186 "unknown sig type: {}",
187 msg.signature.signature_type()
188 ))?;
189 }
190 }
191 }
192 }
193
194 let store = ctx.store();
195 let mut message_array = Amt::<Cid, _>::new(store);
196 for (i, cid) in bls_msg_cids.iter().enumerate() {
197 message_array.set(i as u64, *cid)?;
198 }
199 let bls_msgs_root = message_array.flush()?;
200 let mut message_array = Amt::<Cid, _>::new(store);
201 for (i, cid) in secpk_msg_cids.iter().enumerate() {
202 message_array.set(i as u64, *cid)?;
203 }
204 let secpk_msgs_root = message_array.flush()?;
205
206 let message_meta_cid = store.put_cbor_default(&MessageMeta {
207 bls_messages: bls_msgs_root,
208 secpk_messages: secpk_msgs_root,
209 })?;
210
211 let bls_aggregate = aggregate_from_bls_signatures(bls_sigs)?;
212
213 let mut block_header = RawBlockHeader {
214 miner_address: block_template.miner,
215 ticket: block_template.ticket.into(),
216 election_proof: block_template.eproof.into(),
217 beacon_entries: block_template.beacon_values,
218 winning_post_proof: block_template.winning_post_proof,
219 parents: block_template.parents,
220 weight: parent_weight,
221 epoch: block_template.epoch,
222 state_root,
223 message_receipts: receipt_root,
224 messages: message_meta_cid,
225 bls_aggregate: bls_aggregate.into(),
226 timestamp: block_template.timestamp,
227 signature: None,
228 fork_signal: Default::default(),
229 parent_base_fee,
230 };
231
232 block_header.signature = sign_block_header(&block_header, &worker, &ctx.keystore)?.into();
233
234 Ok(BlockMessage {
235 header: CachingBlockHeader::from(block_header),
236 bls_messages: bls_msg_cids,
237 secpk_messages: secpk_msg_cids,
238 })
239 }
240}
241
242fn sign_block_header(
243 block_header: &RawBlockHeader,
244 worker: &Address,
245 keystore: &RwLock<KeyStore>,
246) -> Result<Signature> {
247 let signing_bytes = block_header.signing_bytes();
248
249 let key = {
250 let keystore = keystore.read();
251 let key_info = crate::key_management::try_find(worker, &keystore)?;
252 Key::try_from(key_info)?
253 };
254
255 let sig = crate::key_management::sign(
256 *key.key_info.key_type(),
257 key.key_info.private_key(),
258 &signing_bytes,
259 )?;
260 Ok(sig)
261}
262
263fn aggregate_from_bls_signatures(bls_sigs: Vec<Signature>) -> anyhow::Result<Signature> {
264 let signatures: Vec<_> = bls_sigs
265 .iter()
266 .map(|sig| anyhow::Ok(bls_signatures::Signature::from_bytes(sig.bytes())?))
267 .try_collect()?;
268
269 if signatures.is_empty() {
270 let sig: bls_signatures::Signature = blstrs::G2Affine::identity().into();
271 let mut raw_signature: [u8; BLS_SIG_LEN] = [0; BLS_SIG_LEN];
272 sig.write_bytes(&mut raw_signature.as_mut())?;
273 Ok(Signature::new_bls(raw_signature.to_vec()))
274 } else {
275 let bls_aggregate =
276 bls_signatures::aggregate(&signatures).context("failed to aggregate signatures")?;
277 Ok(Signature {
278 sig_type: SignatureType::Bls,
279 bytes: bls_aggregate.as_bytes().to_vec(),
280 })
281 }
282}
283
284pub enum MinerGetBaseInfo {}
285impl RpcMethod<3> for MinerGetBaseInfo {
286 const NAME: &'static str = "Filecoin.MinerGetBaseInfo";
287 const PARAM_NAMES: [&'static str; 3] = ["minerAddress", "epoch", "tipsetKey"];
288 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
289 const PERMISSION: Permission = Permission::Read;
290 const DESCRIPTION: Option<&'static str> = Some(
291 "Retrieves the Miner Actor at the given address and tipset, returning basic information such as power and mining eligibility.",
292 );
293
294 type Params = (Address, i64, ApiTipsetKey);
295 type Ok = Option<MiningBaseInfo>;
296
297 async fn handle(
298 ctx: Ctx<impl Blockstore + EthMappingsStore + Send + Sync + 'static>,
299 (miner_address, epoch, ApiTipsetKey(tipset_key)): Self::Params,
300 _: &http::Extensions,
301 ) -> Result<Self::Ok, ServerError> {
302 let tipset = ctx
303 .chain_store()
304 .load_required_tipset_or_heaviest(&tipset_key)?;
305
306 Ok(ctx
307 .state_manager
308 .miner_get_base_info(ctx.beacon(), tipset, miner_address, epoch)
309 .await?)
310 }
311}