Skip to content

Commit

Permalink
refactor(mine_loop): Avoid mutable variable for latest block
Browse files Browse the repository at this point in the history
Instead of using a mutable variable to inform the mine loop about the
latest (most canonical) block, read it from global state whenever
required. Also use block header instead of block in a few function
signatures.
  • Loading branch information
Sword-Smith committed Jan 6, 2025
1 parent 517bd19 commit 8fb1a1b
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 39 deletions.
11 changes: 3 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,9 @@ pub async fn initialize(cli_args: cli_args::Args) -> Result<i32> {
let miner_join_handle = tokio::task::Builder::new()
.name("miner")
.spawn(async move {
mine_loop::mine(
main_to_miner_rx,
miner_to_main_tx,
latest_block,
miner_state_lock,
)
.await
.expect("Error in mining task");
mine_loop::mine(main_to_miner_rx, miner_to_main_tx, miner_state_lock)
.await
.expect("Error in mining task");
})?;
task_join_handles.push(miner_join_handle);
info!("Started mining task");
Expand Down
3 changes: 1 addition & 2 deletions src/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,8 +678,7 @@ impl MainLoopHandler {
.await;

// Inform miner about new block.
self.main_to_miner_tx
.send(MainToMiner::NewBlock(Box::new(last_block)));
self.main_to_miner_tx.send(MainToMiner::NewBlock);
}
PeerTaskToMain::AddPeerMaxBlockHeight((
socket_addr,
Expand Down
53 changes: 32 additions & 21 deletions src/mine_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ async fn compose_block(
#[allow(clippy::too_many_arguments)]
async fn guess_nonce(
block: Block,
previous_block: Block,
previous_block_header: BlockHeader,
sender: oneshot::Sender<NewBlockFound>,
composer_utxos: Vec<ExpectedUtxo>,
sleepy_guessing: bool,
Expand All @@ -121,7 +121,7 @@ async fn guess_nonce(
tokio::task::spawn_blocking(move || {
guess_worker(
block,
previous_block,
previous_block_header,
sender,
composer_utxos,
sleepy_guessing,
Expand All @@ -146,20 +146,20 @@ fn precalculate_mast_leafs(block_template: &Block) -> (Digest, Digest) {

fn guess_worker(
mut block: Block,
previous_block: Block,
previous_block_header: BlockHeader,
sender: oneshot::Sender<NewBlockFound>,
composer_utxos: Vec<ExpectedUtxo>,
sleepy_guessing: bool,
target_block_interval: Option<Timestamp>,
) {
// This must match the rules in `[Block::has_proof_of_work]`.
let prev_difficulty = previous_block.header().difficulty;
let prev_difficulty = previous_block_header.difficulty;
let threshold = prev_difficulty.target();
let input_block_height = block.kernel.header.height;
info!(
"Mining on block with {} outputs and difficulty {}. Attempting to find block with height {} with digest less than target: {}",
block.body().transaction_kernel.outputs.len(),
previous_block.header().difficulty,
previous_block_header.difficulty,
block.header().height,
threshold
);
Expand All @@ -169,14 +169,13 @@ fn guess_worker(
//
// note: number of rayon threads can be set with env var RAYON_NUM_THREADS
// see: https://docs.rs/rayon/latest/rayon/fn.max_num_threads.html
let previous_block_header = previous_block.header();
let block_header_template = block.header().to_owned();
let (block_body_mast_hash_digest, appendix_digest) = precalculate_mast_leafs(&block);
let guess_result = rayon::iter::repeat(0)
.map_init(
|| {
(
previous_block_header,
&previous_block_header,
block_header_template.clone(),
rand::thread_rng(),
)
Expand Down Expand Up @@ -592,7 +591,6 @@ pub(crate) async fn create_block_transaction(
pub(crate) async fn mine(
mut from_main: mpsc::Receiver<MainToMiner>,
to_main: mpsc::Sender<MinerToMain>,
mut latest_block: Block,
mut global_state_lock: GlobalStateLock,
) -> Result<()> {
// Wait before starting mining task to ensure that peers have sent us information about
Expand Down Expand Up @@ -632,13 +630,17 @@ pub(crate) async fn mine(

// safe because above `is_some`
let proposal = maybe_proposal.unwrap();

global_state_lock
.set_mining_status_to_guessing(proposal)
.await;

let latest_block_header = global_state_lock
.lock(|s| s.chain.light_state().header().to_owned())
.await;
let guesser_task = guess_nonce(
proposal.to_owned(),
latest_block.clone(),
latest_block_header,
guesser_tx,
composer_utxos,
cli_args.sleepy_guessing,
Expand All @@ -664,8 +666,12 @@ pub(crate) async fn mine(
&& is_connected
{
global_state_lock.set_mining_status_to_composing().await;

let latest_block = global_state_lock
.lock(|s| s.chain.light_state().to_owned())
.await;
let compose_task = compose_block(
latest_block.clone(),
latest_block,
global_state_lock.clone(),
composer_tx,
Timestamp::now(),
Expand Down Expand Up @@ -717,7 +723,7 @@ pub(crate) async fn mine(

break;
}
MainToMiner::NewBlock(block) => {
MainToMiner::NewBlock => {
if let Some(gt) = guesser_task {
gt.abort();
debug!("Abort-signal sent to guesser worker.");
Expand All @@ -727,8 +733,7 @@ pub(crate) async fn mine(
debug!("Abort-signal sent to composer worker.");
}

latest_block = *block;
info!("Miner task received {} block height {}", cli_args.network, latest_block.kernel.header.height);
info!("Miner task received notification about new block");
}
MainToMiner::NewBlockProposal => {
if let Some(gt) = guesser_task {
Expand Down Expand Up @@ -818,7 +823,10 @@ pub(crate) async fn mine(
// Sanity check, remove for more efficient mining.
// The below PoW check could fail due to race conditions. So we don't panic,
// we only ignore what the worker task sent us.
if !new_block_found.block.has_proof_of_work(&latest_block) {
let latest_block = global_state_lock
.lock(|s| s.chain.light_state().to_owned())
.await;
if !new_block_found.block.has_proof_of_work(latest_block.header()) {
error!("Own mined block did not have valid PoW Discarding.");
continue;
}
Expand All @@ -832,7 +840,6 @@ pub(crate) async fn mine(

info!("Found new {} block with block height {}. Hash: {}", global_state_lock.cli().network, new_block_found.block.kernel.header.height, new_block_found.block.hash());

latest_block = *new_block_found.block.to_owned();
to_main.send(MinerToMain::NewBlockFound(new_block_found)).await?;

wait_for_confirmation = true;
Expand Down Expand Up @@ -1317,7 +1324,7 @@ pub(crate) mod mine_loop_tests {

guess_worker(
block,
tip_block_orig.clone(),
tip_block_orig.header().to_owned(),
worker_task_tx,
coinbase_utxo_info,
sleepy_guessing,
Expand All @@ -1326,7 +1333,9 @@ pub(crate) mod mine_loop_tests {

let mined_block_info = worker_task_rx.await.unwrap();

assert!(mined_block_info.block.has_proof_of_work(&tip_block_orig));
assert!(mined_block_info
.block
.has_proof_of_work(tip_block_orig.header()));
}

/// This test mines a single block at height 1 on the main network
Expand Down Expand Up @@ -1389,7 +1398,7 @@ pub(crate) mod mine_loop_tests {

guess_worker(
template,
tip_block_orig.clone(),
tip_block_orig.header().to_owned(),
worker_task_tx,
coinbase_utxo_info,
sleepy_guessing,
Expand Down Expand Up @@ -1541,7 +1550,7 @@ pub(crate) mod mine_loop_tests {

guess_worker(
block,
prev_block.clone(),
prev_block.header().clone(),
worker_task_tx,
composer_utxos,
sleepy_guessing,
Expand All @@ -1555,7 +1564,9 @@ pub(crate) mod mine_loop_tests {
// which is the method we need here because it allows us to override
// default values for the target block interval and the minimum
// block interval.
assert!(mined_block_info.block.has_proof_of_work(&prev_block,));
assert!(mined_block_info
.block
.has_proof_of_work(prev_block.header()));

prev_block = *mined_block_info.block;

Expand Down Expand Up @@ -1732,7 +1743,7 @@ pub(crate) mod mine_loop_tests {
loop {
successor_block.set_header_nonce(rng.gen());

if successor_block.has_proof_of_work(&predecessor_block) {
if successor_block.has_proof_of_work(predecessor_block.header()) {
break;
}

Expand Down
8 changes: 4 additions & 4 deletions src/models/blockchain/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,22 +861,22 @@ impl Block {
/// `TARGET_BLOCK_INTERVAL` by a factor `ADVANCE_DIFFICULTY_CORRECTION_WAIT`
/// then the effective difficulty is reduced by a factor
/// `ADVANCE_DIFFICULTY_CORRECTION_FACTOR`.
pub fn has_proof_of_work(&self, previous_block: &Block) -> bool {
pub fn has_proof_of_work(&self, previous_block_header: &BlockHeader) -> bool {
let hash = self.hash();
let threshold = previous_block.kernel.header.difficulty.target();
let threshold = previous_block_header.difficulty.target();
if hash <= threshold {
return true;
}

let delta_t = self.header().timestamp - previous_block.header().timestamp;
let delta_t = self.header().timestamp - previous_block_header.timestamp;
let excess_multiple = usize::try_from(
delta_t.to_millis() / TARGET_BLOCK_INTERVAL.to_millis(),
)
.expect("excessive timestamp on incoming block should have been caught by peer loop");
let shift = usize::try_from(ADVANCE_DIFFICULTY_CORRECTION_FACTOR.ilog2()).unwrap()
* (excess_multiple
>> usize::try_from(ADVANCE_DIFFICULTY_CORRECTION_WAIT.ilog2()).unwrap());
let effective_difficulty = previous_block.header().difficulty >> shift;
let effective_difficulty = previous_block_header.difficulty >> shift;
if hash <= effective_difficulty.target() {
return true;
}
Expand Down
6 changes: 4 additions & 2 deletions src/models/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use super::state::wallet::monitored_utxo::MonitoredUtxo;

#[derive(Clone, Debug)]
pub(crate) enum MainToMiner {
NewBlock(Box<Block>),
/// Communicates that a new block is now considered canonical
NewBlock,

Shutdown,

/// Communicates to miner that it should work on a new block proposal
Expand All @@ -41,7 +43,7 @@ pub(crate) enum MainToMiner {
impl MainToMiner {
pub(crate) fn get_type(&self) -> &str {
match self {
MainToMiner::NewBlock(_) => "new block",
MainToMiner::NewBlock => "new block",
MainToMiner::Shutdown => "shutdown",
MainToMiner::NewBlockProposal => "new block proposal",
MainToMiner::WaitForContinue => "wait for continue",
Expand Down
2 changes: 1 addition & 1 deletion src/peer_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ impl PeerLoopHandler {
debug!("validating with respect to current timestamp {now}");
let mut previous_block = &parent_of_first_block;
for new_block in received_blocks.iter() {
let new_block_has_proof_of_work = new_block.has_proof_of_work(previous_block);
let new_block_has_proof_of_work = new_block.has_proof_of_work(previous_block.header());
debug!("new block has proof of work? {new_block_has_proof_of_work}");
let new_block_is_valid = new_block.is_valid(previous_block, now);
debug!("new block is valid? {new_block_is_valid}");
Expand Down
2 changes: 1 addition & 1 deletion src/tests/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ pub(crate) async fn valid_block_from_tx_for_tests(

let threshold = predecessor.header().difficulty.target();
let mut rng = StdRng::from_seed(seed);
while !block.has_proof_of_work(predecessor) {
while !block.has_proof_of_work(predecessor.header()) {
mine_iteration_for_tests(&mut block, threshold, &mut rng);
}

Expand Down

0 comments on commit 8fb1a1b

Please sign in to comment.