From e8e8a148577250c2c21a18177ba584fdeddc84f3 Mon Sep 17 00:00:00 2001 From: Jakub Trnka Date: Thu, 23 Jan 2025 19:20:00 +0100 Subject: [PATCH 1/2] Add independent test-vector for merkle root calculation --- protocols/v2/roles-logic-sv2/src/utils.rs | 78 +++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/protocols/v2/roles-logic-sv2/src/utils.rs b/protocols/v2/roles-logic-sv2/src/utils.rs index 4fee4732d2..62d1a59fb0 100644 --- a/protocols/v2/roles-logic-sv2/src/utils.rs +++ b/protocols/v2/roles-logic-sv2/src/utils.rs @@ -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![ From f54512ae1ebfff0485ac3bfd7c00e3bff203008f Mon Sep 17 00:00:00 2001 From: Jakub Trnka Date: Fri, 24 Jan 2025 16:57:56 +0100 Subject: [PATCH 2/2] Fix NewMiningJob message format - Fix unit test new_mining_job_serialization - Add testcase for NewMiningJob serialization --- protocols/v2/roles-logic-sv2/Cargo.toml | 1 + protocols/v2/roles-logic-sv2/src/parsers.rs | 104 ++++++++++++++++++ .../subprotocols/mining/src/new_mining_job.rs | 6 +- 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/Cargo.toml b/protocols/v2/roles-logic-sv2/Cargo.toml index 983439fa24..1b97cdbb14 100644 --- a/protocols/v2/roles-logic-sv2/Cargo.toml +++ b/protocols/v2/roles-logic-sv2/Cargo.toml @@ -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" diff --git a/protocols/v2/roles-logic-sv2/src/parsers.rs b/protocols/v2/roles-logic-sv2/src/parsers.rs index aac4022937..9e8876783a 100644 --- a/protocols/v2/roles-logic-sv2/src/parsers.rs +++ b/protocols/v2/roles-logic-sv2/src/parsers.rs @@ -1295,3 +1295,107 @@ impl<'a> TryFrom> 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; + + #[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::>()).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, + ); + } +} diff --git a/protocols/v2/subprotocols/mining/src/new_mining_job.rs b/protocols/v2/subprotocols/mining/src/new_mining_job.rs index efe315fe51..29fe02294b 100644 --- a/protocols/v2/subprotocols/mining/src/new_mining_job.rs +++ b/protocols/v2/subprotocols/mining/src/new_mining_job.rs @@ -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}; #[cfg(not(feature = "with_serde"))] use core::convert::TryInto; @@ -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> { @@ -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();