1use std::{collections::HashMap, str::FromStr};
20
21use darkfi::{
22 blockchain::{BlockInfo, Header, HeaderHash},
23 rpc::jsonrpc::{ErrorCode, ErrorCode::InvalidParams, JsonError, JsonResponse, JsonResult},
24 tx::{ContractCallLeaf, Transaction, TransactionBuilder},
25 util::{encoding::base64, time::Timestamp},
26 validator::{
27 consensus::{Fork, Proposal},
28 pow::{RANDOMX_KEY_CHANGE_DELAY, RANDOMX_KEY_CHANGING_HEIGHT},
29 verification::apply_producer_transaction,
30 },
31 zk::ProvingKey,
32 zkas::ZkBinary,
33 Error, Result,
34};
35use darkfi_money_contract::{client::pow_reward_v1::PoWRewardCallBuilder, MoneyFunction};
36use darkfi_sdk::{
37 crypto::{
38 keypair::{Address, Keypair, SecretKey},
39 pasta_prelude::PrimeField,
40 FuncId, MerkleTree, MONEY_CONTRACT_ID,
41 },
42 pasta::pallas,
43 ContractCall,
44};
45use darkfi_serial::{serialize_async, Encodable};
46use num_bigint::BigUint;
47use rand::rngs::OsRng;
48use tinyjson::JsonValue;
49use tracing::{error, info};
50
51use crate::{proto::ProposalMessage, server_error, DarkfiNode, RpcError};
52
53pub struct MinerRewardsRecipientConfig {
55 pub recipient: Address,
57 pub spend_hook: Option<FuncId>,
59 pub user_data: Option<pallas::Base>,
62}
63
64pub struct BlockTemplate {
66 pub block: BlockInfo,
68 randomx_key: String,
70 target: String,
72 secret: SecretKey,
74}
75
76impl DarkfiNode {
77 pub async fn miner_get_current_mining_randomx_key(
92 &self,
93 id: u16,
94 params: JsonValue,
95 ) -> JsonResult {
96 let Some(params) = params.get::<Vec<JsonValue>>() else {
98 return JsonError::new(InvalidParams, None, id).into()
99 };
100 if !params.is_empty() {
101 return JsonError::new(InvalidParams, None, id).into()
102 }
103
104 let randomx_key = match self.validator.current_mining_randomx_key().await {
106 Ok(key) => key,
107 Err(e) => {
108 error!(
109 target: "darkfid::rpc::current_mining_randomx_key",
110 "[RPC] Retrieving current mining RandomX key failed: {e}",
111 );
112 return JsonError::new(ErrorCode::InternalError, None, id).into()
113 }
114 };
115
116 let response = JsonValue::Array(vec![JsonValue::String(base64::encode(
118 &serialize_async(&randomx_key).await,
119 ))]);
120
121 JsonResponse::new(response, id).into()
122 }
123
124 pub async fn miner_get_header(&self, id: u16, params: JsonValue) -> JsonResult {
144 if !*self.validator.synced.read().await {
146 return server_error(RpcError::NotSynced, id, None)
147 }
148
149 let Some(params) = params.get::<HashMap<String, JsonValue>>() else {
151 return JsonError::new(InvalidParams, None, id).into()
152 };
153 if params.len() < 2 || params.len() > 4 {
154 return JsonError::new(InvalidParams, None, id).into()
155 }
156
157 let Some(header_hash) = params.get("header") else {
159 return server_error(RpcError::MinerMissingHeader, id, None)
160 };
161 let Some(header_hash) = header_hash.get::<String>() else {
162 return server_error(RpcError::MinerInvalidHeader, id, None)
163 };
164 let Ok(header_hash) = HeaderHash::from_str(header_hash) else {
165 return server_error(RpcError::MinerInvalidHeader, id, None)
166 };
167
168 let Some(recipient) = params.get("recipient") else {
170 return server_error(RpcError::MinerMissingRecipient, id, None)
171 };
172 let Some(recipient_str) = recipient.get::<String>() else {
173 return server_error(RpcError::MinerInvalidRecipient, id, None)
174 };
175 let Ok(recipient) = Address::from_str(recipient_str) else {
176 return server_error(RpcError::MinerInvalidRecipient, id, None)
177 };
178 if recipient.network() != self.network {
179 return server_error(RpcError::MinerInvalidRecipientPrefix, id, None)
180 };
181
182 let spend_hook = match params.get("spend_hook") {
184 Some(spend_hook) => {
185 let Some(spend_hook) = spend_hook.get::<String>() else {
186 return server_error(RpcError::MinerInvalidSpendHook, id, None)
187 };
188 let Ok(spend_hook) = FuncId::from_str(spend_hook) else {
189 return server_error(RpcError::MinerInvalidSpendHook, id, None)
190 };
191 Some(spend_hook)
192 }
193 None => None,
194 };
195
196 let user_data: Option<pallas::Base> = match params.get("user_data") {
198 Some(user_data) => {
199 let Some(user_data) = user_data.get::<String>() else {
200 return server_error(RpcError::MinerInvalidUserData, id, None)
201 };
202 let Ok(bytes) = bs58::decode(&user_data).into_vec() else {
203 return server_error(RpcError::MinerInvalidUserData, id, None)
204 };
205 let bytes: [u8; 32] = match bytes.try_into() {
206 Ok(b) => b,
207 Err(_) => return server_error(RpcError::MinerInvalidUserData, id, None),
208 };
209 let Some(user_data) = pallas::Base::from_repr(bytes).into() else {
210 return server_error(RpcError::MinerInvalidUserData, id, None)
211 };
212 Some(user_data)
213 }
214 None => None,
215 };
216
217 let address_bytes =
229 serialize_async(&(recipient_str.clone().into_bytes(), spend_hook, user_data)).await;
230 let mut blocktemplates = self.blocktemplates.lock().await;
231 let mut extended_fork = match self.validator.best_current_fork().await {
232 Ok(f) => f,
233 Err(e) => {
234 error!(
235 target: "darkfid::rpc::miner_get_header",
236 "[RPC] Finding best fork index failed: {e}",
237 );
238 return JsonError::new(ErrorCode::InternalError, None, id).into()
239 }
240 };
241 if let Some(blocktemplate) = blocktemplates.get(&address_bytes) {
242 let last_proposal = match extended_fork.last_proposal() {
243 Ok(p) => p,
244 Err(e) => {
245 error!(
246 target: "darkfid::rpc::miner_get_header",
247 "[RPC] Retrieving best fork last proposal failed: {e}",
248 );
249 return JsonError::new(ErrorCode::InternalError, None, id).into()
250 }
251 };
252 if last_proposal.hash == blocktemplate.block.header.previous {
253 return if blocktemplate.block.header.hash() != header_hash {
254 JsonResponse::new(
255 JsonValue::Array(vec![
256 JsonValue::String(blocktemplate.randomx_key.clone()),
257 JsonValue::String(blocktemplate.target.clone()),
258 JsonValue::String(base64::encode(
259 &serialize_async(&blocktemplate.block.header).await,
260 )),
261 ]),
262 id,
263 )
264 .into()
265 } else {
266 JsonResponse::new(JsonValue::Array(vec![]), id).into()
267 }
268 }
269 blocktemplates.remove(&address_bytes);
270 }
271
272 let spend_hook_str = match spend_hook {
276 Some(spend_hook) => format!("{spend_hook}"),
277 None => String::from("-"),
278 };
279 let user_data_str = match user_data {
280 Some(user_data) => bs58::encode(user_data.to_repr()).into_string(),
281 None => String::from("-"),
282 };
283 let recipient_config = MinerRewardsRecipientConfig { recipient, spend_hook, user_data };
284
285 let (target, block, secret) = match generate_next_block(
287 &mut extended_fork,
288 &recipient_config,
289 &self.powrewardv1_zk.zkbin,
290 &self.powrewardv1_zk.provingkey,
291 self.validator.consensus.module.read().await.target,
292 self.validator.verify_fees,
293 )
294 .await
295 {
296 Ok(v) => v,
297 Err(e) => {
298 error!(
299 target: "darkfid::rpc::miner_get_header",
300 "[RPC] Failed to generate next blocktemplate: {e}",
301 );
302 return JsonError::new(ErrorCode::InternalError, None, id).into()
303 }
304 };
305
306 let randomx_key = if block.header.height > RANDOMX_KEY_CHANGING_HEIGHT &&
310 block.header.height % RANDOMX_KEY_CHANGING_HEIGHT == RANDOMX_KEY_CHANGE_DELAY
311 {
312 base64::encode(&serialize_async(&extended_fork.module.darkfi_rx_keys.1).await)
313 } else {
314 base64::encode(&serialize_async(&extended_fork.module.darkfi_rx_keys.0).await)
315 };
316
317 let target = base64::encode(&target.to_bytes_le());
319
320 let blocktemplate = BlockTemplate {
322 block,
323 randomx_key: randomx_key.clone(),
324 target: target.clone(),
325 secret,
326 };
327
328 let header_hash = blocktemplate.block.header.hash().to_string();
331 let header = base64::encode(&serialize_async(&blocktemplate.block.header).await);
332 blocktemplates.insert(address_bytes, blocktemplate);
333 info!(
334 target: "darkfid::rpc::miner_get_header",
335 "[RPC] Created new blocktemplate: address={recipient_str}, spend_hook={spend_hook_str}, user_data={user_data_str}, hash={header_hash}"
336 );
337
338 let response = JsonValue::Array(vec![
339 JsonValue::String(randomx_key),
340 JsonValue::String(target),
341 JsonValue::String(header),
342 ]);
343
344 JsonResponse::new(response, id).into()
345 }
346
347 pub async fn miner_submit_solution(&self, id: u16, params: JsonValue) -> JsonResult {
363 if !*self.validator.synced.read().await {
365 return server_error(RpcError::NotSynced, id, None)
366 }
367
368 let Some(params) = params.get::<HashMap<String, JsonValue>>() else {
370 return JsonError::new(InvalidParams, None, id).into()
371 };
372 if params.len() < 2 || params.len() > 4 {
373 return JsonError::new(InvalidParams, None, id).into()
374 }
375
376 let Some(recipient) = params.get("recipient") else {
378 return server_error(RpcError::MinerMissingRecipient, id, None)
379 };
380 let Some(recipient_str) = recipient.get::<String>() else {
381 return server_error(RpcError::MinerInvalidRecipient, id, None)
382 };
383 let Ok(recipient) = Address::from_str(recipient_str) else {
384 return server_error(RpcError::MinerInvalidRecipient, id, None)
385 };
386 if recipient.network() != self.network {
387 return server_error(RpcError::MinerInvalidRecipientPrefix, id, None)
388 };
389
390 let spend_hook = match params.get("spend_hook") {
392 Some(spend_hook) => {
393 let Some(spend_hook) = spend_hook.get::<String>() else {
394 return server_error(RpcError::MinerInvalidSpendHook, id, None)
395 };
396 let Ok(spend_hook) = FuncId::from_str(spend_hook) else {
397 return server_error(RpcError::MinerInvalidSpendHook, id, None)
398 };
399 Some(spend_hook)
400 }
401 None => None,
402 };
403
404 let user_data: Option<pallas::Base> = match params.get("user_data") {
406 Some(user_data) => {
407 let Some(user_data) = user_data.get::<String>() else {
408 return server_error(RpcError::MinerInvalidUserData, id, None)
409 };
410 let Ok(bytes) = bs58::decode(&user_data).into_vec() else {
411 return server_error(RpcError::MinerInvalidUserData, id, None)
412 };
413 let bytes: [u8; 32] = match bytes.try_into() {
414 Ok(b) => b,
415 Err(_) => return server_error(RpcError::MinerInvalidUserData, id, None),
416 };
417 let Some(user_data) = pallas::Base::from_repr(bytes).into() else {
418 return server_error(RpcError::MinerInvalidUserData, id, None)
419 };
420 Some(user_data)
421 }
422 None => None,
423 };
424
425 let Some(nonce) = params.get("nonce") else {
427 return server_error(RpcError::MinerMissingNonce, id, None)
428 };
429 let Some(nonce) = nonce.get::<f64>() else {
430 return server_error(RpcError::MinerInvalidNonce, id, None)
431 };
432
433 let address_bytes =
435 serialize_async(&(recipient_str.clone().into_bytes(), spend_hook, user_data)).await;
436 let mut blocktemplates = self.blocktemplates.lock().await;
437 let Some(blocktemplate) = blocktemplates.get(&address_bytes) else {
438 return server_error(RpcError::MinerUnknownJob, id, None)
439 };
440
441 info!(
442 target: "darkfid::rpc::miner_submit_solution",
443 "[RPC] Got solution submission for block template: {}", blocktemplate.block.header.hash(),
444 );
445
446 let mut block = blocktemplate.block.clone();
448 block.header.nonce = *nonce as u64;
449 block.sign(&blocktemplate.secret);
450 info!(
451 target: "darkfid::rpc::miner_submit_solution",
452 "[RPC] Mined block header hash: {}", blocktemplate.block.header.hash(),
453 );
454
455 blocktemplates.remove(&address_bytes);
459
460 info!(
462 target: "darkfid::rpc::miner_submit_solution",
463 "[RPC] Proposing new block to network",
464 );
465 let proposal = Proposal::new(block);
466 if let Err(e) = self.validator.append_proposal(&proposal).await {
467 error!(
468 target: "darkfid::rpc::miner_submit_solution",
469 "[RPC] Error proposing new block: {e}",
470 );
471 return JsonResponse::new(JsonValue::String(String::from("rejected")), id).into()
472 }
473
474 let proposals_sub = self.subscribers.get("proposals").unwrap();
475 let enc_prop = JsonValue::String(base64::encode(&serialize_async(&proposal).await));
476 proposals_sub.notify(vec![enc_prop].into()).await;
477
478 info!(
479 target: "darkfid::rpc::miner_submit_solution",
480 "[RPC] Broadcasting new block to network",
481 );
482 let message = ProposalMessage(proposal);
483 self.p2p_handler.p2p.broadcast(&message).await;
484
485 JsonResponse::new(JsonValue::String(String::from("accepted")), id).into()
486 }
487}
488
489pub async fn generate_next_block(
491 extended_fork: &mut Fork,
492 recipient_config: &MinerRewardsRecipientConfig,
493 zkbin: &ZkBinary,
494 pk: &ProvingKey,
495 block_target: u32,
496 verify_fees: bool,
497) -> Result<(BigUint, BlockInfo, SecretKey)> {
498 let last_proposal = extended_fork.last_proposal()?;
500
501 let next_block_height = last_proposal.block.header.height + 1;
503
504 let (mut txs, _, fees, overlay) = extended_fork
506 .unproposed_txs(&extended_fork.blockchain, next_block_height, block_target, verify_fees)
507 .await?;
508
509 let block_signing_keypair = Keypair::random(&mut OsRng);
514
515 let tx = generate_transaction(
517 next_block_height,
518 fees,
519 &block_signing_keypair,
520 recipient_config,
521 zkbin,
522 pk,
523 )?;
524
525 let _ = apply_producer_transaction(
527 &overlay,
528 next_block_height,
529 block_target,
530 &tx,
531 &mut MerkleTree::new(1),
532 )
533 .await?;
534 txs.push(tx);
535
536 overlay.lock().unwrap().contracts.update_state_monotree(&mut extended_fork.state_monotree)?;
538 let Some(state_root) = extended_fork.state_monotree.get_headroot()? else {
539 return Err(Error::ContractsStatesRootNotFoundError);
540 };
541
542 overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
544
545 let mut header =
547 Header::new(last_proposal.hash, next_block_height, Timestamp::current_time(), 0);
548 header.state_root = state_root;
549
550 let mut next_block = BlockInfo::new_empty(header);
552
553 next_block.append_txs(txs);
555
556 let target = extended_fork.module.next_mine_target()?;
558
559 Ok((target, next_block, block_signing_keypair.secret))
560}
561
562fn generate_transaction(
564 block_height: u32,
565 fees: u64,
566 block_signing_keypair: &Keypair,
567 recipient_config: &MinerRewardsRecipientConfig,
568 zkbin: &ZkBinary,
569 pk: &ProvingKey,
570) -> Result<Transaction> {
571 let debris = PoWRewardCallBuilder {
573 signature_keypair: *block_signing_keypair,
574 block_height,
575 fees,
576 recipient: Some(*recipient_config.recipient.public_key()),
577 spend_hook: recipient_config.spend_hook,
578 user_data: recipient_config.user_data,
579 mint_zkbin: zkbin.clone(),
580 mint_pk: pk.clone(),
581 }
582 .build()?;
583
584 let mut data = vec![MoneyFunction::PoWRewardV1 as u8];
586 debris.params.encode(&mut data)?;
587 let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
588 let mut tx_builder =
589 TransactionBuilder::new(ContractCallLeaf { call, proofs: debris.proofs }, vec![])?;
590 let mut tx = tx_builder.build()?;
591 let sigs = tx.create_sigs(&[block_signing_keypair.secret])?;
592 tx.signatures = vec![sigs];
593
594 Ok(tx)
595}