use alloc::vec::Vec;
use binary_sv2::{Deserialize, Seq0255, Serialize, Sv2Option, B064K, U256};
use core::{convert::TryInto, fmt};
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct NewMiningJob<'decoder> {
pub channel_id: u32,
pub job_id: u32,
pub min_ntime: Sv2Option<'decoder, u32>,
pub version: u32,
pub merkle_root: U256<'decoder>,
}
impl fmt::Display for NewMiningJob<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"NewMiningJob(channel_id: {}, job_id: {}, min_ntime: {}, version: 0x{:08x}, merkle_root: {})",
self.channel_id, self.job_id, self.min_ntime, self.version, self.merkle_root
)
}
}
impl NewMiningJob<'_> {
pub fn is_future(&self) -> bool {
self.min_ntime.clone().into_inner().is_none()
}
pub fn set_future(&mut self) {
self.min_ntime = Sv2Option::new(None);
}
pub fn set_no_future(&mut self, min_ntime: u32) {
self.min_ntime = Sv2Option::new(Some(min_ntime));
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct NewExtendedMiningJob<'decoder> {
pub channel_id: u32,
pub job_id: u32,
pub min_ntime: Sv2Option<'decoder, u32>,
pub version: u32,
pub version_rolling_allowed: bool,
pub merkle_path: Seq0255<'decoder, U256<'decoder>>,
pub coinbase_tx_prefix: B064K<'decoder>,
pub coinbase_tx_suffix: B064K<'decoder>,
}
impl fmt::Display for NewExtendedMiningJob<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"NewExtendedMiningJob(channel_id: {}, job_id: {}, min_ntime: {}, version: 0x{:08x}, version_rolling_allowed: {}, merkle_path: {}, coinbase_tx_prefix: {}, coinbase_tx_suffix: {})",
self.channel_id,
self.job_id,
self.min_ntime,
self.version,
self.version_rolling_allowed,
self.merkle_path,
self.coinbase_tx_prefix,
self.coinbase_tx_suffix
)
}
}
impl NewExtendedMiningJob<'_> {
pub fn is_future(&self) -> bool {
self.min_ntime.clone().into_inner().is_none()
}
pub fn set_future(&mut self) {
self.min_ntime = Sv2Option::new(None);
}
pub fn set_no_future(&mut self, min_ntime: u32) {
self.min_ntime = Sv2Option::new(Some(min_ntime));
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
use core::convert::TryFrom;
fn from_arbitrary_vec_to_array(vec: Vec<u8>) -> [u8; 32] {
let mut result = [0_u8; 32];
let start = 32_usize.saturating_sub(vec.len());
let copy_len = vec.len().min(32);
result[start..start + copy_len].copy_from_slice(&vec[..copy_len]);
result
}
#[quickcheck_macros::quickcheck]
#[allow(clippy::too_many_arguments)]
fn test_new_extended_mining_job(
channel_id: u32,
job_id: u32,
min_ntime: Option<u32>,
version: u32,
version_rolling_allowed: bool,
merkle_path: Vec<u8>,
coinbase_tx_prefix: Vec<u8>,
coinbase_tx_suffix: Vec<u8>,
) -> bool {
let merkle_path = helpers::scan_to_u256_sequence(&merkle_path);
let coinbase_tx_prefix = helpers::bytes_to_b064k(&coinbase_tx_prefix);
let coinbase_tx_suffix = helpers::bytes_to_b064k(&coinbase_tx_suffix);
let nemj = NewExtendedMiningJob {
channel_id,
job_id,
min_ntime: Sv2Option::new(min_ntime),
version,
version_rolling_allowed,
merkle_path: merkle_path.clone(),
coinbase_tx_prefix: coinbase_tx_prefix.clone(),
coinbase_tx_suffix: coinbase_tx_suffix.clone(),
};
let static_nmj = nemj.as_static();
static_nmj.channel_id == nemj.channel_id
&& static_nmj.job_id == nemj.job_id
&& static_nmj.min_ntime == nemj.min_ntime
&& static_nmj.version == nemj.version
&& static_nmj.version_rolling_allowed == nemj.version_rolling_allowed
&& static_nmj.merkle_path == merkle_path
&& static_nmj.coinbase_tx_prefix == coinbase_tx_prefix
&& static_nmj.coinbase_tx_suffix == coinbase_tx_suffix
}
#[quickcheck_macros::quickcheck]
fn test_new_mining_job(
channel_id: u32,
job_id: u32,
min_ntime: Option<u32>,
version: u32,
merkle_root: Vec<u8>,
) -> bool {
let merkle_root = from_arbitrary_vec_to_array(merkle_root);
let nmj = NewMiningJob {
channel_id,
job_id,
min_ntime: Sv2Option::new(min_ntime),
version,
merkle_root: U256::from(merkle_root),
};
let static_nmj = nmj.clone().as_static();
static_nmj.channel_id == nmj.channel_id
&& static_nmj.job_id == nmj.job_id
&& static_nmj.min_ntime == nmj.min_ntime
&& static_nmj.version == nmj.version
&& static_nmj.merkle_root == nmj.merkle_root
}
pub mod helpers {
use super::*;
use alloc::borrow::ToOwned;
pub fn scan_to_u256_sequence(bytes: &[u8]) -> Seq0255<U256> {
let inner: Vec<U256> = bytes
.chunks(32)
.map(|chunk| {
let data = from_arbitrary_vec_to_array(chunk.to_vec());
U256::from(data)
})
.collect();
Seq0255::new(inner).expect("Could not convert bytes to SEQ0255<U256")
}
pub fn bytes_to_b064k(bytes: &[u8]) -> B064K {
B064K::try_from(bytes.to_owned()).expect("Failed to convert to B064K")
}
}
}