use crate::addresses::remove_used_address;
use crate::{GLOBAL_TEMPLATE, PAYOUT_ADDRESS, SENTENCES};
use crate::merkle::{ compute_root_from_branch, LAST_TXS, };
use crate::utils::{
build_coinbase_tx, bits_to_target, hash_meets_target, serialize_header_to_array,
};
use bitcoin::{Address, Network};
use std::str::FromStr;
use std::sync::Arc;
use bitcoin::hashes::Hash;
use fastrand;
pub struct MinedBlock {
pub header: bitcoin::block::Header,
pub transactions: Vec<Arc<bitcoin::Transaction>>,
}
pub fn pick_random_sentence() -> &'static str {
unsafe {
if (*SENTENCES).is_empty() {
"default extra nonce"
} else {
&(*SENTENCES)[fastrand::usize(..(*SENTENCES).len())]
}
}
}
pub async fn mine_loop() -> Result<Option<MinedBlock>, Box<dyn std::error::Error + Send + Sync>> {
let payout_address = {
let lock = PAYOUT_ADDRESS.read().await;
match &*lock {
Some(addr) => addr.clone(),
None => return Ok(None),
}
};
let template = {
let guard = GLOBAL_TEMPLATE.read().await;
match &*guard {
Some((t, _)) => Arc::clone(t),
None => return Ok(None),
}
};
let cached = {
let guard = LAST_TXS.read().await;
match &*guard {
Some(c) => Arc::clone(c),
None => return Ok(None),
}
};
let sentence = pick_random_sentence();
let address: Address = payout_address
.parse::<bitcoin::address::Address<bitcoin::address::NetworkUnchecked>>()?
.require_network(Network::Bitcoin)?;
let bits = u32::from_str_radix(&template.bits, 16)?;
let target = bits_to_target(bits);
let dummy_coinbase = build_coinbase_tx(
template.height as u32,
sentence.as_bytes(),
template.coinbasevalue,
&address,
None,
)?;
let witness_root = compute_root_from_branch(
dummy_coinbase.wtxid().to_byte_array(),
&cached.witness_branch,
0,
);
let mut txs = cached.txs.clone();
let coinbase_tx = build_coinbase_tx(
template.height as u32,
sentence.as_bytes(),
template.coinbasevalue,
&address,
Some(witness_root),
)?;
let coinbase_txid = coinbase_tx.txid().to_byte_array();
txs[0] = Arc::new(coinbase_tx);
let merkle_root = compute_root_from_branch(
coinbase_txid,
&cached.merkle_branch,
0,
);
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs() as u32;
let block_time = now.max(template.curtime as u32);
let num = fastrand::u32(..);
let header = bitcoin::block::Header {
version: bitcoin::block::Version::from_consensus(template.version),
prev_blockhash: bitcoin::BlockHash::from_str(&template.previousblockhash)?,
merkle_root: bitcoin::TxMerkleNode::from_slice(&merkle_root)?,
time: block_time,
bits: bitcoin::CompactTarget::from_consensus(bits),
nonce: num,
};
let header_bytes = serialize_header_to_array(&header);
if hash_meets_target(&header_bytes, &target) {
println!("✅ Block found!");
remove_used_address(&payout_address).await?;
return Ok(Some(MinedBlock {
header,
transactions: txs,
}));
}
Ok(None)
}