use super::Job;
use crate::{
merkle_root::merkle_root_from_path,
outputs::deserialize_template_outputs,
server::jobs::{error::ExtendedJobError, standard::StandardJob, JobOrigin},
};
use binary_sv2::{Seq0255Owned, Sv2OptionOwned, U256Owned};
use bitcoin::transaction::TxOut;
use mining_sv2::{NewExtendedMiningJobOwned, NewMiningJobOwned, SetCustomMiningJobOwned};
use template_distribution_sv2::NewTemplateOwned;
#[derive(Debug, Clone)]
pub struct ExtendedJob {
origin: JobOrigin,
extranonce_prefix: Vec<u8>,
coinbase_outputs: Vec<TxOut>,
coinbase_tx_prefix_with_bip141: Vec<u8>,
coinbase_tx_suffix_with_bip141: Vec<u8>,
job_message: NewExtendedMiningJobOwned,
}
impl Job for ExtendedJob {
fn get_job_id(&self) -> u32 {
self.job_message.job_id
}
fn get_extranonce_prefix(&self) -> &[u8] {
&self.extranonce_prefix
}
fn activate(&mut self, min_ntime: u32) {
self.activate(min_ntime);
}
}
impl ExtendedJob {
pub fn from_template(
template: NewTemplateOwned,
extranonce_prefix: Vec<u8>,
additional_coinbase_outputs: Vec<TxOut>,
coinbase_tx_prefix: Vec<u8>,
coinbase_tx_suffix: Vec<u8>,
job_message: NewExtendedMiningJobOwned,
) -> Result<Self, ExtendedJobError> {
let template_coinbase_outputs = deserialize_template_outputs(
template.coinbase_tx_outputs.to_owned_bytes(),
template.coinbase_tx_outputs_count,
)
.map_err(|_| ExtendedJobError::FailedToDeserializeCoinbaseOutputs)?;
let mut coinbase_outputs = vec![];
coinbase_outputs.extend(additional_coinbase_outputs);
coinbase_outputs.extend(template_coinbase_outputs);
Ok(Self {
origin: JobOrigin::NewTemplate(template),
extranonce_prefix,
coinbase_outputs,
coinbase_tx_prefix_with_bip141: coinbase_tx_prefix,
coinbase_tx_suffix_with_bip141: coinbase_tx_suffix,
job_message,
})
}
pub fn from_custom_job(
custom_job: SetCustomMiningJobOwned,
extranonce_prefix: Vec<u8>,
coinbase_outputs: Vec<TxOut>,
coinbase_tx_prefix: Vec<u8>,
coinbase_tx_suffix: Vec<u8>,
job_message: NewExtendedMiningJobOwned,
) -> Self {
Self {
origin: JobOrigin::SetCustomMiningJob(custom_job),
extranonce_prefix,
coinbase_outputs,
coinbase_tx_prefix_with_bip141: coinbase_tx_prefix,
coinbase_tx_suffix_with_bip141: coinbase_tx_suffix,
job_message,
}
}
pub fn into_standard_job(
self,
channel_id: u32,
extranonce_prefix: Vec<u8>,
) -> Result<StandardJob, ExtendedJobError> {
let template = match self.get_origin() {
JobOrigin::NewTemplate(template) => template,
JobOrigin::SetCustomMiningJob(_) => {
return Err(ExtendedJobError::FailedToConvertToStandardJob);
}
};
let merkle_root = merkle_root_from_path(
&self.get_coinbase_tx_prefix_without_bip141(),
&self.get_coinbase_tx_suffix_without_bip141(),
&extranonce_prefix,
self.get_merkle_path().as_slice(),
)
.ok_or(ExtendedJobError::FailedToCalculateMerkleRoot)?
.into();
let standard_job_message = NewMiningJobOwned {
channel_id,
job_id: self.get_job_id(),
merkle_root,
version: self.get_version(),
min_ntime: self.job_message.min_ntime.clone(),
};
let standard_job = StandardJob::from_template(
template.clone(),
extranonce_prefix,
self.get_coinbase_outputs().to_vec(),
standard_job_message,
)
.map_err(|_| ExtendedJobError::FailedToConvertToStandardJob)?;
Ok(standard_job)
}
pub fn get_job_id(&self) -> u32 {
self.job_message.job_id
}
pub fn get_origin(&self) -> &JobOrigin {
&self.origin
}
pub fn get_coinbase_tx_prefix_without_bip141(&self) -> Vec<u8> {
self.job_message.coinbase_tx_prefix.to_owned_bytes()
}
pub fn get_coinbase_tx_suffix_without_bip141(&self) -> Vec<u8> {
self.job_message.coinbase_tx_suffix.to_owned_bytes()
}
pub fn set_extranonce_prefix(&mut self, extranonce_prefix: Vec<u8>) {
self.extranonce_prefix = extranonce_prefix;
}
pub fn get_extranonce_prefix(&self) -> &[u8] {
&self.extranonce_prefix
}
pub fn get_coinbase_outputs(&self) -> &[TxOut] {
&self.coinbase_outputs
}
pub fn get_job_message(&self) -> &NewExtendedMiningJobOwned {
&self.job_message
}
pub fn get_merkle_path(&self) -> &Seq0255Owned<U256Owned> {
&self.job_message.merkle_path
}
pub fn get_min_ntime(&self) -> Option<u32> {
self.job_message.min_ntime.as_ref().copied()
}
pub fn get_version(&self) -> u32 {
self.job_message.version
}
pub fn version_rolling_allowed(&self) -> bool {
self.job_message.version_rolling_allowed
}
pub fn get_coinbase_tx_prefix_with_bip141(&self) -> Vec<u8> {
self.coinbase_tx_prefix_with_bip141.clone()
}
pub fn get_coinbase_tx_suffix_with_bip141(&self) -> Vec<u8> {
self.coinbase_tx_suffix_with_bip141.clone()
}
pub fn is_future(&self) -> bool {
self.job_message.is_future()
}
pub fn activate(&mut self, min_ntime: u32) {
self.job_message.min_ntime = Sv2OptionOwned::new(Some(min_ntime));
}
}