Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Braiins/cross testing #1396

Merged
merged 2 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions protocols/v2/roles-logic-sv2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ nohash-hasher = "0.2.0"
siphasher = "1"

[dev-dependencies]
codec_sv2 = { path = "../../../protocols/v2/codec-sv2" }
quickcheck = "1.0.3"
quickcheck_macros = "1"
rand = "0.8.5"
Expand Down
104 changes: 104 additions & 0 deletions protocols/v2/roles-logic-sv2/src/parsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1295,3 +1295,107 @@ impl<'a> TryFrom<PoolMessages<'a>> for MiningDeviceMessages<'a> {
}
}
}

#[cfg(test)]
mod test {
use crate::{
mining_sv2::NewMiningJob,
parsers::{Mining, PoolMessages},
};
use binary_sv2::{Sv2Option, U256};
use codec_sv2::StandardSv2Frame;
use std::convert::{TryFrom, TryInto};

pub type Message = PoolMessages<'static>;
pub type StdFrame = StandardSv2Frame<Message>;

#[test]
fn new_mining_job_serialization() {
const CORRECTLY_SERIALIZED_MSG: &'static [u8] = &[
0, 128, 21, 49, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 1, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
];
let mining_message = PoolMessages::Mining(Mining::NewMiningJob(NewMiningJob {
channel_id: u32::from_le_bytes([1, 2, 3, 4]),
job_id: u32::from_le_bytes([5, 6, 7, 8]),
min_ntime: Sv2Option::new(Some(u32::from_le_bytes([9, 10, 11, 12]))),
version: u32::from_le_bytes([13, 14, 15, 16]),
merkle_root: U256::try_from((17_u8..(17 + 32)).collect::<Vec<u8>>()).unwrap(),
}));
message_serialization_check(mining_message, CORRECTLY_SERIALIZED_MSG);
}

fn message_serialization_check(message: PoolMessages<'static>, expected_result: &[u8]) {
let frame = StdFrame::try_from(message).unwrap();
let encoded_frame_length = frame.encoded_length();

let mut buffer = [0; 0xffff];
frame.serialize(&mut buffer).unwrap();
check_length_consistency(&buffer[..encoded_frame_length]);
check_length_consistency(&expected_result);
assert_eq!(
is_channel_msg(&buffer),
is_channel_msg(&expected_result),
"Unexpected channel_message flag",
);
assert_eq!(
extract_extension_type(&buffer),
extract_extension_type(&expected_result),
"Unexpected extension type",
);
assert_eq!(
extract_message_type(&buffer),
extract_message_type(&expected_result),
"Unexpected message type",
);
assert_eq!(
extract_payload_length(&buffer),
extract_payload_length(&expected_result),
"Unexpected message length",
);
assert_eq!(
encoded_frame_length as u32,
expected_result.len() as u32,
"Method encoded_length() returned different length than the actual message length",
);
assert_eq!(
extract_payload(&buffer[..encoded_frame_length]),
extract_payload(&expected_result),
"Unexpected payload",
)
}

fn is_channel_msg(serialized_frame: &[u8]) -> bool {
let array_repre = serialized_frame[..2].try_into().unwrap();
let decoded_extension_type = u16::from_le_bytes(array_repre);
(decoded_extension_type & (1 << 15)) != 0
}
fn extract_extension_type(serialized_frame: &[u8]) -> u16 {
let array_repre = serialized_frame[..2].try_into().unwrap();
let decoded_extension_type = u16::from_le_bytes(array_repre);
decoded_extension_type & (u16::MAX >> 1)
}
fn extract_message_type(serialized_frame: &[u8]) -> u8 {
serialized_frame[2]
}
fn extract_payload_length(serialized_frame: &[u8]) -> u32 {
let mut array_repre = [0; 4];
array_repre[..3].copy_from_slice(&serialized_frame[3..6]);
u32::from_le_bytes(array_repre)
}
fn extract_payload(serialized_frame: &[u8]) -> &[u8] {
assert!(serialized_frame.len() > 6);
&serialized_frame[6..]
}

fn check_length_consistency(serialized_frame: &[u8]) {
let message_length = extract_payload_length(serialized_frame) as usize;
let payload_length = extract_payload(serialized_frame).len();
assert_eq!(
message_length, payload_length,
"Header declared length [{} bytes] differs from the actual payload length [{} bytes]",
message_length, payload_length,
);
}
}
78 changes: 78 additions & 0 deletions protocols/v2/roles-logic-sv2/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,84 @@ fn test_group_id_new_into_group_id() {
assert!(channel_id == channel_from_complete);
}

#[test]
fn test_merkle_root_independent_vector() {
const REFERENCE_MERKLE_ROOT: [u8; 32] = [
28, 204, 213, 73, 250, 160, 146, 15, 5, 127, 9, 214, 204, 20, 164, 199, 20, 181, 26, 190,
236, 91, 40, 225, 128, 239, 213, 148, 232, 77, 4, 36,
];
const BRANCH: &[[u8; 32]] = &[
[
224, 195, 140, 86, 17, 172, 9, 61, 54, 73, 215, 202, 109, 83, 124, 163, 215, 78, 143,
204, 44, 242, 242, 122, 37, 106, 55, 81, 58, 234, 27, 210,
],
[
35, 10, 232, 246, 235, 117, 56, 190, 87, 77, 81, 11, 159, 79, 90, 62, 91, 52, 41, 49,
57, 245, 219, 122, 115, 223, 199, 229, 238, 60, 47, 144,
],
[
95, 18, 132, 87, 213, 76, 188, 74, 245, 106, 18, 149, 106, 32, 209, 158, 239, 3, 17,
26, 207, 230, 118, 149, 120, 48, 96, 66, 214, 150, 137, 220,
],
[
205, 167, 106, 179, 82, 50, 157, 76, 91, 36, 54, 226, 34, 183, 162, 179, 109, 64, 185,
207, 103, 192, 63, 31, 141, 126, 34, 30, 68, 69, 154, 176,
],
[
251, 236, 76, 1, 218, 98, 98, 236, 144, 52, 151, 246, 95, 13, 109, 240, 240, 195, 64,
157, 7, 142, 28, 242, 29, 123, 51, 93, 51, 36, 143, 148,
],
[
35, 146, 105, 130, 188, 39, 97, 252, 75, 229, 185, 148, 242, 106, 164, 112, 123, 66,
34, 95, 218, 203, 50, 203, 129, 208, 109, 220, 112, 228, 121, 160,
],
[
44, 55, 125, 47, 249, 213, 175, 143, 140, 50, 219, 72, 111, 71, 125, 54, 85, 70, 4, 85,
60, 92, 208, 35, 113, 245, 128, 139, 228, 4, 230, 177,
],
[
169, 119, 48, 178, 205, 188, 19, 220, 85, 29, 174, 45, 158, 172, 222, 238, 170, 144,
79, 140, 56, 90, 105, 187, 204, 145, 241, 96, 75, 88, 6, 133,
],
[
72, 202, 11, 90, 167, 140, 253, 12, 58, 85, 223, 17, 82, 112, 24, 129, 186, 39, 224,
171, 227, 192, 14, 167, 154, 248, 150, 55, 114, 169, 43, 17,
],
];
const CB_PREFIX: &[u8] = &[
1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 75, 3, 139, 133, 11, 250, 190, 109, 109, 43, 220,
215, 96, 154, 211, 18, 14, 53, 53, 0, 95, 132, 159, 127, 54, 197, 70, 135, 74, 17, 149, 12,
104, 133, 16, 182, 152, 109, 207, 13, 9, 1, 0, 0, 0, 0, 0, 0, 0,
];
const CB_SUFFIX: &[u8] = &[
89, 236, 29, 54, 20, 47, 115, 108, 117, 115, 104, 47, 0, 0, 0, 0, 3, 236, 42, 86, 37, 0, 0,
0, 0, 25, 118, 169, 20, 124, 21, 78, 209, 220, 89, 96, 158, 61, 38, 171, 178, 223, 46, 163,
213, 135, 205, 140, 65, 136, 172, 0, 0, 0, 0, 0, 0, 0, 0, 44, 106, 76, 41, 82, 83, 75, 66,
76, 79, 67, 75, 58, 155, 83, 3, 23, 69, 4, 30, 18, 212, 34, 33, 76, 167, 101, 132, 91, 1,
127, 124, 85, 238, 57, 118, 135, 107, 35, 25, 33, 0, 71, 6, 88, 0, 0, 0, 0, 0, 0, 0, 0, 38,
106, 36, 170, 33, 169, 237, 123, 170, 130, 253, 191, 130, 150, 16, 0, 18, 157, 2, 231, 33,
177, 230, 137, 182, 134, 51, 32, 216, 181, 6, 73, 60, 103, 211, 194, 61, 77, 64, 0, 0, 0,
0,
];
const EXTRANONCE_PREFIX: &[u8] = &[41, 101, 8, 3, 39, 21, 251];
const EXTRANONCE: &[u8] = &[165, 6, 238, 7, 139, 252, 22, 7];

let full_extranonce = {
let mut xn = EXTRANONCE_PREFIX.to_vec();
xn.extend_from_slice(EXTRANONCE);
xn
};

let calculated_merkle_root =
merkle_root_from_path(CB_PREFIX, CB_SUFFIX, &full_extranonce, BRANCH)
.expect("Ultimate failure. Merkle root calculator returned None");
assert_eq!(
calculated_merkle_root, REFERENCE_MERKLE_ROOT,
"Merkle root does not match reference"
)
}

#[test]
fn test_merkle_root_from_path() {
let coinbase_bytes = vec![
Expand Down
6 changes: 3 additions & 3 deletions protocols/v2/subprotocols/mining/src/new_mining_job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use alloc::vec::Vec;
#[cfg(not(feature = "with_serde"))]
use binary_sv2::binary_codec_sv2;
use binary_sv2::{Deserialize, Seq0255, Serialize, Sv2Option, B032, B064K, U256};
use binary_sv2::{Deserialize, Seq0255, Serialize, Sv2Option, B064K, U256};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If B032 doesn't exist in the spec, maybe binary_sv2 shouldn't define it?

More rigorously, should it wiped from the codebase?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

B32 doesn't exist in the spec.

B0_32 does exist in the spec.

Data Type Byte Length Description
B0_32 1 + LENGTH Byte array with 8-bit length prefix L. Unsigned integer, followed by a sequence of L bytes. Allowed range of length is 0 to 32.

https://stratumprotocol.org/specification/03-Protocol-Overview/#31-data-types-mapping

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, it's still used for extranonce and extranonce_prefix.

Out of scope for this PR, but is it really worth having a separate B0_32 datatype instead of just using B0_255 and then having the spec limit the extranonce size to 31?

It would be less confusing, because extranonce_size is U16 which allows for much bigger values. (U8 would have made more sense, but not worth changing now).

Adding an explanation as to why that limit was picked would be good too.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reason behind the limit of 32 bytes was set because it's enough to use it in a custom way from the pool (e.g. setting the pool tag in the coinbase tx), while letting the rest of the space to the miner to use it as real extranonce space.

#[cfg(not(feature = "with_serde"))]
use core::convert::TryInto;

Expand Down Expand Up @@ -49,7 +49,7 @@ pub struct NewMiningJob<'decoder> {
///
/// Note that this field is fixed and cannot be modified by the downstream node.
#[cfg_attr(feature = "with_serde", serde(borrow))]
pub merkle_root: B032<'decoder>,
pub merkle_root: U256<'decoder>,
}

impl<'d> NewMiningJob<'d> {
Expand Down Expand Up @@ -215,7 +215,7 @@ mod tests {
job_id,
min_ntime: Sv2Option::new(min_ntime),
version,
merkle_root: B032::try_from(merkle_root.to_vec())
merkle_root: U256::try_from(merkle_root.to_vec())
.expect("NewMiningJob: failed to convert merkle_root to B032"),
};
let static_nmj = nmj.clone().as_static();
Expand Down
Loading