use std::cmp::max;
use std::sync::Arc;
use std::time::Instant;
use dusk_bytes::Serializable;
use dusk_core::signatures::bls as core_bls;
use node_data::hard_fork::bls_version_at;
use node_data::ledger::{Block, Fault, IterationsInfo, Seed, Slash, to_str};
use node_data::message::payload::Candidate;
use node_data::message::{BLOCK_HEADER_VERSION, Message, SignedStepMessage};
use node_data::{get_current_timestamp, ledger};
use tracing::{debug, info};
use crate::commons::RoundUpdate;
use crate::config::{MAX_BLOCK_SIZE, MAX_NUMBER_OF_FAULTS, MINIMUM_BLOCK_TIME};
use crate::merkle::merkle_root;
use crate::operations::{Operations, StateTransitionData};
pub struct Generator<T: Operations> {
executor: Arc<T>,
}
impl<T: Operations> Generator<T> {
pub fn new(executor: Arc<T>) -> Self {
Self { executor }
}
pub async fn generate_candidate_message(
&self,
ru: &RoundUpdate,
iteration: u8,
failed_iterations: IterationsInfo,
) -> Result<Message, crate::errors::OperationError> {
let candidate = self
.generate_block(ru, iteration, failed_iterations, &[])
.await?;
let mut candidate_msg = Candidate { candidate };
candidate_msg.sign(&ru.secret_key, ru.pubkey_bls.inner());
debug!(event = "Candidate signed", header = ?candidate_msg.candidate.header());
Ok(candidate_msg.into())
}
pub async fn generate_block(
&self,
ru: &RoundUpdate,
iteration: u8,
failed_iterations: IterationsInfo,
faults: &[Fault],
) -> Result<Block, crate::errors::OperationError> {
let start = Instant::now();
let seed_sig: [u8; 48] = core_bls::sign_multisig(
&ru.secret_key,
ru.pubkey_bls.inner(),
&ru.seed().inner()[..],
bls_version_at(ru.round),
)
.to_bytes();
let seed = Seed::from(seed_sig);
let faults = if faults.len() > MAX_NUMBER_OF_FAULTS {
&faults[..MAX_NUMBER_OF_FAULTS]
} else {
faults
};
let gas_limit = self.executor.get_block_gas_limit().await;
let slashes =
Slash::from_iterations_and_faults(&failed_iterations, faults)?;
let prev_block_hash = ru.hash();
let mut blk_header = ledger::Header {
version: BLOCK_HEADER_VERSION,
height: ru.round,
gas_limit,
prev_block_hash,
seed,
generator_bls_pubkey: *ru.pubkey_bls.bytes(),
prev_block_cert: *ru.att(),
iteration,
failed_iterations,
..Default::default()
};
let header_size = blk_header.size();
let mut faults_size = u32::SIZE;
let fault_digests: Vec<_> = faults
.iter()
.map(|f| {
faults_size += f.size();
f.digest()
})
.collect();
blk_header.faultroot = merkle_root(&fault_digests);
let max_txs_bytes = MAX_BLOCK_SIZE - header_size - faults_size;
let prev_blk_voters = ru.att_voters();
let transition_data = StateTransitionData {
round: ru.round,
generator: ru.pubkey_bls.clone(),
slashes,
cert_voters: prev_blk_voters.to_owned(),
max_txs_bytes,
prev_state_root: ru.state_root(),
};
let (executed_txs, transition_result) = self
.executor
.generate_state_transition(transition_data)
.await?;
blk_header.state_hash = transition_result.state_root;
blk_header.event_bloom = transition_result.event_bloom;
let tx_digests: Vec<_> =
executed_txs.iter().map(|t| t.inner.digest()).collect();
let txs: Vec<_> = executed_txs.into_iter().map(|t| t.inner).collect();
blk_header.txroot = merkle_root(&tx_digests[..]);
blk_header.timestamp = max(
ru.timestamp() + *MINIMUM_BLOCK_TIME,
get_current_timestamp(),
);
match Block::new(blk_header, txs, faults.to_vec()) {
Ok(blk) => {
info!(
event = "Block generated",
round = blk.header().height,
iter = blk.header().iteration,
prev_block = &to_str(&blk.header().prev_block_hash),
hash = &to_str(&blk.header().hash),
gas_limit = blk.header().gas_limit,
state_hash = &to_str(&blk.header().state_hash),
dur = format!("{:?}ms", start.elapsed().as_millis()),
);
Ok(blk)
}
Err(e) => Err(crate::errors::OperationError::BlockCreation(
format!("{e}",),
)),
}
}
}