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