use alloc::{string::ToString, vec::Vec};
use binary_sv2::{self, Deserialize, Serialize, Str0255, U32AsRef, B032, U256};
use core::{convert::TryInto, fmt};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OpenStandardMiningChannel<'decoder> {
pub request_id: U32AsRef<'decoder>,
pub user_identity: Str0255<'decoder>,
pub nominal_hash_rate: f32,
pub max_target: U256<'decoder>,
}
impl fmt::Display for OpenStandardMiningChannel<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"OpenStandardMiningChannel(request_id: {}, user_identity: {}, nominal_hash_rate: {}, max_target: {})",
self.request_id,
self.user_identity.as_utf8_or_hex(),
self.nominal_hash_rate,
self.max_target
)
}
}
impl OpenStandardMiningChannel<'_> {
pub fn get_request_id_as_u32(&self) -> u32 {
(&self.request_id).into()
}
pub fn update_id(&mut self, new_id: u32) {
let bytes_new = new_id.to_le_bytes();
let bytes_old = self.request_id.inner_as_mut();
bytes_old[0] = bytes_new[0];
bytes_old[1] = bytes_new[1];
bytes_old[2] = bytes_new[2];
bytes_old[3] = bytes_new[3];
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct OpenStandardMiningChannelSuccess<'decoder> {
pub request_id: U32AsRef<'decoder>,
pub channel_id: u32,
pub target: U256<'decoder>,
pub extranonce_prefix: B032<'decoder>,
pub group_channel_id: u32,
}
impl fmt::Display for OpenStandardMiningChannelSuccess<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"OpenStandardMiningChannelSuccess(request_id: {}, channel_id: {}, target: {}, extranonce_prefix: {}, group_channel_id: {})",
self.request_id,
self.channel_id,
self.target,
self.extranonce_prefix,
self.group_channel_id
)
}
}
impl OpenStandardMiningChannelSuccess<'_> {
pub fn get_request_id_as_u32(&self) -> u32 {
(&self.request_id).into()
}
pub fn update_id(&mut self, new_id: u32) {
let bytes_new = new_id.to_le_bytes();
let bytes_old = self.request_id.inner_as_mut();
bytes_old[0] = bytes_new[0];
bytes_old[1] = bytes_new[1];
bytes_old[2] = bytes_new[2];
bytes_old[3] = bytes_new[3];
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct OpenExtendedMiningChannel<'decoder> {
pub request_id: u32,
pub user_identity: Str0255<'decoder>,
pub nominal_hash_rate: f32,
pub max_target: U256<'decoder>,
pub min_extranonce_size: u16,
}
impl fmt::Display for OpenExtendedMiningChannel<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"OpenExtendedMiningChannel(request_id: {}, user_identity: {}, nominal_hash_rate: {}, max_target: {}, min_extranonce_size: {})",
self.request_id,
self.user_identity.as_utf8_or_hex(),
self.nominal_hash_rate,
self.max_target,
self.min_extranonce_size
)
}
}
impl OpenExtendedMiningChannel<'_> {
pub fn get_request_id_as_u32(&self) -> u32 {
self.request_id
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct OpenExtendedMiningChannelSuccess<'decoder> {
pub request_id: u32,
pub channel_id: u32,
pub target: U256<'decoder>,
pub extranonce_size: u16,
pub extranonce_prefix: B032<'decoder>,
pub group_channel_id: u32,
}
impl fmt::Display for OpenExtendedMiningChannelSuccess<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"OpenExtendedMiningChannelSuccess(request_id: {}, channel_id: {}, target: {}, extranonce_size: {}, extranonce_prefix: {}, group_channel_id: {})",
self.request_id,
self.channel_id,
self.target,
self.extranonce_size,
self.extranonce_prefix,
self.group_channel_id,
)
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OpenMiningChannelError<'decoder> {
pub request_id: u32,
pub error_code: Str0255<'decoder>,
}
impl fmt::Display for OpenMiningChannelError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"OpenMiningChannelError(request_id: {}, error_code: {})",
self.request_id,
self.error_code.as_utf8_or_hex()
)
}
}
impl OpenMiningChannelError<'_> {
pub fn new_max_target_out_of_range(request_id: u32) -> Self {
Self {
request_id,
error_code: "max-target-out-of-range".to_string().try_into().unwrap(),
}
}
pub fn unsupported_extranonce_size(request_id: u32) -> Self {
Self {
request_id,
error_code: "unsupported-min-extranonce-size"
.to_string()
.try_into()
.unwrap(),
}
}
pub fn new_unknown_user(request_id: u32) -> Self {
Self {
request_id,
error_code: "unknown-user".to_string().try_into().unwrap(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::{string::String, 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]
fn test_open_standard_mining_channel_fns(
request_id: u32,
user_identity: String,
nominal_hash_rate: f32,
max_target: Vec<u8>,
new_request_id: u32,
) -> bool {
let max_target: [u8; 32] = from_arbitrary_vec_to_array(max_target);
let mut osmc = OpenStandardMiningChannel {
request_id: U32AsRef::from(request_id),
user_identity: Str0255::try_from(user_identity.clone())
.expect("could not convert string to Str0255"),
nominal_hash_rate,
max_target: U256::from(max_target),
};
let test_request_id_1 = osmc.get_request_id_as_u32();
osmc.update_id(new_request_id);
let test_request_id_2 = osmc.get_request_id_as_u32();
request_id == test_request_id_1
&& new_request_id == test_request_id_2
&& helpers::compare_static_osmc(osmc)
}
#[quickcheck_macros::quickcheck]
fn test_open_standard_mining_channel_success(
request_id: u32,
channel_id: u32,
target: Vec<u8>,
extranonce_prefix: Vec<u8>,
group_channel_id: u32,
new_request_id: u32,
) -> bool {
let target = from_arbitrary_vec_to_array(target);
let extranonce_prefix = from_arbitrary_vec_to_array(extranonce_prefix);
let mut osmcs = OpenStandardMiningChannelSuccess {
request_id: U32AsRef::from(request_id),
channel_id,
target: U256::from(target),
extranonce_prefix: B032::try_from(extranonce_prefix.to_vec()).expect(
"OpenStandardMiningChannelSuccess: failed to convert extranonce_prefix to B032",
),
group_channel_id,
};
let test_request_id_1 = osmcs.get_request_id_as_u32();
osmcs.update_id(new_request_id);
let test_request_id_2 = osmcs.get_request_id_as_u32();
request_id == test_request_id_1 && new_request_id == test_request_id_2
}
#[quickcheck_macros::quickcheck]
fn test_extended_standard_mining_channel_fns(
request_id: u32,
user_identity: String,
nominal_hash_rate: f32,
max_target: Vec<u8>,
min_extranonce_size: u16,
) -> bool {
let max_target: [u8; 32] = from_arbitrary_vec_to_array(max_target);
let oemc = OpenExtendedMiningChannel {
request_id,
user_identity: Str0255::try_from(user_identity.clone())
.expect("could not convert string to Str0255"),
nominal_hash_rate,
max_target: U256::from(max_target),
min_extranonce_size,
};
let test_request_id_1 = oemc.get_request_id_as_u32();
request_id == test_request_id_1
}
mod helpers {
use super::*;
pub fn compare_static_osmc(osmc: OpenStandardMiningChannel) -> bool {
let static_osmc = OpenStandardMiningChannel::into_static(osmc.clone());
static_osmc.request_id == osmc.request_id
&& static_osmc.user_identity == osmc.user_identity
&& static_osmc.nominal_hash_rate.to_ne_bytes()
== osmc.nominal_hash_rate.to_ne_bytes()
&& static_osmc.max_target == osmc.max_target
}
}
#[test]
fn test() {}
}