From efa21d4ade8f87120b597a10d85044d0852be142 Mon Sep 17 00:00:00 2001 From: plebhash Date: Fri, 6 Jun 2025 15:10:44 -0300 Subject: [PATCH 001/338] implement channels::server::jobs::ExtendedJob::into_custom_job --- .../src/channels/server/jobs/error.rs | 8 +- .../src/channels/server/jobs/extended.rs | 105 ++++++++++++++++-- 2 files changed, 100 insertions(+), 13 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs index f99adfaf21..14bda6147b 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs @@ -1,6 +1,10 @@ pub enum ExtendedJobError { - CoinbaseOutputsSumOverflow, - InvalidCoinbaseOutputsSum, + FailedToDeserializeCoinbase, + CoinbaseInputCountMismatch, + FailedToSerializeCoinbaseOutputs, + FailedToSerializeCoinbasePrefix, + FutureJobNotAllowed, + InvalidMinNTime, } pub enum StandardJobError {} diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs index 8f7c1552b9..8242d96b95 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs @@ -1,10 +1,18 @@ use crate::{ - channels::server::jobs::JobOrigin, template_distribution_sv2::NewTemplate, + channels::{ + chain_tip::ChainTip, + server::jobs::{error::ExtendedJobError, JobOrigin}, + }, + template_distribution_sv2::NewTemplate, utils::deserialize_outputs, }; -use binary_sv2::{Seq0255, Sv2Option, B064K, U256}; -use mining_sv2::{NewExtendedMiningJob, SetCustomMiningJob}; -use stratum_common::bitcoin::transaction::TxOut; +use binary_sv2::{Seq0255, Sv2Option, B0255, B064K, U256}; +use mining_sv2::{NewExtendedMiningJob, SetCustomMiningJob, MAX_EXTRANONCE_LEN}; +use std::convert::TryInto; +use stratum_common::bitcoin::{ + consensus::{deserialize, serialize}, + transaction::{Transaction, TxOut}, +}; /// Abstraction of an extended mining job with: /// - the `NewTemplate` OR `SetCustomMiningJob` message that originated it @@ -60,13 +68,88 @@ impl<'a> ExtendedJob<'a> { /// Converts the `ExtendedJob` into a `SetCustomMiningJob` message. /// /// To be used by a Sv2 Job Declaration Client after: - /// - creating a non-future extended job from a non-future template - /// - activating a future extended job (that was created from a future template) - pub fn into_custom_job(self) -> SetCustomMiningJob<'a> { - // we need to wait for the outcome of the discussions around - // https://github.com/stratum-mining/sv2-spec/issues/133 - // before implementing this - todo!() + /// - a non-future `ExtendedJob` was created from a non-future `NewTemplate` + /// - a future `ExtendedJob` was activated into a non-future `ExtendedJob` + /// + /// In other words, a future `ExtendedJob` cannot be converted into a `SetCustomMiningJob`. + pub fn into_custom_job( + self, + request_id: u32, + token: B0255<'a>, + chain_tip: ChainTip, + ) -> Result, ExtendedJobError> { + let coinbase_tx_prefix = self.get_coinbase_tx_prefix().inner_as_ref(); + let coinbase_tx_suffix = self.get_coinbase_tx_suffix().inner_as_ref(); + + let mut serialized_coinbase = Vec::new(); + serialized_coinbase.extend(coinbase_tx_prefix); + serialized_coinbase.extend(vec![0; MAX_EXTRANONCE_LEN]); + serialized_coinbase.extend(coinbase_tx_suffix); + + let deserialized_coinbase: Transaction = deserialize(&serialized_coinbase) + .map_err(|_| ExtendedJobError::FailedToDeserializeCoinbase)?; + + if deserialized_coinbase.input.len() != 1 { + return Err(ExtendedJobError::CoinbaseInputCountMismatch); + } + + let min_ntime = if let Some(job_min_ntime) = self.job_message.min_ntime.clone().into_inner() + { + // job min_ntime must be coherent with the provided chain tip + // because chain_tip is where prev_hash and nbits are coming from + if job_min_ntime < chain_tip.min_ntime() { + return Err(ExtendedJobError::InvalidMinNTime); + } + + job_min_ntime + } else { + // future jobs are not allowed to be converted into `SetCustomMiningJob` messages + return Err(ExtendedJobError::FutureJobNotAllowed); + }; + + let prev_hash = chain_tip.prev_hash(); + let nbits = chain_tip.nbits(); + + let coinbase_prefix_start_index = 4 // tx version + + 2 // segwit bytes + + 1 // number of inputs + + 32 // prev OutPoint + + 4 // index + + 1; // bytes in script + let coinbase_prefix: B0255<'a> = coinbase_tx_prefix[coinbase_prefix_start_index..] + .to_vec() + .try_into() + .map_err(|_| ExtendedJobError::FailedToSerializeCoinbasePrefix)?; + let coinbase_tx_version = deserialized_coinbase.version.0 as u32; + let coinbase_tx_locktime = deserialized_coinbase.lock_time.to_consensus_u32(); + let coinbase_tx_input_n_sequence = deserialized_coinbase.input[0].sequence.0 as u32; + let coinbase_tx_value_remaining = 0; // this will be removed soon + + let mut serialized_outputs = Vec::new(); + for output in &deserialized_coinbase.output { + serialized_outputs.extend_from_slice(&serialize(output)); + } + + let coinbase_tx_outputs: B064K<'a> = serialized_outputs + .try_into() + .map_err(|_| ExtendedJobError::FailedToSerializeCoinbaseOutputs)?; + + Ok(SetCustomMiningJob { + channel_id: self.job_message.channel_id, + request_id, + token, + version: self.get_version(), + prev_hash, + min_ntime, + nbits, + coinbase_tx_version, + coinbase_prefix, + coinbase_tx_input_n_sequence, + coinbase_tx_value_remaining, + coinbase_tx_outputs, + coinbase_tx_locktime, + merkle_path: self.get_merkle_path().clone(), + }) } pub fn get_job_id(&self) -> u32 { From 4e2ae43584e406c98fa12f353af0dcef952d8c01 Mon Sep 17 00:00:00 2001 From: plebhash Date: Mon, 9 Jun 2025 18:50:01 -0300 Subject: [PATCH 002/338] stop using SetCustomMiningJob.coinbase_tx_value_remaining for channels::server::jobs::factory::ExtendedJobFactory::custom_coinbase this message field is marked for deprecation from here onwards, we simply assume SetCustomMiningJob.coinbase_tx_outputs already carry all outputs with their final amounts --- .../src/channels/server/jobs/factory.rs | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs index 8df9bc7959..23fc6dae43 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs @@ -231,26 +231,6 @@ impl JobFactory { let deserialized_outputs = deserialize_outputs(m.coinbase_tx_outputs.inner_as_ref().to_vec()); - // note: this is assuming there's only one output - // where all the sats are added to - // hopefully we will clean this once we get a clear outcome from - // https://github.com/stratum-mining/sv2-spec/issues/133 - let mut outputs_with_value_remaining = vec![]; - for output in deserialized_outputs.iter() { - if output.script_pubkey.is_p2pk() - || output.script_pubkey.is_p2pkh() - || output.script_pubkey.is_p2tr() - || output.script_pubkey.is_p2wpkh() - || output.script_pubkey.is_p2wsh() - { - let mut output_with_value_remaining = output.clone(); - output_with_value_remaining.value = Amount::from_sat(m.coinbase_tx_value_remaining); - outputs_with_value_remaining.push(output_with_value_remaining); - } else { - outputs_with_value_remaining.push(output.clone()); - } - } - let mut script_sig = vec![]; script_sig.extend_from_slice(m.coinbase_prefix.inner_as_ref()); script_sig.extend_from_slice(&[0; MAX_EXTRANONCE_LEN]); @@ -267,7 +247,7 @@ impl JobFactory { version: Version::non_standard(m.coinbase_tx_version as i32), lock_time: LockTime::from_consensus(m.coinbase_tx_locktime), input: vec![tx_in], - output: outputs_with_value_remaining, + output: deserialized_outputs, }) } From 304ad6bc05ec1e0018e034c321822ee82d56a64c Mon Sep 17 00:00:00 2001 From: plebhash Date: Mon, 9 Jun 2025 19:35:09 -0300 Subject: [PATCH 003/338] add Debug macro to ExtendedJobError --- protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs index 14bda6147b..d975898f18 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub enum ExtendedJobError { FailedToDeserializeCoinbase, CoinbaseInputCountMismatch, From 64d9747398f1b8f89b29a1abb6d06b16a0130619 Mon Sep 17 00:00:00 2001 From: plebhash Date: Mon, 9 Jun 2025 20:05:54 -0300 Subject: [PATCH 004/338] test_custom_job_creation_flow --- .../src/channels/server/extended.rs | 159 +++++++++++++++++- .../src/channels/server/jobs/mod.rs | 2 +- 2 files changed, 156 insertions(+), 5 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs index 136c2a6ddc..bcbdb336f4 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs @@ -600,6 +600,7 @@ mod tests { server::{ error::ExtendedChannelError, extended::ExtendedChannel, + jobs::JobOrigin, share_accounting::{ShareValidationError, ShareValidationResult}, }, }; @@ -877,10 +878,160 @@ mod tests { #[test] fn test_custom_job_creation_flow() { - // todo: assert that a SetCustomMiningJob leads to - // the correct NewExtendedMiningJob message - // we should wait until the following spec cleanup is finished - // https://github.com/stratum-mining/sv2-spec/issues/133 + // this extended channel lives on JDC + let jdc_channel_id = 1; + let user_identity = "user_identity".to_string(); + let extranonce_prefix = [ + 83, 116, 114, 97, 116, 117, 109, 32, 86, 50, 32, 83, 82, 73, 32, 80, 111, 111, 108, 0, + 0, 0, 0, 0, 0, 0, 1, + ] + .to_vec(); + let max_target = [0xff; 32].into(); + let expected_share_per_minute = 1.0; + let nominal_hashrate = 1.0; + let version_rolling_allowed = true; + let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; + let share_batch_size = 100; + + // this extended channel lives on JDC + let mut jdc_extended_channel = ExtendedChannel::new( + jdc_channel_id, + user_identity, + extranonce_prefix, + max_target, + nominal_hashrate, + version_rolling_allowed, + rollable_extranonce_size, + share_batch_size, + expected_share_per_minute, + ) + .unwrap(); + + let template = NewTemplate { + template_id: 1, + future_template: true, + version: 536870912, + coinbase_tx_version: 2, + coinbase_prefix: vec![82, 0].try_into().unwrap(), + coinbase_tx_input_sequence: 4294967295, + coinbase_tx_value_remaining: SATS_AVAILABLE_IN_TEMPLATE, + coinbase_tx_outputs_count: 1, + coinbase_tx_outputs: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 38, 106, 36, 170, 33, 169, 237, 226, 246, 28, 63, 113, 209, + 222, 253, 63, 169, 153, 223, 163, 105, 83, 117, 92, 105, 6, 137, 121, 153, 98, 180, + 139, 235, 216, 54, 151, 78, 140, 249, + ] + .try_into() + .unwrap(), + coinbase_tx_locktime: 0, + merkle_path: vec![].try_into().unwrap(), + }; + + // match the original script format used to generate the coinbase_reward_outputs for the + // expected job + let pubkey_hash = [ + 235, 225, 183, 220, 194, 147, 204, 170, 14, 231, 67, 168, 111, 137, 223, 130, 88, 194, + 8, 252, + ]; + let mut script_bytes = vec![0]; // SegWit version 0 + script_bytes.push(20); // Push 20 bytes (length of pubkey hash) + script_bytes.extend_from_slice(&pubkey_hash); + let script = ScriptBuf::from(script_bytes); + let coinbase_reward_outputs = vec![TxOut { + value: Amount::from_sat(SATS_AVAILABLE_IN_TEMPLATE), + script_pubkey: script, + }]; + + jdc_extended_channel + .on_new_template(template.clone(), coinbase_reward_outputs.clone()) + .unwrap(); + + let ntime = 1746839905; + let prev_hash = [ + 200, 53, 253, 129, 214, 31, 43, 84, 179, 58, 58, 76, 128, 213, 24, 53, 38, 144, 205, + 88, 172, 20, 251, 22, 217, 141, 21, 221, 21, 0, 0, 0, + ] + .into(); + let n_bits = 503543726; + let chain_tip = ChainTip::new(prev_hash, n_bits, ntime); + let set_new_prev_hash = SetNewPrevHash { + template_id: 1, + prev_hash: [ + 200, 53, 253, 129, 214, 31, 43, 84, 179, 58, 58, 76, 128, 213, 24, 53, 38, 144, + 205, 88, 172, 20, 251, 22, 217, 141, 21, 221, 21, 0, 0, 0, + ] + .into(), + header_timestamp: ntime, + n_bits: 503543726, + target: [ + 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, + 174, 119, 3, 0, 0, + ] + .into(), + }; + + jdc_extended_channel + .on_set_new_prev_hash(set_new_prev_hash) + .unwrap(); + + let jdc_active_job = jdc_extended_channel.get_active_job().unwrap().clone(); + + let mining_job_token = vec![0].try_into().unwrap(); + let set_custom_mining_job = jdc_active_job + .clone() + .into_custom_job(0, mining_job_token, chain_tip) + .unwrap(); + + // this extended channel lives on Pool Mining Server + let pool_channel_id = 1; + let user_identity = "user_identity".to_string(); + let extranonce_prefix = [ + 83, 116, 114, 97, 116, 117, 109, 32, 86, 50, 32, 83, 82, 73, 32, 80, 111, 111, 108, 0, + 0, 0, 0, 0, 0, 0, 1, + ] + .to_vec(); + let max_target = [0xff; 32].into(); + let expected_share_per_minute = 1.0; + let nominal_hashrate = 1.0; + let version_rolling_allowed = true; + let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; + let share_batch_size = 100; + + // this extended channel lives on Pool Mining Server + let mut pool_extended_channel = ExtendedChannel::new( + pool_channel_id, + user_identity, + extranonce_prefix.clone(), + max_target, + nominal_hashrate, + version_rolling_allowed, + rollable_extranonce_size, + share_batch_size, + expected_share_per_minute, + ) + .unwrap(); + + pool_extended_channel + .on_set_custom_mining_job(set_custom_mining_job.clone()) + .unwrap(); + + let pool_active_job = pool_extended_channel.get_active_job().unwrap().clone(); + assert_eq!( + pool_active_job.get_origin(), + &JobOrigin::SetCustomMiningJob(set_custom_mining_job) + ); + + assert_eq!( + pool_active_job.get_job_message(), + jdc_active_job.get_job_message() + ); + + assert_eq!(pool_active_job.get_extranonce_prefix(), &extranonce_prefix); + + assert_eq!( + pool_active_job.get_coinbase_outputs(), + jdc_active_job.get_coinbase_outputs() + ); } #[test] diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/mod.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/mod.rs index e980dbcdb6..dd00f9a6df 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/mod.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/mod.rs @@ -6,7 +6,7 @@ pub mod standard; use mining_sv2::SetCustomMiningJob; use template_distribution_sv2::NewTemplate; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum JobOrigin<'a> { NewTemplate(NewTemplate<'a>), SetCustomMiningJob(SetCustomMiningJob<'a>), From 5b86a7ad71c7923b57fd8b65d7d527be60de015c Mon Sep 17 00:00:00 2001 From: plebhash Date: Mon, 9 Jun 2025 20:32:43 -0300 Subject: [PATCH 005/338] test_new_custom_job --- .../src/channels/server/jobs/factory.rs | 71 +++++++++++++++++-- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs index 23fc6dae43..f7283531d8 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs @@ -498,9 +498,72 @@ mod tests { #[test] fn test_new_custom_job() { - // todo: assert that a SetCustomMiningJob leads to - // the correct NewExtendedMiningJob message - // we should wait until the following spec cleanup is finished - // https://github.com/stratum-mining/sv2-spec/issues/133 + let mut job_factory = JobFactory::new(true); + + let extranonce_prefix = [ + 83, 116, 114, 97, 116, 117, 109, 32, 86, 50, 32, 83, 82, 73, 32, 80, 111, 111, 108, 0, + 0, 0, 0, 0, 0, 0, 1, + ] + .to_vec(); + + let set_custom_mining_job = SetCustomMiningJob { + channel_id: 1, + request_id: 0, + token: vec![0].try_into().unwrap(), + version: 536870912, + prev_hash: [ + 200, 53, 253, 129, 214, 31, 43, 84, 179, 58, 58, 76, 128, 213, 24, 53, 38, 144, + 205, 88, 172, 20, 251, 22, 217, 141, 21, 221, 21, 0, 0, 0, + ] + .into(), + min_ntime: 1746839905, + nbits: 503543726, + coinbase_tx_version: 2, + coinbase_prefix: vec![82, 0].try_into().unwrap(), + coinbase_tx_input_n_sequence: 4294967295, + coinbase_tx_value_remaining: 0, + coinbase_tx_outputs: vec![ + 0, 242, 5, 42, 1, 0, 0, 0, 22, 0, 20, 235, 225, 183, 220, 194, 147, 204, 170, 14, + 231, 67, 168, 111, 137, 223, 130, 88, 194, 8, 252, 0, 0, 0, 0, 0, 0, 0, 0, 38, 106, + 36, 170, 33, 169, 237, 226, 246, 28, 63, 113, 209, 222, 253, 63, 169, 153, 223, + 163, 105, 83, 117, 92, 105, 6, 137, 121, 153, 98, 180, 139, 235, 216, 54, 151, 78, + 140, 249, + ] + .try_into() + .unwrap(), + coinbase_tx_locktime: 0, + merkle_path: vec![].try_into().unwrap(), + }; + + let expected_job = NewExtendedMiningJob { + channel_id: 1, + job_id: 1, + min_ntime: Sv2Option::new(Some(1746839905)), + version: 536870912, + version_rolling_allowed: true, + coinbase_tx_prefix: vec![ + 2, 0, 0, 0, 0, 1, 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, 34, 82, 0, + ] + .try_into() + .unwrap(), + coinbase_tx_suffix: vec![ + 255, 255, 255, 255, 2, 0, 242, 5, 42, 1, 0, 0, 0, 22, 0, 20, 235, 225, 183, 220, + 194, 147, 204, 170, 14, 231, 67, 168, 111, 137, 223, 130, 88, 194, 8, 252, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 106, 36, 170, 33, 169, 237, 226, 246, 28, 63, 113, 209, 222, + 253, 63, 169, 153, 223, 163, 105, 83, 117, 92, 105, 6, 137, 121, 153, 98, 180, 139, + 235, 216, 54, 151, 78, 140, 249, 1, 32, 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, 0, 0, 0, 0, + ] + .try_into() + .unwrap(), + merkle_path: vec![].try_into().unwrap(), + }; + + let job = job_factory + .new_custom_job(set_custom_mining_job, extranonce_prefix) + .unwrap(); + + assert_eq!(job.get_job_message(), &expected_job); } } From e34d50571b9cf050c5376917be5df4bb050c11e0 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 18 Jun 2025 18:17:29 +0530 Subject: [PATCH 006/338] **Update protocol crates to support `State` cloning** This commit introduces changes to the protocol crate to enable the `State` to be clonable. This is necessary to allow the `State`, once it transitions to the transport phase, to be used independently on the reader and writer sides of the stream without requiring synchronization primitives to share the state between them. This is valid because, in the Noise protocol, the encryptor and decryptor in the transport phase are mutually exclusive and do not need to coexist within the same state instance. Although a more thorough refactor could have been done to split the `noise_codec` into separate `noise_codec_encryptor` and `noise_codec_decryptor` components, we plan to revisit this as part of a future protocol refactoring. --- protocols/v2/codec-sv2/src/lib.rs | 4 ++-- protocols/v2/noise-sv2/src/cipher_state.rs | 2 ++ protocols/v2/noise-sv2/src/initiator.rs | 1 + protocols/v2/noise-sv2/src/lib.rs | 1 + protocols/v2/noise-sv2/src/responder.rs | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/protocols/v2/codec-sv2/src/lib.rs b/protocols/v2/codec-sv2/src/lib.rs index a4a1e995a1..b56816b89c 100644 --- a/protocols/v2/codec-sv2/src/lib.rs +++ b/protocols/v2/codec-sv2/src/lib.rs @@ -79,7 +79,7 @@ pub use framing_sv2::{self, framing::handshake_message_to_frame as h2f}; /// process accordingly. #[allow(clippy::large_enum_variant)] #[cfg(feature = "noise_sv2")] -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum HandshakeRole { /// The initiator role in the Noise handshake process. /// @@ -103,7 +103,7 @@ pub enum HandshakeRole { /// [`State::HandShake`] and finally to transport mode [`State::Transport`] as the encryption /// handshake is completed. #[cfg(feature = "noise_sv2")] -#[derive(Debug)] +#[derive(Debug, Clone)] #[allow(clippy::large_enum_variant)] pub enum State { /// The codec has not been initialized yet. diff --git a/protocols/v2/noise-sv2/src/cipher_state.rs b/protocols/v2/noise-sv2/src/cipher_state.rs index 09d75f39c2..be20884eb5 100644 --- a/protocols/v2/noise-sv2/src/cipher_state.rs +++ b/protocols/v2/noise-sv2/src/cipher_state.rs @@ -175,6 +175,7 @@ where // `GenericCipher` enables easy switching between ciphers while maintaining secure key and nonce // management. #[allow(clippy::large_enum_variant)] +#[derive(Clone)] pub enum GenericCipher { ChaCha20Poly1305(Cipher), #[allow(dead_code)] @@ -302,6 +303,7 @@ impl CipherState for GenericCipher { // It stores the optional encryption key, the nonce, and the optional cipher instance itself. The // [`CipherState`] trait is implemented to provide a consistent interface for managing cipher // state across different AEAD ciphers. +#[derive(Clone)] pub struct Cipher { // Optional 32-byte encryption key. k: Option<[u8; 32]>, diff --git a/protocols/v2/noise-sv2/src/initiator.rs b/protocols/v2/noise-sv2/src/initiator.rs index abaa2a03d7..ad198c0bd3 100644 --- a/protocols/v2/noise-sv2/src/initiator.rs +++ b/protocols/v2/noise-sv2/src/initiator.rs @@ -61,6 +61,7 @@ use secp256k1::{ /// exchanges, and maintains the handshake hash, chaining key, and nonce for message encryption. /// After the handshake, it facilitates secure communication using either [`ChaCha20Poly1305`] or /// `AES-GCM` ciphers. Sensitive data is securely erased when no longer needed. +#[derive(Clone)] pub struct Initiator { // Cipher used for encrypting and decrypting messages during the handshake. // diff --git a/protocols/v2/noise-sv2/src/lib.rs b/protocols/v2/noise-sv2/src/lib.rs index 5c9436ea43..5f32af4f50 100644 --- a/protocols/v2/noise-sv2/src/lib.rs +++ b/protocols/v2/noise-sv2/src/lib.rs @@ -105,6 +105,7 @@ const PARITY: secp256k1::Parity = secp256k1::Parity::Even; /// Manages the encryption and decryption of messages between two parties, the [`Initiator`] and /// [`Responder`], using the Noise protocol. A symmetric cipher is used for both encrypting /// outgoing messages and decrypting incoming messages. +#[derive(Clone)] pub struct NoiseCodec { // Cipher to encrypt outgoing messages. encryptor: GenericCipher, diff --git a/protocols/v2/noise-sv2/src/responder.rs b/protocols/v2/noise-sv2/src/responder.rs index 66b4519e03..53997e1cca 100644 --- a/protocols/v2/noise-sv2/src/responder.rs +++ b/protocols/v2/noise-sv2/src/responder.rs @@ -60,6 +60,7 @@ const VERSION: u16 = 0; /// a connection with the initiator. The responder manages key generation, Diffie-Hellman exchanges, /// message decryption, and state transitions, ensuring secure communication. Sensitive /// cryptographic material is securely erased when no longer needed. +#[derive(Clone)] pub struct Responder { // Cipher used for encrypting and decrypting messages during the handshake. // From 335ce50a708a14c43325cac4ca59ec9bc7caa7ea Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 18 Jun 2025 18:25:13 +0530 Subject: [PATCH 007/338] Restructure the `noise_connection` Architecture This commit simplifies the previously convoluted setup used to make the noise connection work. We had been facing lingering (ghost) tasks in our roles for quite some time, which originated within the network helpers, particularly in the noise connection logic. Previously, the handshake was performed in a somewhat blocking or compute-intensive manner, which could have contributed to the ghost task issue and made the flow difficult to reason about. In this revision, we introduce a much simpler and more intuitive flow: the handshake is now fully completed first, followed by spawning tasks for handling subsequent message processing for each role. This is a significant shift from the earlier structure, where tasks were spawned immediately and the handshake messages were exchanged later, making the sequence complex and prone to starvation. We also previously wrapped the state in synchronous primitives to share it across streams, which required locking even though encryption and decryption in the Noise protocol are completely independent operations. As noted in the previous commit, we now clone the state, allowing each stream to use its own dedicated encryptor or decryptor without interfering with the other. These were the key issues likely contributing to the ghost tasks. This commit simplifies the architecture and makes the entire flow much more intuitive and easier to maintain. --- .../network-helpers/src/noise_connection.rs | 283 ++++++++++-------- 1 file changed, 152 insertions(+), 131 deletions(-) diff --git a/roles/roles-utils/network-helpers/src/noise_connection.rs b/roles/roles-utils/network-helpers/src/noise_connection.rs index 40119dcaf2..3cc8813d6a 100644 --- a/roles/roles-utils/network-helpers/src/noise_connection.rs +++ b/roles/roles-utils/network-helpers/src/noise_connection.rs @@ -1,13 +1,18 @@ use crate::Error; use async_channel::{unbounded, Receiver, Sender}; use binary_sv2::{Deserialize, GetSize, Serialize}; -use codec_sv2::{HandshakeRole, StandardEitherFrame, StandardNoiseDecoder}; -use futures::lock::Mutex; -use std::sync::Arc; +use codec_sv2::{ + noise_sv2::{ELLSWIFT_ENCODING_SIZE, INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE}, + HandShakeFrame, HandshakeRole, StandardEitherFrame, StandardNoiseDecoder, State, +}; +use std::convert::TryInto; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, - net::TcpStream, - select, task, + net::{ + tcp::{OwnedReadHalf, OwnedWriteHalf}, + TcpStream, + }, + task, }; use tracing::{debug, error}; @@ -16,23 +21,34 @@ pub struct Connection { pub state: codec_sv2::State, } -impl crate::SetState for Connection { - async fn set_state(self_: Arc>, state: codec_sv2::State) { - loop { - if crate::HANDSHAKE_READY.load(std::sync::atomic::Ordering::SeqCst) { - if let Some(mut connection) = self_.try_lock() { - connection.state = state; - crate::TRANSPORT_READY.store(true, std::sync::atomic::Ordering::Relaxed); - break; - }; - } - task::yield_now().await; - } - } +async fn send_message<'a, Message: Serialize + Deserialize<'a> + GetSize + Send + 'static>( + writer: &mut OwnedWriteHalf, + msg: StandardEitherFrame, + state: &mut State, + encoder: &mut codec_sv2::NoiseEncoder, +) -> Result<(), Error> { + let buffer = encoder.encode(msg, state)?; + writer + .write_all(buffer.as_ref()) + .await + .map_err(|_| Error::SocketClosed)?; + Ok(()) +} + +async fn receive_message<'a, Message: Serialize + Deserialize<'a> + GetSize + Send + 'static>( + reader: &mut OwnedReadHalf, + state: &mut State, + decoder: &mut StandardNoiseDecoder, +) -> Result, Error> { + let writable = decoder.writable(); + reader + .read_exact(writable) + .await + .map_err(|_| Error::SocketClosed)?; + decoder.next_frame(state).map_err(Error::CodecError) } impl Connection { - #[allow(clippy::new_ret_no_self)] pub async fn new<'a, Message: Serialize + Deserialize<'a> + GetSize + Send + 'static>( stream: TcpStream, role: HandshakeRole, @@ -44,138 +60,143 @@ impl Connection { Error, > { let address = stream.peer_addr().map_err(|_| Error::SocketClosed)?; - let (mut reader, mut writer) = stream.into_split(); + let mut decoder = StandardNoiseDecoder::::new(); + let mut encoder = codec_sv2::NoiseEncoder::::new(); + let mut state = codec_sv2::State::initialized(role.clone()); - let (sender_incoming, receiver_incoming): ( - Sender>, - Receiver>, - ) = unbounded(); - let (sender_outgoing, receiver_outgoing): ( - Sender>, - Receiver>, - ) = unbounded(); - - let state = codec_sv2::State::not_initialized(&role); - - let connection = Arc::new(Mutex::new(Self { state })); - - let cloned1 = connection.clone(); - let cloned2 = connection.clone(); + // Handshake Phase + match role { + HandshakeRole::Initiator(_) => { + debug!("Initializing as downstream for {}", address); + let mut responder_state = codec_sv2::State::not_initialized(&role); + let first_msg = state.step_0()?; + send_message(&mut writer, first_msg.into(), &mut state, &mut encoder).await?; + debug!("First handshake message sent"); - task::spawn(async move { - select!( - _ = tokio::signal::ctrl_c() => { }, - _ = async { - let mut decoder = StandardNoiseDecoder::::new(); loop { - let writable = decoder.writable(); - match reader.read_exact(writable).await { - Ok(_) => { - let mut connection = cloned1.lock().await; - let decoded = decoder.next_frame(&mut connection.state); - drop(connection); - match decoded { - Ok(x) => { - if sender_incoming.send(x).await.is_err() { - error!("Shutting down noise stream reader!"); + match receive_message(&mut reader, &mut responder_state, &mut decoder).await { + Ok(second_msg) => { + debug!("Second handshake message received"); + let handshake_frame: HandShakeFrame = second_msg + .try_into() + .map_err(|_| Error::HandshakeRemoteInvalidMessage)?; + let payload: [u8; INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE] = + handshake_frame + .get_payload_when_handshaking() + .try_into() + .map_err(|_| Error::HandshakeRemoteInvalidMessage)?; + let transport_state = state.step_2(payload)?; + state = transport_state; break; - } + } + Err(Error::CodecError(codec_sv2::Error::MissingBytes(_))) => { + debug!("Waiting for more bytes during handshake"); } Err(e) => { - if let codec_sv2::Error::MissingBytes(_) = e { - } else { - error!("Shutting down noise stream reader! {:#?}", e); - sender_incoming.close(); - break; - } + error!("Handshake failed with upstream: {:?}", e); + return Err(e); } - } - } - Err(e) => { - error!( - "Disconnected from client while reading : {} - {}", - e, &address - ); - sender_incoming.close(); - break; } - } } - } => {} - ); - }); + } + HandshakeRole::Responder(_) => { + debug!("Initializing as upstream for {}", address); + let mut initiator_state = codec_sv2::State::not_initialized(&role); - let receiver_outgoing_cloned = receiver_outgoing.clone(); - task::spawn(async move { - select!( - _ = tokio::signal::ctrl_c() => { }, - _ = async { - let mut encoder = codec_sv2::NoiseEncoder::::new(); loop { - let received = receiver_outgoing_cloned.recv().await; + match receive_message(&mut reader, &mut initiator_state, &mut decoder).await { + Ok(first_msg) => { + debug!("First handshake message received"); + let handshake_frame: HandShakeFrame = first_msg + .try_into() + .map_err(|_| Error::HandshakeRemoteInvalidMessage)?; + let payload: [u8; ELLSWIFT_ENCODING_SIZE] = handshake_frame + .get_payload_when_handshaking() + .try_into() + .map_err(|_| Error::HandshakeRemoteInvalidMessage)?; + let (second_msg, transport_state) = state.step_1(payload)?; + send_message(&mut writer, second_msg.into(), &mut state, &mut encoder) + .await?; + debug!("Second handshake message sent"); + state = transport_state; + break; + } + Err(Error::CodecError(codec_sv2::Error::MissingBytes(_))) => { + debug!("Waiting for more bytes during handshake"); + } + Err(e) => { + error!("Handshake failed with downstream: {:?}", e); + return Err(e); + } + } + } + } + }; + + debug!("Handshake completed with state: {:?}", state); + + let (sender_incoming, receiver_incoming) = unbounded(); + let (sender_outgoing, receiver_outgoing) = unbounded(); - match received { + // Spawn Reader + let read_state = state.clone(); + Self::spawn_reader(reader, read_state, address, sender_incoming.clone()); + + // Spawn Writer + let write_state = state; + Self::spawn_writer(writer, write_state, address, receiver_outgoing.clone()); + + Ok((receiver_incoming, sender_outgoing)) + } + + fn spawn_reader<'a, Message: Serialize + Deserialize<'a> + GetSize + Send + 'static>( + mut reader: OwnedReadHalf, + mut reader_state: State, + address: std::net::SocketAddr, + sender_incoming: Sender>, + ) -> task::JoinHandle<()> { + task::spawn(async move { + let mut decoder = StandardNoiseDecoder::::new(); + loop { + match receive_message(&mut reader, &mut reader_state, &mut decoder).await { Ok(frame) => { - let mut connection = cloned2.lock().await; - let b = encoder.encode(frame, &mut connection.state).unwrap(); - drop(connection); - let b = b.as_ref(); - match (writer).write_all(b).await { - Ok(_) => (), - Err(e) => { - let _ = writer.shutdown().await; - // Just fail and force to reinitialize everything - error!( - "Disconnecting from client due to error writing: {} - {}", - e, &address - ); - task::yield_now().await; - break; + if sender_incoming.send(frame).await.is_err() { + error!("Shutting down reader for {}", address); + break; } - } + } + Err(Error::CodecError(codec_sv2::Error::MissingBytes(_))) => { + debug!("Waiting for more bytes while reading stream"); } Err(e) => { - // Just fail and force to reinitialize everything - let _ = writer.shutdown().await; - error!( - "Disconnecting from client due to error receiving: {} - {}", - e, &address - ); - task::yield_now().await; - break; + error!("Reader shutting down due to error: {:?}", e); + sender_incoming.close(); + break; } - }; - crate::HANDSHAKE_READY.store(true, std::sync::atomic::Ordering::Relaxed); } - } => {} - ); - }); - - // DO THE NOISE HANDSHAKE - match role { - HandshakeRole::Initiator(_) => { - debug!("Initializing as downstream for - {}", &address); - crate::initialize_as_downstream( - connection.clone(), - role, - sender_outgoing.clone(), - receiver_incoming.clone(), - ) - .await? } - HandshakeRole::Responder(_) => { - debug!("Initializing as upstream for - {}", &address); - crate::initialize_as_upstream( - connection.clone(), - role, - sender_outgoing.clone(), - receiver_incoming.clone(), - ) - .await? + }) + } + + fn spawn_writer<'a, Message: Serialize + Deserialize<'a> + GetSize + Send + 'static>( + mut writer: OwnedWriteHalf, + mut write_state: State, + address: std::net::SocketAddr, + receiver_outgoing: Receiver>, + ) -> task::JoinHandle<()> { + task::spawn(async move { + let mut encoder = codec_sv2::NoiseEncoder::::new(); + while let Ok(frame) = receiver_outgoing.recv().await { + if let Err(e) = + send_message(&mut writer, frame, &mut write_state, &mut encoder).await + { + error!("Error while writing to client {}: {:?}", address, e); + let _ = writer.shutdown().await; + break; + } } - }; - debug!("Noise handshake complete - {}", &address); - Ok((receiver_incoming, sender_outgoing)) + let _ = writer.shutdown().await; + }) } } From 97b17ac91a9ccaa37855ec8ce096aa72fa81c882 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 18 Jun 2025 18:38:43 +0530 Subject: [PATCH 008/338] remove all old connection architecture utilities --- roles/roles-utils/network-helpers/src/lib.rs | 91 +------------------- 1 file changed, 2 insertions(+), 89 deletions(-) diff --git a/roles/roles-utils/network-helpers/src/lib.rs b/roles/roles-utils/network-helpers/src/lib.rs index 6c377f954e..0447549872 100644 --- a/roles/roles-utils/network-helpers/src/lib.rs +++ b/roles/roles-utils/network-helpers/src/lib.rs @@ -1,19 +1,10 @@ -use binary_sv2::{Deserialize, GetSize, Serialize}; pub mod noise_connection; pub mod plain_connection; #[cfg(feature = "sv1")] pub mod sv1_connection; -use async_channel::{Receiver, RecvError, SendError, Sender}; -use codec_sv2::{ - noise_sv2::{ELLSWIFT_ENCODING_SIZE, INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE}, - Error as CodecError, HandShakeFrame, HandshakeRole, StandardEitherFrame, -}; -use futures::lock::Mutex; -use std::{ - convert::TryInto, - sync::{atomic::AtomicBool, Arc}, -}; +use async_channel::{RecvError, SendError}; +use codec_sv2::Error as CodecError; #[derive(Debug)] pub enum Error { @@ -41,81 +32,3 @@ impl From> for Error { Error::SendError } } - -trait SetState { - async fn set_state(self_: Arc>, state: codec_sv2::State); -} - -async fn initialize_as_downstream< - 'a, - Message: Serialize + Deserialize<'a> + GetSize, - T: SetState, ->( - self_: Arc>, - role: HandshakeRole, - sender_outgoing: Sender>, - receiver_incoming: Receiver>, -) -> Result<(), Error> { - let mut state = codec_sv2::State::initialized(role); - - // Create and send first handshake message - let first_message = state.step_0()?; - sender_outgoing.send(first_message.into()).await?; - - // Receive and deserialize second handshake message - let second_message = receiver_incoming.recv().await?; - let second_message: HandShakeFrame = second_message - .try_into() - .map_err(|_| Error::HandshakeRemoteInvalidMessage)?; - let second_message: [u8; INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE] = second_message - .get_payload_when_handshaking() - .try_into() - .map_err(|_| Error::HandshakeRemoteInvalidMessage)?; - - // Create and send thirth handshake message - let transport_mode = state.step_2(second_message)?; - - T::set_state(self_, transport_mode).await; - while !TRANSPORT_READY.load(std::sync::atomic::Ordering::SeqCst) { - std::hint::spin_loop() - } - Ok(()) -} - -async fn initialize_as_upstream<'a, Message: Serialize + Deserialize<'a> + GetSize, T: SetState>( - self_: Arc>, - role: HandshakeRole, - sender_outgoing: Sender>, - receiver_incoming: Receiver>, -) -> Result<(), Error> { - let mut state = codec_sv2::State::initialized(role); - - // Receive and deserialize first handshake message - let first_message: HandShakeFrame = receiver_incoming - .recv() - .await? - .try_into() - .map_err(|_| Error::HandshakeRemoteInvalidMessage)?; - let first_message: [u8; ELLSWIFT_ENCODING_SIZE] = first_message - .get_payload_when_handshaking() - .try_into() - .map_err(|_| Error::HandshakeRemoteInvalidMessage)?; - - // Create and send second handshake message - let (second_message, transport_mode) = state.step_1(first_message)?; - HANDSHAKE_READY.store(false, std::sync::atomic::Ordering::SeqCst); - sender_outgoing.send(second_message.into()).await?; - - // This sets the state to Handshake state - this prompts the task above to move the state - // to transport mode so that the next incoming message will be decoded correctly - // It is important to do this directly before sending the fourth message - T::set_state(self_, transport_mode).await; - while !TRANSPORT_READY.load(std::sync::atomic::Ordering::SeqCst) { - std::hint::spin_loop() - } - - Ok(()) -} - -static HANDSHAKE_READY: AtomicBool = AtomicBool::new(false); -static TRANSPORT_READY: AtomicBool = AtomicBool::new(false); From d1fce08786acc0a2e8c2c5b93c094ed5ad0fe143 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 18 Jun 2025 18:49:32 +0530 Subject: [PATCH 009/338] add new_ret_no_self to make new method use acceptable for non self types --- roles/roles-utils/network-helpers/src/noise_connection.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/roles-utils/network-helpers/src/noise_connection.rs b/roles/roles-utils/network-helpers/src/noise_connection.rs index 3cc8813d6a..04fce8d1fd 100644 --- a/roles/roles-utils/network-helpers/src/noise_connection.rs +++ b/roles/roles-utils/network-helpers/src/noise_connection.rs @@ -1,3 +1,4 @@ +#![allow(clippy::new_ret_no_self)] use crate::Error; use async_channel::{unbounded, Receiver, Sender}; use binary_sv2::{Deserialize, GetSize, Serialize}; From f3a83b5235d8225596b04b832f1bd2a825b1dab5 Mon Sep 17 00:00:00 2001 From: plebhash Date: Thu, 19 Jun 2025 20:24:33 -0300 Subject: [PATCH 010/338] add get_chain_tip to client channels --- protocols/v2/roles-logic-sv2/src/channels/client/extended.rs | 4 ++++ protocols/v2/roles-logic-sv2/src/channels/client/standard.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs index 48fbc827f3..c32b4ac9c3 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs @@ -110,6 +110,10 @@ impl<'a> ExtendedChannel<'a> { self.version_rolling } + pub fn get_chain_tip(&self) -> Option<&ChainTip> { + self.chain_tip.as_ref() + } + /// Sets the extranonce prefix. /// /// Note: after this, all new jobs will be associated with the new extranonce prefix. diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs index b23a561e87..1d04bac346 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs @@ -86,6 +86,10 @@ impl<'a> StandardChannel<'a> { &self.user_identity } + pub fn get_chain_tip(&self) -> Option<&ChainTip> { + self.chain_tip.as_ref() + } + pub fn set_extranonce_prefix( &mut self, extranonce_prefix: Vec, From cc50639d336602928b6f02519c0c754a48577c69 Mon Sep 17 00:00:00 2001 From: plebhash Date: Fri, 20 Jun 2025 13:01:04 -0300 Subject: [PATCH 011/338] add get_share_accounting to client channels --- protocols/v2/roles-logic-sv2/src/channels/client/extended.rs | 4 ++++ protocols/v2/roles-logic-sv2/src/channels/client/standard.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs index c32b4ac9c3..4e48c2d9c4 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs @@ -171,6 +171,10 @@ impl<'a> ExtendedChannel<'a> { &self.stale_jobs } + pub fn get_share_accounting(&self) -> &ShareAccounting { + &self.share_accounting + } + /// Called when a `NewExtendedMiningJob` message is received from upstream. pub fn on_new_extended_mining_job( &mut self, diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs index 1d04bac346..939730e80b 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs @@ -135,6 +135,10 @@ impl<'a> StandardChannel<'a> { &self.stale_jobs } + pub fn get_share_accounting(&self) -> &ShareAccounting { + &self.share_accounting + } + /// Called when the Group Channel receives a new extended job. /// /// Essentially converts the extended job into a standard job (with the current channel's From 860b36c3bca99347b2e93764c716a6c75dfcc01b Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 19 Jun 2025 12:52:33 -0400 Subject: [PATCH 012/338] update imports in `roles` and `tests` to reflect library reorganization --- benches/Cargo.toml | 2 +- common/Cargo.lock | 1336 ++++++++++++++++- common/Cargo.toml | 10 +- common/src/lib.rs | 7 +- examples/ping-pong-encrypted/Cargo.toml | 2 +- protocols/v2/codec-sv2/Cargo.toml | 1 - protocols/v2/codec-sv2/src/lib.rs | 2 + protocols/v2/framing-sv2/Cargo.toml | 1 - protocols/v2/noise-sv2/Cargo.toml | 1 - protocols/v2/roles-logic-sv2/Cargo.toml | 6 +- .../src/channel_logic/channel_factory.rs | 7 +- .../src/channel_logic/proxy_group_channel.rs | 2 +- .../roles-logic-sv2/src/channels/chain_tip.rs | 2 +- .../src/channels/client/extended.rs | 14 +- .../src/channels/client/share_accounting.rs | 2 +- .../src/channels/client/standard.rs | 14 +- .../src/channels/server/extended.rs | 11 +- .../src/channels/server/group.rs | 6 +- .../src/channels/server/jobs/extended.rs | 8 +- .../src/channels/server/jobs/factory.rs | 10 +- .../src/channels/server/jobs/standard.rs | 4 +- .../src/channels/server/share_accounting.rs | 2 +- .../src/channels/server/standard.rs | 11 +- protocols/v2/roles-logic-sv2/src/errors.rs | 4 +- .../v2/roles-logic-sv2/src/handlers/mining.rs | 1 + .../v2/roles-logic-sv2/src/job_creator.rs | 33 +- .../v2/roles-logic-sv2/src/job_dispatcher.rs | 6 +- protocols/v2/roles-logic-sv2/src/lib.rs | 2 + protocols/v2/roles-logic-sv2/src/parsers.rs | 19 +- protocols/v2/roles-logic-sv2/src/utils.rs | 26 +- .../subprotocols/common-messages/Cargo.toml | 1 - .../subprotocols/job-declaration/Cargo.toml | 1 - protocols/v2/subprotocols/mining/Cargo.toml | 1 - .../template-distribution/Cargo.toml | 1 - protocols/v2/sv2-ffi/Cargo.toml | 1 - roles/Cargo.lock | 109 +- roles/Cargo.toml | 3 +- roles/jd-client/Cargo.toml | 8 +- roles/jd-client/src/lib/config.rs | 2 +- roles/jd-client/src/lib/downstream.rs | 13 +- roles/jd-client/src/lib/error.rs | 6 +- .../src/lib/job_declarator/message_handler.rs | 5 +- roles/jd-client/src/lib/job_declarator/mod.rs | 32 +- .../lib/job_declarator/setup_connection.rs | 7 +- roles/jd-client/src/lib/mod.rs | 2 +- .../lib/template_receiver/message_handler.rs | 2 +- .../src/lib/template_receiver/mod.rs | 31 +- .../lib/template_receiver/setup_connection.rs | 7 +- roles/jd-client/src/lib/upstream_sv2/mod.rs | 6 +- .../src/lib/upstream_sv2/upstream.rs | 40 +- roles/jd-server/Cargo.toml | 7 +- roles/jd-server/src/lib/config.rs | 4 +- roles/jd-server/src/lib/error.rs | 6 +- .../src/lib/job_declarator/message_handler.rs | 18 +- roles/jd-server/src/lib/job_declarator/mod.rs | 37 +- roles/jd-server/src/lib/mempool/mod.rs | 7 +- roles/jd-server/src/lib/mod.rs | 7 +- roles/jd-server/src/lib/status.rs | 8 +- roles/mining-proxy/Cargo.toml | 6 +- .../mining-proxy/src/lib/downstream_mining.rs | 27 +- roles/mining-proxy/src/lib/error.rs | 3 +- roles/mining-proxy/src/lib/mod.rs | 2 +- roles/mining-proxy/src/lib/routing_logic.rs | 4 +- roles/mining-proxy/src/lib/selectors.rs | 4 +- roles/mining-proxy/src/lib/upstream_mining.rs | 41 +- roles/pool/Cargo.toml | 8 +- roles/pool/src/lib/error.rs | 6 +- .../src/lib/mining_pool/message_handler.rs | 14 +- roles/pool/src/lib/mining_pool/mod.rs | 49 +- .../src/lib/mining_pool/setup_connection.rs | 5 +- roles/pool/src/lib/status.rs | 2 +- .../lib/template_receiver/message_handler.rs | 4 +- roles/pool/src/lib/template_receiver/mod.rs | 21 +- .../lib/template_receiver/setup_connection.rs | 5 +- roles/roles-utils/network-helpers/Cargo.toml | 6 +- roles/roles-utils/network-helpers/src/lib.rs | 2 + .../network-helpers/src/noise_connection.rs | 2 +- .../network-helpers/src/plain_connection.rs | 5 +- roles/roles-utils/rpc/Cargo.toml | 2 +- roles/roles-utils/rpc/src/mini_rpc_client.rs | 4 +- roles/test-utils/mining-device-sv1/Cargo.toml | 3 +- .../mining-device-sv1/src/client.rs | 2 +- roles/test-utils/mining-device-sv1/src/job.rs | 1 + .../test-utils/mining-device-sv1/src/miner.rs | 2 +- roles/test-utils/mining-device/Cargo.toml | 8 +- roles/test-utils/mining-device/src/lib/mod.rs | 35 +- roles/translator/Cargo.toml | 7 +- .../src/lib/downstream_sv1/diff_management.rs | 13 +- .../src/lib/downstream_sv1/downstream.rs | 5 +- .../translator/src/lib/downstream_sv1/mod.rs | 2 +- roles/translator/src/lib/error.rs | 7 +- roles/translator/src/lib/mod.rs | 2 +- roles/translator/src/lib/proxy/bridge.rs | 14 +- .../src/lib/proxy/next_mining_notify.rs | 2 +- roles/translator/src/lib/status.rs | 2 + .../src/lib/upstream_sv2/diff_management.rs | 8 +- roles/translator/src/lib/upstream_sv2/mod.rs | 6 +- .../src/lib/upstream_sv2/upstream.rs | 45 +- test/integration-tests/Cargo.lock | 57 +- test/integration-tests/Cargo.toml | 8 +- test/integration-tests/lib/interceptor.rs | 2 +- .../lib/message_aggregator.rs | 2 +- test/integration-tests/lib/mock_roles.rs | 12 +- test/integration-tests/lib/mod.rs | 2 +- test/integration-tests/lib/sniffer.rs | 2 +- test/integration-tests/lib/sv1_sniffer.rs | 2 +- .../lib/template_provider.rs | 2 +- test/integration-tests/lib/types.rs | 3 +- test/integration-tests/lib/utils.rs | 33 +- .../integration-tests/tests/jd_integration.rs | 5 +- .../tests/jd_provide_missing_transaction.rs | 2 +- .../tests/jd_tproxy_integration.rs | 2 +- .../tests/jdc_block_propogation.rs | 2 +- test/integration-tests/tests/jdc_fallback.rs | 4 +- .../tests/jds_block_propogation.rs | 2 +- .../tests/pool_integration.rs | 2 +- .../tests/sniffer_integration.rs | 4 +- .../tests/sv2_mining_device.rs | 2 +- .../tests/translator_integration.rs | 2 +- utils/Cargo.lock | 491 +++++- utils/bip32-key-derivation/Cargo.toml | 3 +- utils/bip32-key-derivation/src/lib.rs | 2 +- utils/bip32-key-derivation/src/main.rs | 2 +- 123 files changed, 2285 insertions(+), 675 deletions(-) diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 7bc179f3fb..4cfd8f0161 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -4,7 +4,7 @@ version = "1.0.1" edition = "2021" [dependencies] -stratum-common = { path = "../common", features=["bitcoin"]} +bitcoin = { version = "0.32.5", optional = true } async-std={version = "1.10.0", features = ["attributes"]} criterion = "0.5.1" async-channel = "1.4.0" diff --git a/common/Cargo.lock b/common/Cargo.lock index 83e061fb92..8b9906f2ad 100644 --- a/common/Cargo.lock +++ b/common/Cargo.lock @@ -2,20 +2,102 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + [[package]] name = "base58ck" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" dependencies = [ - "bitcoin-internals", - "bitcoin_hashes", + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.0", ] [[package]] @@ -24,23 +106,44 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +[[package]] +name = "binary_codec_sv2" +version = "2.0.0" +dependencies = [ + "buffer_sv2", +] + +[[package]] +name = "binary_sv2" +version = "3.0.0" +dependencies = [ + "binary_codec_sv2", + "derive_codec_sv2", +] + [[package]] name = "bitcoin" -version = "0.32.5" +version = "0.32.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" +checksum = "ad8929a18b8e33ea6b3c09297b687baaa71fb1b97353243a3f1029fad5c59c5b" dependencies = [ "base58ck", "bech32", - "bitcoin-internals", + "bitcoin-internals 0.3.0", "bitcoin-io", "bitcoin-units", - "bitcoin_hashes", - "hex-conservative", + "bitcoin_hashes 0.14.0", + "hex-conservative 0.2.1", "hex_lit", "secp256k1 0.29.1", ] +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + [[package]] name = "bitcoin-internals" version = "0.3.0" @@ -59,7 +162,26 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" dependencies = [ - "bitcoin-internals", + "bitcoin-internals 0.3.0", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b7a2e9773ee7ae7f2560f0426c938f57902dcb9e39321b0cbd608f47ed579a4" +dependencies = [ + "byteorder", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals 0.2.0", + "hex-conservative 0.1.2", ] [[package]] @@ -69,137 +191,1215 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ "bitcoin-io", - "hex-conservative", + "hex-conservative 0.2.1", ] +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "buffer_sv2" +version = "2.0.0" +dependencies = [ + "aes-gcm", +] + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + [[package]] name = "cc" -version = "1.0.97" +version = "1.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] -name = "getrandom" -version = "0.2.15" +name = "chacha20" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "libc", - "wasi", + "cipher", + "cpufeatures", ] [[package]] -name = "hex-conservative" -version = "0.2.1" +name = "chacha20poly1305" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ - "arrayvec", + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", ] [[package]] -name = "hex_lit" -version = "0.1.1" +name = "cipher" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] [[package]] -name = "libc" -version = "0.2.154" +name = "codec_sv2" +version = "2.1.0" +dependencies = [ + "binary_sv2", + "buffer_sv2", + "framing_sv2", + "noise_sv2", + "rand", + "tracing", +] + +[[package]] +name = "common_messages_sv2" +version = "5.1.0" +dependencies = [ + "binary_sv2", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const_format" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] [[package]] -name = "ppv-lite86" -version = "0.2.17" +name = "const_format_proc_macros" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] [[package]] -name = "rand" -version = "0.8.5" +name = "cpufeatures" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", - "rand_chacha", - "rand_core", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "crossbeam-utils" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "ppv-lite86", + "generic-array", "rand_core", + "typenum", ] [[package]] -name = "rand_core" -version = "0.6.4" +name = "ctr" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "getrandom", + "cipher", ] [[package]] -name = "secp256k1" -version = "0.28.2" +name = "derive_codec_sv2" +version = "1.1.1" +dependencies = [ + "binary_codec_sv2", +] + +[[package]] +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ + "byteorder", "rand", - "secp256k1-sys 0.9.2", + "rustc-hex", + "static_assertions", ] [[package]] -name = "secp256k1" -version = "0.29.1" +name = "framing_sv2" +version = "5.1.0" +dependencies = [ + "binary_sv2", + "buffer_sv2", + "noise_sv2", +] + +[[package]] +name = "funty" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ - "bitcoin_hashes", - "secp256k1-sys 0.10.1", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] -name = "secp256k1-sys" -version = "0.9.2" +name = "futures-channel" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ - "cc", + "futures-core", + "futures-sink", ] [[package]] -name = "secp256k1-sys" -version = "0.10.1" +name = "futures-core" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ - "cc", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "stratum-common" -version = "2.0.0" +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ - "bitcoin", - "secp256k1 0.28.2", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" + +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex-conservative" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afe881d0527571892c4034822e59bb10c6c991cce6abe8199b6f5cf10766f55" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "impl-codec" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d40b9d5e17727407e55028eafc22b2dc68781786e6d7eb8a21103f5058e3a14" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "job_declaration_sv2" +version = "4.0.0" +dependencies = [ + "binary_sv2", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "mining_sv2" +version = "4.0.0" +dependencies = [ + "binary_sv2", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.59.0", +] + +[[package]] +name = "network_helpers_sv2" +version = "4.0.0" +dependencies = [ + "async-channel", + "codec_sv2", + "futures", + "serde_json", + "sv1_api", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "noise_sv2" +version = "1.4.0" +dependencies = [ + "aes-gcm", + "chacha20poly1305", + "rand", + "rand_chacha", + "secp256k1 0.28.2", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primitive-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +dependencies = [ + "bitflags", +] + +[[package]] +name = "roles_logic_sv2" +version = "3.0.0" +dependencies = [ + "bitcoin", + "chacha20poly1305", + "codec_sv2", + "common_messages_sv2", + "hex-conservative 0.3.0", + "job_declaration_sv2", + "mining_sv2", + "nohash-hasher", + "primitive-types", + "template_distribution_sv2", + "tracing", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "secp256k1" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +dependencies = [ + "bitcoin_hashes 0.13.0", + "rand", + "secp256k1-sys 0.9.2", +] + +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "bitcoin_hashes 0.13.0", + "secp256k1-sys 0.10.1", +] + +[[package]] +name = "secp256k1-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +dependencies = [ + "cc", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stratum-common" +version = "3.0.0" +dependencies = [ + "network_helpers_sv2", + "roles_logic_sv2", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "sv1_api" +version = "1.0.1" +dependencies = [ + "binary_sv2", + "bitcoin_hashes 0.3.2", + "byteorder", + "hex", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "syn" +version = "2.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6397daf94fa90f058bd0fd88429dd9e5738999cca8d701813c80723add80462" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "template_distribution_sv2" +version = "3.1.0" +dependencies = [ + "binary_sv2", +] + +[[package]] +name = "tokio" +version = "1.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/common/Cargo.toml b/common/Cargo.toml index abbac337c5..74fff4abea 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,11 +1,15 @@ [package] name = "stratum-common" -version = "2.0.0" +version = "3.0.0" edition = "2018" description = "SV2 pool role" license = "MIT OR Apache-2.0" repository = "https://github.com/stratum-mining/stratum" [dependencies] -bitcoin = {version="0.32.5",optional=true} -secp256k1 = { version = "0.28.2", default-features = false, features =["alloc","rand","rand-std"] } +roles_logic_sv2 = { path = "../protocols/v2/roles-logic-sv2" } +network_helpers_sv2 = { path = "../roles/roles-utils/network-helpers", features = ["with_buffer_pool"], optional = true } + +[features] +with_network_helpers = ["dep:network_helpers_sv2"] +sv1 = ["network_helpers_sv2/sv1"] diff --git a/common/src/lib.rs b/common/src/lib.rs index 242c7acb35..eca6226f3f 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -2,6 +2,7 @@ //! //! `stratum_common` is a utility crate designed to centralize //! and manage the shared dependencies and utils across stratum crates. -#[cfg(feature = "bitcoin")] -pub use bitcoin; -pub use secp256k1; + +#[cfg(feature = "with_network_helpers")] +pub use network_helpers_sv2; +pub use roles_logic_sv2; diff --git a/examples/ping-pong-encrypted/Cargo.toml b/examples/ping-pong-encrypted/Cargo.toml index 8b3099a018..cf9e99fe51 100644 --- a/examples/ping-pong-encrypted/Cargo.toml +++ b/examples/ping-pong-encrypted/Cargo.toml @@ -11,7 +11,7 @@ binary_sv2 = { path = "../../protocols/v2/binary-sv2" } codec_sv2 = { path = "../../protocols/v2/codec-sv2", features = [ "noise_sv2" ] } noise_sv2 = { path = "../../protocols/v2/noise-sv2" } key-utils = { version = "^1.0.0", path = "../../utils/key-utils" } -network_helpers_sv2 = { version = "3.0.0", path = "../../roles/roles-utils/network-helpers" } +network_helpers_sv2 = { version = "4.0.0", path = "../../roles/roles-utils/network-helpers" } rand = "0.8" tokio = { version = "1.44.1", features = [ "full" ] } async-channel = "1.5.1" diff --git a/protocols/v2/codec-sv2/Cargo.toml b/protocols/v2/codec-sv2/Cargo.toml index 5e5108fbb4..d67a25ce71 100644 --- a/protocols/v2/codec-sv2/Cargo.toml +++ b/protocols/v2/codec-sv2/Cargo.toml @@ -15,7 +15,6 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] framing_sv2 = { path = "../../../protocols/v2/framing-sv2", version = "^5.0.0" } noise_sv2 = { path = "../../../protocols/v2/noise-sv2", default-features = false, optional = true, version = "^1.0.0" } binary_sv2 = { path = "../../../protocols/v2/binary-sv2", version = "^3.0.0" } -stratum-common = { version = "2.0.0", path = "../../../common" } buffer_sv2 = { path = "../../../utils/buffer", version = "^2.0.0" } rand = { version = "0.8.5", default-features = false } tracing = { version = "0.1", optional = true } diff --git a/protocols/v2/codec-sv2/src/lib.rs b/protocols/v2/codec-sv2/src/lib.rs index b56816b89c..04ef3d4574 100644 --- a/protocols/v2/codec-sv2/src/lib.rs +++ b/protocols/v2/codec-sv2/src/lib.rs @@ -67,6 +67,8 @@ pub use noise_sv2::{self, Initiator, NoiseCodec, Responder}; pub use buffer_sv2; +pub use binary_sv2; + pub use framing_sv2::{self, framing::handshake_message_to_frame as h2f}; /// Represents the role in the Noise handshake process, either as an initiator or a responder. diff --git a/protocols/v2/framing-sv2/Cargo.toml b/protocols/v2/framing-sv2/Cargo.toml index c5af6f2cfe..479b798401 100644 --- a/protocols/v2/framing-sv2/Cargo.toml +++ b/protocols/v2/framing-sv2/Cargo.toml @@ -14,7 +14,6 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -stratum-common = { path = "../../../common", version = "^2.0.0" } binary_sv2 = { path = "../../../protocols/v2/binary-sv2", version = "^3.0.0" } buffer_sv2 = { path = "../../../utils/buffer", optional=true, version = "^2.0.0" } noise_sv2 = { path = "../../../protocols/v2/noise-sv2", version = "^1.0.0" } diff --git a/protocols/v2/noise-sv2/Cargo.toml b/protocols/v2/noise-sv2/Cargo.toml index eef8a96115..b0db3cbb9d 100644 --- a/protocols/v2/noise-sv2/Cargo.toml +++ b/protocols/v2/noise-sv2/Cargo.toml @@ -17,7 +17,6 @@ rand = {version = "0.8.5", default-features = false } aes-gcm = { version = "0.10.2", features = ["alloc", "aes"], default-features = false } chacha20poly1305 = { version = "0.10.1", default-features = false, features = ["alloc"]} rand_chacha = { version = "0.3.1", default-features = false } -stratum-common = { path = "../../../common", version = "^2.0.0"} [features] default = ["std"] diff --git a/protocols/v2/roles-logic-sv2/Cargo.toml b/protocols/v2/roles-logic-sv2/Cargo.toml index 450e1a1616..aa78357564 100644 --- a/protocols/v2/roles-logic-sv2/Cargo.toml +++ b/protocols/v2/roles-logic-sv2/Cargo.toml @@ -13,21 +13,19 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -stratum-common = { path = "../../../common", features=["bitcoin"], version = "^2.0.0"} -binary_sv2 = { path = "../../../protocols/v2/binary-sv2", version = "^3.0.0" } +bitcoin = { version = "0.32.5" } common_messages_sv2 = { path = "../../../protocols/v2/subprotocols/common-messages", version = "^5.0.0" } mining_sv2 = { path = "../../../protocols/v2/subprotocols/mining", version = "^4.0.0" } template_distribution_sv2 = { path = "../../../protocols/v2/subprotocols/template-distribution", version = "^3.0.0" } job_declaration_sv2 = { path = "../../../protocols/v2/subprotocols/job-declaration", version = "^4.0.0" } -framing_sv2 = { path = "../../../protocols/v2/framing-sv2", version = "^5.0.0" } tracing = { version = "0.1"} chacha20poly1305 = { version = "0.10.1"} nohash-hasher = "0.2.0" primitive-types = "0.13.1" hex = {package = "hex-conservative", version = "0.3.0"} +codec_sv2 = { path = "../../../protocols/v2/codec-sv2", features = ["noise_sv2", "with_buffer_pool"] } [dev-dependencies] -codec_sv2 = { path = "../../../protocols/v2/codec-sv2", version = "^2.0.0" } quickcheck = "1.0.3" quickcheck_macros = "1" rand = "0.8.5" diff --git a/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs b/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs index c99aef3757..2710f3095b 100644 --- a/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs +++ b/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs @@ -11,6 +11,7 @@ use crate::{ Error, }; +use codec_sv2::binary_sv2; use mining_sv2::{ ExtendedExtranonce, NewExtendedMiningJob, NewMiningJob, OpenExtendedMiningChannelSuccess, OpenMiningChannelError, OpenStandardMiningChannelSuccess, SetCustomMiningJob, @@ -25,7 +26,7 @@ use template_distribution_sv2::{NewTemplate, SetNewPrevHash as SetNewPrevHashFro use tracing::{debug, error, info, trace, warn}; -use stratum_common::bitcoin::{ +use bitcoin::{ block::{Header, Version}, hash_types, hashes::sha256d::Hash, @@ -1809,9 +1810,9 @@ impl ExtendedChannelKind { #[cfg(test)] mod test { use super::*; - use binary_sv2::{Seq0255, B064K, U256}; + use bitcoin::{Amount, PublicKey, Target, TxOut, WPubkeyHash}; + use codec_sv2::binary_sv2::{Seq0255, B064K, U256}; use mining_sv2::OpenStandardMiningChannel; - use stratum_common::bitcoin::{Amount, PublicKey, Target, TxOut, WPubkeyHash}; const BLOCK_REWARD: u64 = 2_000_000_000; diff --git a/protocols/v2/roles-logic-sv2/src/channel_logic/proxy_group_channel.rs b/protocols/v2/roles-logic-sv2/src/channel_logic/proxy_group_channel.rs index 50a59d175b..41e23c93da 100644 --- a/protocols/v2/roles-logic-sv2/src/channel_logic/proxy_group_channel.rs +++ b/protocols/v2/roles-logic-sv2/src/channel_logic/proxy_group_channel.rs @@ -213,7 +213,7 @@ impl GroupChannel { #[cfg(test)] mod test { use super::*; - use binary_sv2::B064K; + use codec_sv2::binary_sv2::{self, B064K}; use std::convert::TryFrom; #[test] diff --git a/protocols/v2/roles-logic-sv2/src/channels/chain_tip.rs b/protocols/v2/roles-logic-sv2/src/channels/chain_tip.rs index b8beca1ecd..2371d4a6af 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/chain_tip.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/chain_tip.rs @@ -1,5 +1,5 @@ //! # Chain Tip -use binary_sv2::U256; +use codec_sv2::binary_sv2::U256; /// An abstraction over the chain tip, carrying information from `SetNewPrevHash` messages. /// diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs index 4e48c2d9c4..ec824a1a93 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs @@ -10,17 +10,17 @@ use crate::{ }, utils::{bytes_to_hex, merkle_root_from_path, target_to_difficulty, u256_to_block_hash}, }; -use binary_sv2::Sv2Option; +use bitcoin::{ + blockdata::block::{Header, Version}, + hashes::sha256d::Hash, + CompactTarget, Target as BitcoinTarget, +}; +use codec_sv2::binary_sv2::{self, Sv2Option}; use mining_sv2::{ NewExtendedMiningJob, SetNewPrevHash as SetNewPrevHashMp, SubmitSharesExtended, Target, MAX_EXTRANONCE_LEN, }; use std::{collections::HashMap, convert::TryInto}; -use stratum_common::bitcoin::{ - blockdata::block::{Header, Version}, - hashes::sha256d::Hash, - CompactTarget, Target as BitcoinTarget, -}; use tracing::debug; // ExtendedJob is a tuple of: @@ -390,7 +390,7 @@ mod tests { extended::ExtendedChannel, share_accounting::{ShareValidationError, ShareValidationResult}, }; - use binary_sv2::Sv2Option; + use codec_sv2::binary_sv2::Sv2Option; use mining_sv2::{ NewExtendedMiningJob, SetNewPrevHash as SetNewPrevHashMp, SubmitSharesExtended, MAX_EXTRANONCE_LEN, diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/share_accounting.rs b/protocols/v2/roles-logic-sv2/src/channels/client/share_accounting.rs index 2a842a81db..06bc984d79 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/client/share_accounting.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/client/share_accounting.rs @@ -1,7 +1,7 @@ //! Abstractions for share validation for a Mining Client +use bitcoin::hashes::sha256d::Hash; use std::collections::HashSet; -use stratum_common::bitcoin::hashes::sha256d::Hash; /// The outcome of share validation, from the perspective of a Mining Client. #[derive(Debug)] diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs index 939730e80b..fe1d9cc372 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs @@ -10,17 +10,17 @@ use crate::{ }, utils::{bytes_to_hex, merkle_root_from_path, target_to_difficulty, u256_to_block_hash}, }; -use binary_sv2::Sv2Option; +use bitcoin::{ + blockdata::block::{Header, Version}, + hashes::sha256d::Hash, + CompactTarget, Target as BitcoinTarget, +}; +use codec_sv2::binary_sv2::{self, Sv2Option}; use mining_sv2::{ NewExtendedMiningJob, NewMiningJob, SetNewPrevHash as SetNewPrevHashMp, SubmitSharesStandard, Target, MAX_EXTRANONCE_LEN, }; use std::{collections::HashMap, convert::TryInto}; -use stratum_common::bitcoin::{ - blockdata::block::{Header, Version}, - hashes::sha256d::Hash, - CompactTarget, Target as BitcoinTarget, -}; use tracing::debug; /// Mining Client abstraction over the state of a Sv2 Standard Channel. @@ -350,7 +350,7 @@ mod tests { share_accounting::{ShareValidationError, ShareValidationResult}, standard::StandardChannel, }; - use binary_sv2::Sv2Option; + use codec_sv2::binary_sv2::Sv2Option; use mining_sv2::{NewMiningJob, SetNewPrevHash as SetNewPrevHashMp, SubmitSharesStandard}; #[test] diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs index bcbdb336f4..b01ecc3c93 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs @@ -14,14 +14,15 @@ use crate::{ u256_to_block_hash, }, }; -use mining_sv2::{SetCustomMiningJob, SubmitSharesExtended, Target, MAX_EXTRANONCE_LEN}; -use std::{collections::HashMap, convert::TryInto}; -use stratum_common::bitcoin::{ +use bitcoin::{ blockdata::block::{Header, Version}, hashes::sha256d::Hash, transaction::TxOut, CompactTarget, Target as BitcoinTarget, }; +use codec_sv2::binary_sv2; +use mining_sv2::{SetCustomMiningJob, SubmitSharesExtended, Target, MAX_EXTRANONCE_LEN}; +use std::{collections::HashMap, convert::TryInto}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash as SetNewPrevHashTdp}; use tracing::debug; @@ -604,10 +605,10 @@ mod tests { share_accounting::{ShareValidationError, ShareValidationResult}, }, }; - use binary_sv2::Sv2Option; + use bitcoin::{transaction::TxOut, Amount, ScriptBuf}; + use codec_sv2::binary_sv2::Sv2Option; use mining_sv2::{NewExtendedMiningJob, SubmitSharesExtended, Target, MAX_EXTRANONCE_LEN}; use std::convert::TryInto; - use stratum_common::bitcoin::{transaction::TxOut, Amount, ScriptBuf}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash}; const SATS_AVAILABLE_IN_TEMPLATE: u64 = 5000000000; diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/group.rs b/protocols/v2/roles-logic-sv2/src/channels/server/group.rs index 71569c0633..fbb960831f 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/group.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/group.rs @@ -6,7 +6,7 @@ use crate::channels::{ jobs::{extended::ExtendedJob, factory::JobFactory}, }, }; -use stratum_common::bitcoin::transaction::TxOut; +use bitcoin::transaction::TxOut; use template_distribution_sv2::{NewTemplate, SetNewPrevHash as SetNewPrevHashTdp}; use std::collections::{HashMap, HashSet}; @@ -196,10 +196,10 @@ impl<'a> GroupChannel<'a> { #[cfg(test)] mod tests { use crate::channels::{chain_tip::ChainTip, server::group::GroupChannel}; - use binary_sv2::Sv2Option; + use bitcoin::{transaction::TxOut, Amount, ScriptBuf}; + use codec_sv2::binary_sv2::Sv2Option; use mining_sv2::NewExtendedMiningJob; use std::convert::TryInto; - use stratum_common::bitcoin::{transaction::TxOut, Amount, ScriptBuf}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash}; const SATS_AVAILABLE_IN_TEMPLATE: u64 = 5000000000; diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs index 8242d96b95..adbe18ae0b 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs @@ -6,13 +6,13 @@ use crate::{ template_distribution_sv2::NewTemplate, utils::deserialize_outputs, }; -use binary_sv2::{Seq0255, Sv2Option, B0255, B064K, U256}; -use mining_sv2::{NewExtendedMiningJob, SetCustomMiningJob, MAX_EXTRANONCE_LEN}; -use std::convert::TryInto; -use stratum_common::bitcoin::{ +use bitcoin::{ consensus::{deserialize, serialize}, transaction::{Transaction, TxOut}, }; +use codec_sv2::binary_sv2::{Seq0255, Sv2Option, B0255, B064K, U256}; +use mining_sv2::{NewExtendedMiningJob, SetCustomMiningJob, MAX_EXTRANONCE_LEN}; +use std::convert::TryInto; /// Abstraction of an extended mining job with: /// - the `NewTemplate` OR `SetCustomMiningJob` message that originated it diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs index f7283531d8..ef7138a1b2 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs @@ -7,16 +7,16 @@ use crate::{ template_distribution_sv2::NewTemplate, utils::{deserialize_outputs, merkle_root_from_path, Id as JobIdFactory}, }; -use binary_sv2::{Sv2Option, B064K}; -use mining_sv2::{NewExtendedMiningJob, NewMiningJob, SetCustomMiningJob, MAX_EXTRANONCE_LEN}; -use std::convert::TryInto; -use stratum_common::bitcoin::{ +use bitcoin::{ absolute::LockTime, blockdata::witness::Witness, consensus::{serialize, Decodable}, transaction::{OutPoint, Transaction, TxIn, TxOut, Version}, Amount, Sequence, }; +use codec_sv2::binary_sv2::{Sv2Option, B064K}; +use mining_sv2::{NewExtendedMiningJob, NewMiningJob, SetCustomMiningJob, MAX_EXTRANONCE_LEN}; +use std::convert::TryInto; /// A Factory for creating Extended or Standard Jobs. /// @@ -404,7 +404,7 @@ impl JobFactory { #[cfg(test)] mod tests { use super::*; - use stratum_common::bitcoin::ScriptBuf; + use bitcoin::ScriptBuf; use template_distribution_sv2::NewTemplate; #[test] diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs index 3aa6f90561..66c294e11f 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs @@ -1,7 +1,7 @@ use crate::utils::deserialize_outputs; -use binary_sv2::{Sv2Option, U256}; +use bitcoin::transaction::TxOut; +use codec_sv2::binary_sv2::{Sv2Option, U256}; use mining_sv2::NewMiningJob; -use stratum_common::bitcoin::transaction::TxOut; use template_distribution_sv2::NewTemplate; /// Abstraction of a standard mining job with: diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/share_accounting.rs b/protocols/v2/roles-logic-sv2/src/channels/server/share_accounting.rs index 42ea0bd913..457046b6ac 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/share_accounting.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/share_accounting.rs @@ -1,7 +1,7 @@ //! Abstractions for share validation for a Mining Server +use bitcoin::hashes::sha256d::Hash; use std::collections::HashSet; -use stratum_common::bitcoin::hashes::sha256d::Hash; /// The outcome of share validation, from the perspective of a Mining Server. /// diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs index 789b76d380..dc4d814004 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs @@ -10,9 +10,7 @@ use crate::{ }, utils::{bytes_to_hex, hash_rate_to_target, target_to_difficulty, u256_to_block_hash}, }; -use mining_sv2::{SubmitSharesStandard, Target, MAX_EXTRANONCE_LEN}; -use std::{collections::HashMap, convert::TryInto}; -use stratum_common::bitcoin::{ +use bitcoin::{ absolute::LockTime, blockdata::{ block::{Header, Version}, @@ -23,6 +21,9 @@ use stratum_common::bitcoin::{ transaction::{OutPoint, Transaction, TxIn, TxOut, Version as TxVersion}, CompactTarget, Sequence, Target as BitcoinTarget, }; +use codec_sv2::binary_sv2; +use mining_sv2::{SubmitSharesStandard, Target, MAX_EXTRANONCE_LEN}; +use std::{collections::HashMap, convert::TryInto}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash}; use tracing::debug; @@ -539,10 +540,10 @@ mod tests { standard::StandardChannel, }, }; - use binary_sv2::Sv2Option; + use bitcoin::{transaction::TxOut, Amount, ScriptBuf}; + use codec_sv2::binary_sv2::Sv2Option; use mining_sv2::{NewMiningJob, SubmitSharesStandard, Target}; use std::convert::TryInto; - use stratum_common::bitcoin::{transaction::TxOut, Amount, ScriptBuf}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash as SetNewPrevHashTdp}; const SATS_AVAILABLE_IN_TEMPLATE: u64 = 5000000000; diff --git a/protocols/v2/roles-logic-sv2/src/errors.rs b/protocols/v2/roles-logic-sv2/src/errors.rs index 917e93aac3..6b2d1825a2 100644 --- a/protocols/v2/roles-logic-sv2/src/errors.rs +++ b/protocols/v2/roles-logic-sv2/src/errors.rs @@ -10,13 +10,13 @@ use crate::{ utils::InputError, vardiff::error::VardiffError, }; -use binary_sv2::Error as BinarySv2Error; +use bitcoin::hashes::FromSliceError; +use codec_sv2::binary_sv2::Error as BinarySv2Error; use mining_sv2::ExtendedExtranonceError; use std::{ fmt::{self, Display, Formatter}, sync::{MutexGuard, PoisonError}, }; -use stratum_common::bitcoin::hashes::FromSliceError; /// Error enum #[derive(Debug)] diff --git a/protocols/v2/roles-logic-sv2/src/handlers/mining.rs b/protocols/v2/roles-logic-sv2/src/handlers/mining.rs index 1447194711..9be8ad5463 100644 --- a/protocols/v2/roles-logic-sv2/src/handlers/mining.rs +++ b/protocols/v2/roles-logic-sv2/src/handlers/mining.rs @@ -28,6 +28,7 @@ //! handling edge cases and ensuring the correctness of the mining process. use crate::{errors::Error, parsers::Mining}; +use codec_sv2::binary_sv2; use core::convert::TryInto; use mining_sv2::{ CloseChannel, NewExtendedMiningJob, NewMiningJob, OpenExtendedMiningChannel, diff --git a/protocols/v2/roles-logic-sv2/src/job_creator.rs b/protocols/v2/roles-logic-sv2/src/job_creator.rs index 414cb91984..258669d109 100644 --- a/protocols/v2/roles-logic-sv2/src/job_creator.rs +++ b/protocols/v2/roles-logic-sv2/src/job_creator.rs @@ -3,23 +3,20 @@ //! This module provides logic to create extended mining jobs given a template from //! a template provider as well as logic to clean up old templates when new blocks are mined. use crate::{errors, utils::Id, Error}; -use binary_sv2::B064K; +use bitcoin::{ + absolute::LockTime, + blockdata::{ + transaction::{OutPoint, Transaction, TxIn, TxOut, Version}, + witness::Witness, + }, + consensus, + consensus::Decodable, + Amount, +}; +use codec_sv2::binary_sv2::{self, B064K}; use mining_sv2::NewExtendedMiningJob; use nohash_hasher::BuildNoHashHasher; use std::{collections::HashMap, convert::TryInto}; -use stratum_common::{ - bitcoin, - bitcoin::{ - absolute::LockTime, - blockdata::{ - transaction::{OutPoint, Transaction, TxIn, TxOut, Version}, - witness::Witness, - }, - consensus, - consensus::Decodable, - Amount, - }, -}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash}; use tracing::debug; @@ -435,16 +432,14 @@ pub mod tests { use super::*; use crate::utils::merkle_root_from_path; #[cfg(feature = "prop_test")] - use binary_sv2::u256_from_int; + use codec_sv2::binary_sv2::u256_from_int; use quickcheck::{Arbitrary, Gen}; use std::{cmp, vec}; #[cfg(feature = "prop_test")] use std::borrow::BorrowMut; - use stratum_common::bitcoin::{ - consensus::Encodable, secp256k1::Secp256k1, Network, PrivateKey, PublicKey, - }; + use bitcoin::{consensus::Encodable, secp256k1::Secp256k1, Network, PrivateKey, PublicKey}; pub fn template_from_gen(g: &mut Gen) -> NewTemplate<'static> { let mut coinbase_prefix_gen = Gen::new(255); @@ -501,7 +496,7 @@ pub mod tests { } #[cfg(feature = "prop_test")] - use stratum_common::bitcoin::ScriptBuf; + use bitcoin::ScriptBuf; // Test job_id_from_template #[cfg(feature = "prop_test")] diff --git a/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs b/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs index e95e08450a..d199580b15 100644 --- a/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs +++ b/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs @@ -18,7 +18,7 @@ use mining_sv2::{ use nohash_hasher::BuildNoHashHasher; use std::{collections::HashMap, convert::TryInto, sync::Arc}; -use stratum_common::bitcoin::hashes::{sha256d, Hash, HashEngine}; +use bitcoin::hashes::{sha256d, Hash, HashEngine}; /// Used to convert an extended mining job to a standard mining job. The `extranonce` field must /// be exactly 32 bytes. @@ -239,12 +239,12 @@ mod tests { JobsCreators, }, }; - use binary_sv2::{u256_from_int, U256}; + use codec_sv2::binary_sv2::{u256_from_int, U256}; use mining_sv2::Extranonce; use quickcheck::{Arbitrary, Gen}; use std::convert::TryFrom; - use stratum_common::bitcoin::{Amount, ScriptBuf, TxOut}; + use bitcoin::{Amount, ScriptBuf, TxOut}; const BLOCK_REWARD: u64 = 625_000_000_000; diff --git a/protocols/v2/roles-logic-sv2/src/lib.rs b/protocols/v2/roles-logic-sv2/src/lib.rs index c94be788ce..75a95074b6 100644 --- a/protocols/v2/roles-logic-sv2/src/lib.rs +++ b/protocols/v2/roles-logic-sv2/src/lib.rs @@ -27,6 +27,8 @@ pub mod job_dispatcher; pub mod parsers; pub mod utils; pub mod vardiff; +pub use bitcoin; +pub use codec_sv2; pub use common_messages_sv2; pub use errors::Error; pub use job_declaration_sv2; diff --git a/protocols/v2/roles-logic-sv2/src/parsers.rs b/protocols/v2/roles-logic-sv2/src/parsers.rs index a5e13a6bd9..ee8c661279 100644 --- a/protocols/v2/roles-logic-sv2/src/parsers.rs +++ b/protocols/v2/roles-logic-sv2/src/parsers.rs @@ -21,14 +21,17 @@ //! submission). use crate::Error; -use binary_sv2::{ - decodable::{DecodableField, FieldMarker}, - encodable::EncodableField, - from_bytes, Deserialize, GetSize, +use codec_sv2::{ + binary_sv2::{ + self, + decodable::{DecodableField, FieldMarker}, + encodable::EncodableField, + from_bytes, Deserialize, GetSize, + }, + framing_sv2::framing::Sv2Frame, }; use common_messages_sv2::*; use core::convert::{TryFrom, TryInto}; -use framing_sv2::framing::Sv2Frame; use job_declaration_sv2::*; use mining_sv2::*; use template_distribution_sv2::*; @@ -1215,8 +1218,10 @@ mod test { mining_sv2::NewMiningJob, parsers::{AnyMessage, Mining}, }; - use binary_sv2::{Sv2Option, U256}; - use codec_sv2::StandardSv2Frame; + use codec_sv2::{ + binary_sv2::{Sv2Option, U256}, + StandardSv2Frame, + }; use std::convert::{TryFrom, TryInto}; pub type Message = AnyMessage<'static>; diff --git a/protocols/v2/roles-logic-sv2/src/utils.rs b/protocols/v2/roles-logic-sv2/src/utils.rs index 89ad188972..84fd0ca2ce 100644 --- a/protocols/v2/roles-logic-sv2/src/utils.rs +++ b/protocols/v2/roles-logic-sv2/src/utils.rs @@ -5,8 +5,16 @@ //! management, mutex management, difficulty target calculations, merkle root calculations, and //! more. -use binary_sv2::U256; -use bitcoin::Block; +use bitcoin::{ + blockdata::block::{Header, Version}, + consensus, + consensus::Decodable, + hash_types::{BlockHash, TxMerkleNode}, + hashes::{sha256d::Hash as DHash, Hash}, + transaction::TxOut, + Block, CompactTarget, Transaction, +}; +use codec_sv2::binary_sv2::U256; use job_declaration_sv2::{DeclareMiningJob, PushSolution}; use mining_sv2::Target; use primitive_types::U256 as U256Primitive; @@ -17,18 +25,6 @@ use std::{ ops::Div, sync::{Mutex as Mutex_, MutexGuard, PoisonError}, }; -use stratum_common::{ - bitcoin, - bitcoin::{ - blockdata::block::{Header, Version}, - consensus, - consensus::Decodable, - hash_types::{BlockHash, TxMerkleNode}, - hashes::{sha256d::Hash as DHash, Hash}, - transaction::TxOut, - CompactTarget, Transaction, - }, -}; use tracing::error; use crate::errors::Error; @@ -888,7 +884,7 @@ impl<'a> From> for bitcoin::Block { mod tests { use super::{hash_rate_from_target, hash_rate_to_target, *}; - use binary_sv2::{Seq0255, B064K, U256}; + use codec_sv2::binary_sv2::{Seq0255, B064K, U256}; use rand::Rng; use serde::Deserialize; use std::{convert::TryInto, num::ParseIntError}; diff --git a/protocols/v2/subprotocols/common-messages/Cargo.toml b/protocols/v2/subprotocols/common-messages/Cargo.toml index e7054b38ca..7fd13b3b7b 100644 --- a/protocols/v2/subprotocols/common-messages/Cargo.toml +++ b/protocols/v2/subprotocols/common-messages/Cargo.toml @@ -15,7 +15,6 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] [dependencies] binary_sv2 = { path = "../../binary-sv2", version = "^3.0.0" } -stratum-common = { version = "^2.0.0", path = "../../../../common" } quickcheck = { version = "1.0.3", optional = true } quickcheck_macros = { version = "1", optional = true } diff --git a/protocols/v2/subprotocols/job-declaration/Cargo.toml b/protocols/v2/subprotocols/job-declaration/Cargo.toml index 06ec244551..158b350c03 100644 --- a/protocols/v2/subprotocols/job-declaration/Cargo.toml +++ b/protocols/v2/subprotocols/job-declaration/Cargo.toml @@ -14,4 +14,3 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] [dependencies] binary_sv2 = { path = "../../binary-sv2", version = "^3.0.0" } -stratum-common = { version = "^2.0.0", path = "../../../../common" } diff --git a/protocols/v2/subprotocols/mining/Cargo.toml b/protocols/v2/subprotocols/mining/Cargo.toml index c3d4eacead..8d83256687 100644 --- a/protocols/v2/subprotocols/mining/Cargo.toml +++ b/protocols/v2/subprotocols/mining/Cargo.toml @@ -16,7 +16,6 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] [dependencies] binary_sv2 = { path = "../../binary-sv2", version = "^3.0.0" } -stratum-common = { version = "^2.0.0", path = "../../../../common" } [dev-dependencies] quickcheck = "1.0.3" diff --git a/protocols/v2/subprotocols/template-distribution/Cargo.toml b/protocols/v2/subprotocols/template-distribution/Cargo.toml index ef8db761f9..fe6ac9f86a 100644 --- a/protocols/v2/subprotocols/template-distribution/Cargo.toml +++ b/protocols/v2/subprotocols/template-distribution/Cargo.toml @@ -15,7 +15,6 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] [dependencies] binary_sv2 = { path = "../../binary-sv2", version = "^3.0.0" } -stratum-common = { version = "^2.0.0", path = "../../../../common" } quickcheck = { version = "1.0.3", optional=true } quickcheck_macros = { version = "1", optional=true } diff --git a/protocols/v2/sv2-ffi/Cargo.toml b/protocols/v2/sv2-ffi/Cargo.toml index eae8e9bc49..402237c55c 100644 --- a/protocols/v2/sv2-ffi/Cargo.toml +++ b/protocols/v2/sv2-ffi/Cargo.toml @@ -15,7 +15,6 @@ crate-type = ["staticlib"] [dependencies] codec_sv2 = { path = "../codec-sv2", version = "^2.0.0" } -stratum-common = { version = "^2.0.0", path = "../../../common" } binary_sv2 = { path = "../binary-sv2", version = "^3.0.0" } common_messages_sv2 = { path = "../subprotocols/common-messages", version = "^5.0.0" } template_distribution_sv2 = { path = "../subprotocols/template-distribution", version = "^3.0.0" } diff --git a/roles/Cargo.lock b/roles/Cargo.lock index 525f4c69b8..52100e1b77 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -174,14 +174,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", + "pin-project-lite", "slab", ] @@ -202,9 +203,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" dependencies = [ "async-lock", "cfg-if", @@ -213,7 +214,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 0.38.44", + "rustix", "slab", "tracing", "windows-sys 0.59.0", @@ -515,9 +516,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "byte-slice-cast" @@ -636,7 +637,6 @@ dependencies = [ "framing_sv2", "noise_sv2", "rand 0.8.5", - "stratum-common", "tracing", ] @@ -651,7 +651,6 @@ name = "common_messages_sv2" version = "5.1.0" dependencies = [ "binary_sv2", - "stratum-common", ] [[package]] @@ -973,7 +972,6 @@ dependencies = [ "binary_sv2", "buffer_sv2", "noise_sv2", - "stratum-common", ] [[package]] @@ -1207,9 +1205,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -1377,8 +1375,6 @@ name = "integration_tests_sv2" version = "0.1.0" dependencies = [ "async-channel 1.9.0", - "binary_sv2", - "codec_sv2", "config-helpers", "corepc-node", "flate2", @@ -1389,11 +1385,9 @@ dependencies = [ "mining_device_sv1", "mining_proxy_sv2", "minreq", - "network_helpers_sv2", "once_cell", "pool_sv2", "rand 0.9.0", - "roles_logic_sv2", "stratum-common", "tar", "tokio", @@ -1420,20 +1414,16 @@ version = "0.1.4" dependencies = [ "async-channel 1.9.0", "async-recursion 0.3.2", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "config", "config-helpers", "error_handling", - "framing_sv2", "futures", "key-utils", - "network_helpers_sv2", "nohash-hasher", "primitive-types", - "roles_logic_sv2", + "secp256k1 0.28.2", "serde", "stratum-common", "tokio", @@ -1446,21 +1436,16 @@ name = "jd_server" version = "0.1.3" dependencies = [ "async-channel 1.9.0", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "config", "config-helpers", "error_handling", "hashbrown 0.11.2", "hex", "key-utils", - "network_helpers_sv2", "nohash-hasher", - "noise_sv2", "rand 0.8.5", - "roles_logic_sv2", "rpc_sv2", "serde", "serde_json", @@ -1475,7 +1460,6 @@ name = "job_declaration_sv2" version = "4.0.0" dependencies = [ "binary_sv2", - "stratum-common", ] [[package]] @@ -1554,12 +1538,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.9.3" @@ -1603,16 +1581,12 @@ version = "0.1.3" dependencies = [ "async-channel 1.9.0", "async-recursion 0.3.2", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "futures", "key-utils", - "network_helpers_sv2", "primitive-types", "rand 0.8.5", - "roles_logic_sv2", "sha2 0.10.8", "stratum-common", "tokio", @@ -1628,7 +1602,6 @@ dependencies = [ "num-bigint", "num-traits", "primitive-types", - "roles_logic_sv2", "serde", "serde_json", "stratum-common", @@ -1644,17 +1617,13 @@ version = "0.1.3" dependencies = [ "async-channel 1.9.0", "async-recursion 0.3.2", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "config", "futures", "key-utils", - "network_helpers_sv2", "nohash-hasher", "once_cell", - "roles_logic_sv2", "serde", "stratum-common", "tokio", @@ -1667,7 +1636,6 @@ name = "mining_sv2" version = "4.0.0" dependencies = [ "binary_sv2", - "stratum-common", ] [[package]] @@ -1717,15 +1685,13 @@ dependencies = [ [[package]] name = "network_helpers_sv2" -version = "3.1.0" +version = "4.0.0" dependencies = [ "async-channel 1.9.0", "async-std", - "binary_sv2", "codec_sv2", "futures", "serde_json", - "stratum-common", "sv1_api", "tokio", "tokio-util", @@ -1747,7 +1713,6 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "secp256k1 0.28.2", - "stratum-common", ] [[package]] @@ -1968,15 +1933,15 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.4" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 0.38.44", + "rustix", "tracing", "windows-sys 0.59.0", ] @@ -2010,21 +1975,17 @@ version = "0.1.3" dependencies = [ "async-channel 1.9.0", "async-recursion 1.1.1", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "config", "config-helpers", "error_handling", "hex", "integration_tests_sv2", "key-utils", - "network_helpers_sv2", "nohash-hasher", - "noise_sv2", "rand 0.8.5", - "roles_logic_sv2", + "secp256k1 0.28.2", "serde", "stratum-common", "tokio", @@ -2178,16 +2139,15 @@ dependencies = [ name = "roles_logic_sv2" version = "3.0.0" dependencies = [ - "binary_sv2", + "bitcoin", "chacha20poly1305", + "codec_sv2", "common_messages_sv2", - "framing_sv2", "hex-conservative 0.3.0", "job_declaration_sv2", "mining_sv2", "nohash-hasher", "primitive-types", - "stratum-common", "template_distribution_sv2", "tracing", ] @@ -2242,27 +2202,14 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustix" -version = "1.0.3" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.9.3", + "linux-raw-sys", "windows-sys 0.59.0", ] @@ -2478,10 +2425,10 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stratum-common" -version = "2.0.0" +version = "3.0.0" dependencies = [ - "bitcoin", - "secp256k1 0.28.2", + "network_helpers_sv2", + "roles_logic_sv2", ] [[package]] @@ -2555,7 +2502,7 @@ checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", "once_cell", - "rustix 1.0.3", + "rustix", "windows-sys 0.59.0", ] @@ -2564,7 +2511,6 @@ name = "template_distribution_sv2" version = "3.1.0" dependencies = [ "binary_sv2", - "stratum-common", ] [[package]] @@ -2752,20 +2698,15 @@ version = "1.0.0" dependencies = [ "async-channel 1.9.0", "async-recursion 0.3.2", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "config", "error_handling", - "framing_sv2", "futures", "key-utils", - "network_helpers_sv2", "once_cell", "primitive-types", "rand 0.8.5", - "roles_logic_sv2", "serde", "serde_json", "sha2 0.10.8", diff --git a/roles/Cargo.toml b/roles/Cargo.toml index fe9d5fa0aa..857edeed6a 100644 --- a/roles/Cargo.toml +++ b/roles/Cargo.toml @@ -8,7 +8,8 @@ members = [ "test-utils/mining-device-sv1", "translator", "jd-client", - "jd-server" + "jd-server", + "roles-utils/network-helpers" ] [profile.dev] diff --git a/roles/jd-client/Cargo.toml b/roles/jd-client/Cargo.toml index ed69a0f4f6..0afc5117ef 100644 --- a/roles/jd-client/Cargo.toml +++ b/roles/jd-client/Cargo.toml @@ -16,15 +16,11 @@ name = "jd_client" path = "src/lib/mod.rs" [dependencies] -stratum-common = { path = "../../common", features=["bitcoin"]} +secp256k1 = { version = "0.28.2", default-features = false, features = ["alloc", "rand", "rand-std"] } async-channel = "1.5.1" async-recursion = "0.3.2" -binary_sv2 = { path = "../../protocols/v2/binary-sv2" } buffer_sv2 = { path = "../../utils/buffer" } -codec_sv2 = { path = "../../protocols/v2/codec-sv2", features = ["noise_sv2", "with_buffer_pool"] } -framing_sv2 = { path = "../../protocols/v2/framing-sv2" } -network_helpers_sv2 = { path = "../roles-utils/network-helpers", features=["with_buffer_pool"] } -roles_logic_sv2 = { path = "../../protocols/v2/roles-logic-sv2" } +stratum-common = { path = "../../common", features = ["with_network_helpers"] } serde = { version = "1.0.89", default-features = false, features = ["derive", "alloc"] } futures = "0.3.25" tokio = { version = "1.44.1", features = ["full"] } diff --git a/roles/jd-client/src/lib/config.rs b/roles/jd-client/src/lib/config.rs index 22045bb9cb..357becfc3c 100644 --- a/roles/jd-client/src/lib/config.rs +++ b/roles/jd-client/src/lib/config.rs @@ -10,7 +10,7 @@ use config_helpers::CoinbaseOutput; use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; use serde::Deserialize; use std::{net::SocketAddr, time::Duration}; -use stratum_common::bitcoin::{Amount, TxOut}; +use stratum_common::roles_logic_sv2::bitcoin::{Amount, TxOut}; /// Represents the configuration of a Job Declarator Client (JDC). /// diff --git a/roles/jd-client/src/lib/downstream.rs b/roles/jd-client/src/lib/downstream.rs index def7b61e36..a6d4614881 100644 --- a/roles/jd-client/src/lib/downstream.rs +++ b/roles/jd-client/src/lib/downstream.rs @@ -28,8 +28,11 @@ use super::{ upstream_sv2::Upstream as UpstreamMiningNode, }; use async_channel::{bounded, Receiver, SendError, Sender}; -use roles_logic_sv2::{ +use stratum_common::roles_logic_sv2::{ + self, + bitcoin::{consensus::Decodable, TxOut}, channel_logic::channel_factory::{OnNewShare, PoolChannelFactory, Share}, + codec_sv2, common_messages_sv2::{SetupConnection, SetupConnectionSuccess}, common_properties::{CommonDownstreamData, IsDownstream, IsMiningDownstream}, errors::Error, @@ -49,8 +52,6 @@ use tracing::{debug, error, info, warn}; use codec_sv2::{HandshakeRole, Responder, StandardEitherFrame, StandardSv2Frame}; use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; -use stratum_common::bitcoin::{consensus::Decodable, TxOut}; - pub type Message = MiningDeviceMessages<'static>; pub type StdFrame = StandardSv2Frame; pub type EitherFrame = StandardEitherFrame; @@ -939,9 +940,11 @@ impl ParseCommonMessagesFromDownstream for DownstreamMiningNode { } } -use binary_sv2::Str0255; -use network_helpers_sv2::noise_connection::Connection; use std::net::SocketAddr; +use stratum_common::{ + network_helpers_sv2::noise_connection::Connection, + roles_logic_sv2::codec_sv2::binary_sv2::Str0255, +}; use tokio::{ net::TcpListener, task::AbortHandle, diff --git a/roles/jd-client/src/lib/error.rs b/roles/jd-client/src/lib/error.rs index 682c0244ba..01d68fa365 100644 --- a/roles/jd-client/src/lib/error.rs +++ b/roles/jd-client/src/lib/error.rs @@ -12,8 +12,12 @@ //! This module ensures that all errors can be passed around consistently, including across async //! boundaries. use ext_config::ConfigError; -use roles_logic_sv2::mining_sv2::{ExtendedExtranonce, NewExtendedMiningJob, SetCustomMiningJob}; use std::fmt; +use stratum_common::roles_logic_sv2::{ + self, + codec_sv2::{self, binary_sv2, framing_sv2}, + mining_sv2::{ExtendedExtranonce, NewExtendedMiningJob, SetCustomMiningJob}, +}; pub type ProxyResult<'a, T> = core::result::Result>; diff --git a/roles/jd-client/src/lib/job_declarator/message_handler.rs b/roles/jd-client/src/lib/job_declarator/message_handler.rs index 07b26938b6..ce1115e080 100644 --- a/roles/jd-client/src/lib/job_declarator/message_handler.rs +++ b/roles/jd-client/src/lib/job_declarator/message_handler.rs @@ -3,7 +3,8 @@ //! Handles upstream Job Declaration Protocol messages by implementing the //! `ParseJobDeclarationMessagesFromUpstream` trait. use super::JobDeclarator; -use roles_logic_sv2::{ +use stratum_common::roles_logic_sv2::{ + codec_sv2::binary_sv2, handlers::{job_declaration::ParseJobDeclarationMessagesFromUpstream, SendTo_}, job_declaration_sv2::{ AllocateMiningJobTokenSuccess, DeclareMiningJobError, DeclareMiningJobSuccess, @@ -13,7 +14,7 @@ use roles_logic_sv2::{ }; use tracing::{debug, error, info}; pub type SendTo = SendTo_, ()>; -use roles_logic_sv2::errors::Error; +use stratum_common::roles_logic_sv2::errors::Error; impl ParseJobDeclarationMessagesFromUpstream for JobDeclarator { /// Handles an `AllocateMiningJobTokenSuccess` message received from the JDS. diff --git a/roles/jd-client/src/lib/job_declarator/mod.rs b/roles/jd-client/src/lib/job_declarator/mod.rs index 0694852e8d..bd31a2f114 100644 --- a/roles/jd-client/src/lib/job_declarator/mod.rs +++ b/roles/jd-client/src/lib/job_declarator/mod.rs @@ -13,31 +13,35 @@ pub mod message_handler; use async_channel::{Receiver, Sender}; -use binary_sv2::{Seq0255, Seq064K, B016M, B064K, U256}; -use codec_sv2::{HandshakeRole, Initiator, StandardEitherFrame, StandardSv2Frame}; -use network_helpers_sv2::noise_connection::Connection; -use roles_logic_sv2::{ - handlers::SendTo_, - job_declaration_sv2::{AllocateMiningJobTokenSuccess, PushSolution}, - mining_sv2::SubmitSharesExtended, - parsers::{AnyMessage, JobDeclaration}, - template_distribution_sv2::SetNewPrevHash, - utils::Mutex, -}; use std::{collections::HashMap, convert::TryInto}; -use stratum_common::bitcoin::{consensus, hashes::Hash, Transaction}; +use stratum_common::{ + network_helpers_sv2::noise_connection::Connection, + roles_logic_sv2::{ + bitcoin::{consensus, hashes::Hash, Transaction}, + codec_sv2::{ + binary_sv2::{Seq0255, Seq064K, B016M, B064K, U256}, + HandshakeRole, Initiator, StandardEitherFrame, StandardSv2Frame, + }, + handlers::SendTo_, + job_declaration_sv2::{AllocateMiningJobTokenSuccess, PushSolution}, + mining_sv2::SubmitSharesExtended, + parsers::{AnyMessage, JobDeclaration}, + template_distribution_sv2::SetNewPrevHash, + utils::Mutex, + }, +}; use tokio::task::AbortHandle; use tracing::{debug, error, info}; use async_recursion::async_recursion; use nohash_hasher::BuildNoHashHasher; -use roles_logic_sv2::{ +use std::{net::SocketAddr, sync::Arc}; +use stratum_common::roles_logic_sv2::{ handlers::job_declaration::ParseJobDeclarationMessagesFromUpstream, job_declaration_sv2::{AllocateMiningJobToken, DeclareMiningJob}, template_distribution_sv2::NewTemplate, utils::Id, }; -use std::{net::SocketAddr, sync::Arc}; pub type Message = AnyMessage<'static>; pub type SendTo = SendTo_, ()>; diff --git a/roles/jd-client/src/lib/job_declarator/setup_connection.rs b/roles/jd-client/src/lib/job_declarator/setup_connection.rs index a696639e67..236095db9a 100644 --- a/roles/jd-client/src/lib/job_declarator/setup_connection.rs +++ b/roles/jd-client/src/lib/job_declarator/setup_connection.rs @@ -6,15 +6,16 @@ //! and handling common SV2 connection-related messages. use async_channel::{Receiver, Sender}; -use codec_sv2::{StandardEitherFrame, StandardSv2Frame}; -use roles_logic_sv2::{ +use std::{convert::TryInto, net::SocketAddr, sync::Arc}; +use stratum_common::roles_logic_sv2::{ + self, + codec_sv2::{StandardEitherFrame, StandardSv2Frame}, common_messages_sv2::{Protocol, Reconnect, SetupConnection}, handlers::common::{ParseCommonMessagesFromUpstream, SendTo}, parsers::AnyMessage, utils::Mutex, Error, }; -use std::{convert::TryInto, net::SocketAddr, sync::Arc}; use tracing::info; pub type Message = AnyMessage<'static>; diff --git a/roles/jd-client/src/lib/mod.rs b/roles/jd-client/src/lib/mod.rs index fefdf85397..81d84ea1d9 100644 --- a/roles/jd-client/src/lib/mod.rs +++ b/roles/jd-client/src/lib/mod.rs @@ -19,12 +19,12 @@ use async_channel::unbounded; use config::JobDeclaratorClientConfig; use futures::{select, FutureExt}; use job_declarator::JobDeclarator; -use roles_logic_sv2::utils::Mutex; use std::{ net::{IpAddr, SocketAddr}, str::FromStr, sync::Arc, }; +use stratum_common::roles_logic_sv2::utils::Mutex; use tokio::{sync::Notify, task::AbortHandle}; use tracing::{error, info}; diff --git a/roles/jd-client/src/lib/template_receiver/message_handler.rs b/roles/jd-client/src/lib/template_receiver/message_handler.rs index fff8e6fb94..1665069a34 100644 --- a/roles/jd-client/src/lib/template_receiver/message_handler.rs +++ b/roles/jd-client/src/lib/template_receiver/message_handler.rs @@ -6,7 +6,7 @@ //! template distribution messages received from a server, and how it responds //! to each message type accordingly. use super::TemplateRx; -use roles_logic_sv2::{ +use stratum_common::roles_logic_sv2::{ errors::Error, handlers::template_distribution::{ParseTemplateDistributionMessagesFromServer, SendTo}, parsers::TemplateDistribution, diff --git a/roles/jd-client/src/lib/template_receiver/mod.rs b/roles/jd-client/src/lib/template_receiver/mod.rs index 2803654e68..c0d5685826 100644 --- a/roles/jd-client/src/lib/template_receiver/mod.rs +++ b/roles/jd-client/src/lib/template_receiver/mod.rs @@ -7,24 +7,27 @@ //! and downstream subsystem. use super::{job_declarator::JobDeclarator, status, PoolChangerTrigger}; use async_channel::{Receiver, Sender}; -use codec_sv2::{HandshakeRole, Initiator, StandardEitherFrame, StandardSv2Frame}; use error_handling::handle_result; use key_utils::Secp256k1PublicKey; -use network_helpers_sv2::noise_connection::Connection; -use roles_logic_sv2::{ - handlers::{template_distribution::ParseTemplateDistributionMessagesFromServer, SendTo_}, - job_declaration_sv2::AllocateMiningJobTokenSuccess, - parsers::{AnyMessage, TemplateDistribution}, - template_distribution_sv2::{ - CoinbaseOutputConstraints, NewTemplate, RequestTransactionData, SubmitSolution, - }, - utils::Mutex, -}; use setup_connection::SetupConnectionHandler; use std::{convert::TryInto, net::SocketAddr, sync::Arc}; -use stratum_common::bitcoin::{ - consensus::{deserialize, Encodable}, - Transaction, TxOut, +use stratum_common::{ + network_helpers_sv2::noise_connection::Connection, + roles_logic_sv2::{ + self, + bitcoin::{ + consensus::{deserialize, Encodable}, + Transaction, TxOut, + }, + codec_sv2::{HandshakeRole, Initiator, StandardEitherFrame, StandardSv2Frame}, + handlers::{template_distribution::ParseTemplateDistributionMessagesFromServer, SendTo_}, + job_declaration_sv2::AllocateMiningJobTokenSuccess, + parsers::{AnyMessage, TemplateDistribution}, + template_distribution_sv2::{ + CoinbaseOutputConstraints, NewTemplate, RequestTransactionData, SubmitSolution, + }, + utils::Mutex, + }, }; use tokio::task::AbortHandle; use tracing::{error, info, warn}; diff --git a/roles/jd-client/src/lib/template_receiver/setup_connection.rs b/roles/jd-client/src/lib/template_receiver/setup_connection.rs index af1b6e7e77..bf2a5f3914 100644 --- a/roles/jd-client/src/lib/template_receiver/setup_connection.rs +++ b/roles/jd-client/src/lib/template_receiver/setup_connection.rs @@ -5,15 +5,16 @@ //! This includes sending a `SetupConnection` message and processing responses from the upstream. use async_channel::{Receiver, Sender}; -use codec_sv2::{StandardEitherFrame, StandardSv2Frame}; -use roles_logic_sv2::{ +use std::{convert::TryInto, net::SocketAddr, sync::Arc}; +use stratum_common::roles_logic_sv2::{ + self, + codec_sv2::{StandardEitherFrame, StandardSv2Frame}, common_messages_sv2::{Protocol, Reconnect, SetupConnection}, handlers::common::{ParseCommonMessagesFromUpstream, SendTo}, parsers::AnyMessage, utils::Mutex, Error, }; -use std::{convert::TryInto, net::SocketAddr, sync::Arc}; use tracing::info; pub type Message = AnyMessage<'static>; diff --git a/roles/jd-client/src/lib/upstream_sv2/mod.rs b/roles/jd-client/src/lib/upstream_sv2/mod.rs index c7f9dcc53d..aa5472316e 100644 --- a/roles/jd-client/src/lib/upstream_sv2/mod.rs +++ b/roles/jd-client/src/lib/upstream_sv2/mod.rs @@ -1,5 +1,7 @@ -use codec_sv2::{StandardEitherFrame, StandardSv2Frame}; -use roles_logic_sv2::parsers::AnyMessage; +use stratum_common::roles_logic_sv2::{ + codec_sv2::{StandardEitherFrame, StandardSv2Frame}, + parsers::AnyMessage, +}; pub mod upstream; pub use upstream::Upstream; diff --git a/roles/jd-client/src/lib/upstream_sv2/upstream.rs b/roles/jd-client/src/lib/upstream_sv2/upstream.rs index adc8bce59d..1b4e7eae56 100644 --- a/roles/jd-client/src/lib/upstream_sv2/upstream.rs +++ b/roles/jd-client/src/lib/upstream_sv2/upstream.rs @@ -34,25 +34,8 @@ use super::super::{ PoolChangerTrigger, }; use async_channel::{Receiver, Sender}; -use binary_sv2::{Seq0255, U256}; -use codec_sv2::{HandshakeRole, Initiator}; use error_handling::handle_result; use key_utils::Secp256k1PublicKey; -use network_helpers_sv2::noise_connection::Connection; -use roles_logic_sv2::{ - channel_logic::channel_factory::PoolChannelFactory, - common_messages_sv2::{Protocol, Reconnect, SetupConnection}, - common_properties::{IsMiningUpstream, IsUpstream}, - handlers::{ - common::{ParseCommonMessagesFromUpstream, SendTo as SendToCommon}, - mining::{ParseMiningMessagesFromUpstream, SendTo, SupportedChannelTypes}, - }, - job_declaration_sv2::DeclareMiningJob, - mining_sv2::{ExtendedExtranonce, Extranonce, SetCustomMiningJob, SetGroupChannel}, - parsers::{AnyMessage, Mining, MiningDeviceMessages}, - utils::{Id, Mutex}, - Error as RolesLogicError, -}; use std::{ collections::{HashMap, VecDeque}, net::SocketAddr, @@ -60,6 +43,29 @@ use std::{ thread::sleep, time::Duration, }; +use stratum_common::{ + network_helpers_sv2::noise_connection::Connection, + roles_logic_sv2, + roles_logic_sv2::{ + channel_logic::channel_factory::PoolChannelFactory, + codec_sv2::{ + self, binary_sv2, + binary_sv2::{Seq0255, U256}, + framing_sv2, HandshakeRole, Initiator, + }, + common_messages_sv2::{Protocol, Reconnect, SetupConnection}, + common_properties::{IsMiningUpstream, IsUpstream}, + handlers::{ + common::{ParseCommonMessagesFromUpstream, SendTo as SendToCommon}, + mining::{ParseMiningMessagesFromUpstream, SendTo, SupportedChannelTypes}, + }, + job_declaration_sv2::DeclareMiningJob, + mining_sv2::{ExtendedExtranonce, Extranonce, SetCustomMiningJob, SetGroupChannel}, + parsers::{AnyMessage, Mining, MiningDeviceMessages}, + utils::{Id, Mutex}, + Error as RolesLogicError, + }, +}; use tokio::{net::TcpStream, task, task::AbortHandle}; use tracing::{debug, error, info, warn}; diff --git a/roles/jd-server/Cargo.toml b/roles/jd-server/Cargo.toml index 8ba64dcce3..224888710f 100644 --- a/roles/jd-server/Cargo.toml +++ b/roles/jd-server/Cargo.toml @@ -17,15 +17,10 @@ name = "jd_server" path = "src/lib/mod.rs" [dependencies] -stratum-common = { version = "2.0.0", path = "../../common" } +stratum-common = { path = "../../common", features = ["with_network_helpers"] } async-channel = "1.5.1" -binary_sv2 = { path = "../../protocols/v2/binary-sv2" } buffer_sv2 = { path = "../../utils/buffer" } -codec_sv2 = { path = "../../protocols/v2/codec-sv2", features = ["noise_sv2"] } -network_helpers_sv2 = { path = "../roles-utils/network-helpers" } -noise_sv2 = { path = "../../protocols/v2/noise-sv2" } rand = "0.8.4" -roles_logic_sv2 = { path = "../../protocols/v2/roles-logic-sv2" } tokio = { version = "1.44.1", features = ["full"] } ext-config = { version = "0.14.0", features = ["toml"], package = "config" } tracing = { version = "0.1" } diff --git a/roles/jd-server/src/lib/config.rs b/roles/jd-server/src/lib/config.rs index 26495817eb..2b9466be23 100644 --- a/roles/jd-server/src/lib/config.rs +++ b/roles/jd-server/src/lib/config.rs @@ -15,7 +15,7 @@ use config_helpers::CoinbaseOutput; use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; use serde::Deserialize; use std::{convert::TryInto, time::Duration}; -use stratum_common::bitcoin::{Amount, TxOut}; +use stratum_common::roles_logic_sv2::bitcoin::{Amount, TxOut}; #[derive(Debug, serde::Deserialize, Clone)] pub struct JobDeclaratorServerConfig { @@ -174,7 +174,7 @@ mod tests { use config_helpers::CoinbaseOutput; use ext_config::{Config, File, FileFormat}; use std::{convert::TryInto, path::PathBuf}; - use stratum_common::bitcoin::{Amount, ScriptBuf, TxOut}; + use stratum_common::roles_logic_sv2::bitcoin::{Amount, ScriptBuf, TxOut}; use crate::config::JobDeclaratorServerConfig; diff --git a/roles/jd-server/src/lib/error.rs b/roles/jd-server/src/lib/error.rs index 152d842349..6065b443e7 100644 --- a/roles/jd-server/src/lib/error.rs +++ b/roles/jd-server/src/lib/error.rs @@ -19,7 +19,11 @@ use std::{ sync::{MutexGuard, PoisonError}, }; -use roles_logic_sv2::parsers::Mining; +use stratum_common::roles_logic_sv2::{ + self, + codec_sv2::{self, binary_sv2, noise_sv2}, + parsers::Mining, +}; use crate::mempool::error::JdsMempoolError; diff --git a/roles/jd-server/src/lib/job_declarator/message_handler.rs b/roles/jd-server/src/lib/job_declarator/message_handler.rs index 9373144506..254600bc23 100644 --- a/roles/jd-server/src/lib/job_declarator/message_handler.rs +++ b/roles/jd-server/src/lib/job_declarator/message_handler.rs @@ -1,5 +1,11 @@ -use binary_sv2::{Decodable, Serialize, U256}; -use roles_logic_sv2::{ +use std::{convert::TryInto, io::Cursor, sync::Arc}; +use stratum_common::roles_logic_sv2::{ + bitcoin::{ + consensus::Decodable as BitcoinDecodable, + hashes::{sha256d, Hash}, + Transaction, Txid, + }, + codec_sv2::binary_sv2::{Decodable, Serialize, U256}, handlers::{job_declaration::ParseJobDeclarationMessagesFromDownstream, SendTo_}, job_declaration_sv2::{ AllocateMiningJobToken, AllocateMiningJobTokenSuccess, DeclareMiningJob, @@ -9,17 +15,11 @@ use roles_logic_sv2::{ parsers::JobDeclaration, utils::Mutex, }; -use std::{convert::TryInto, io::Cursor, sync::Arc}; -use stratum_common::bitcoin::{ - hashes::{sha256d, Hash}, - Transaction, Txid, -}; pub type SendTo = SendTo_, ()>; use crate::mempool::JDsMempool; use super::{signed_token, TransactionState}; -use roles_logic_sv2::{errors::Error, parsers::AnyMessage as AllMessages}; -use stratum_common::bitcoin::consensus::Decodable as BitcoinDecodable; +use stratum_common::roles_logic_sv2::{errors::Error, parsers::AnyMessage as AllMessages}; use tracing::{debug, info}; use super::JobDeclaratorDownstream; diff --git a/roles/jd-server/src/lib/job_declarator/mod.rs b/roles/jd-server/src/lib/job_declarator/mod.rs index ad8729d479..d563988128 100644 --- a/roles/jd-server/src/lib/job_declarator/mod.rs +++ b/roles/jd-server/src/lib/job_declarator/mod.rs @@ -22,31 +22,36 @@ use super::{ error::JdsError, mempool::JDsMempool, status, EitherFrame, JobDeclaratorServerConfig, StdFrame, }; use async_channel::{Receiver, Sender}; -use binary_sv2::{B0255, U256}; -use codec_sv2::{HandshakeRole, Responder}; use core::panic; use error_handling::handle_result; use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey, SignatureService}; -use network_helpers_sv2::noise_connection::Connection; use nohash_hasher::BuildNoHashHasher; -use roles_logic_sv2::{ - common_messages_sv2::{ - Protocol, SetupConnection, SetupConnectionError, SetupConnectionSuccess, +use std::{collections::HashMap, convert::TryInto, sync::Arc}; +use stratum_common::{ + network_helpers_sv2::noise_connection::Connection, + roles_logic_sv2::{ + self, + bitcoin::{ + consensus::{encode::serialize, Encodable}, + Block, Transaction, Txid, + }, + codec_sv2::{ + binary_sv2, + binary_sv2::{B0255, U256}, + HandshakeRole, Responder, + }, + common_messages_sv2::{ + Protocol, SetupConnection, SetupConnectionError, SetupConnectionSuccess, + }, + handlers::job_declaration::{ParseJobDeclarationMessagesFromDownstream, SendTo}, + job_declaration_sv2::{DeclareMiningJob, PushSolution}, + parsers::{AnyMessage as JdsMessages, JobDeclaration}, + utils::{Id, Mutex}, }, - handlers::job_declaration::{ParseJobDeclarationMessagesFromDownstream, SendTo}, - job_declaration_sv2::{DeclareMiningJob, PushSolution}, - parsers::{AnyMessage as JdsMessages, JobDeclaration}, - utils::{Id, Mutex}, }; -use std::{collections::HashMap, convert::TryInto, sync::Arc}; use tokio::{net::TcpListener, time::Duration}; use tracing::{debug, error, info}; -use stratum_common::bitcoin::{ - consensus::{encode::serialize, Encodable}, - Block, Transaction, Txid, -}; - /// Represents whether a transaction declared in a mining job is known to the JDS mempool /// or still missing and needs to be fetched/provided. #[derive(Clone, Debug)] diff --git a/roles/jd-server/src/lib/mempool/mod.rs b/roles/jd-server/src/lib/mempool/mod.rs index 0899bdfe23..693af8fee6 100644 --- a/roles/jd-server/src/lib/mempool/mod.rs +++ b/roles/jd-server/src/lib/mempool/mod.rs @@ -19,12 +19,13 @@ pub mod error; use super::job_declarator::AddTrasactionsToMempoolInner; use crate::mempool::error::JdsMempoolError; use async_channel::Receiver; -use bitcoin::blockdata::transaction::Transaction; use hashbrown::HashMap; -use roles_logic_sv2::utils::Mutex; use rpc_sv2::{mini_rpc_client, mini_rpc_client::RpcError}; use std::{str::FromStr, sync::Arc}; -use stratum_common::{bitcoin, bitcoin::hash_types::Txid}; +use stratum_common::roles_logic_sv2::{ + bitcoin::{blockdata::transaction::Transaction, hash_types::Txid}, + utils::Mutex, +}; /// Wrapper around a known transaction and its hash. #[derive(Clone, Debug)] diff --git a/roles/jd-server/src/lib/mod.rs b/roles/jd-server/src/lib/mod.rs index c2e4944b4c..00aea8cc54 100644 --- a/roles/jd-server/src/lib/mod.rs +++ b/roles/jd-server/src/lib/mod.rs @@ -23,15 +23,18 @@ pub mod job_declarator; pub mod mempool; pub mod status; use async_channel::{bounded, unbounded, Receiver, Sender}; -use codec_sv2::{StandardEitherFrame, StandardSv2Frame}; use config::JobDeclaratorServerConfig; use error::JdsError; use error_handling::handle_result; use job_declarator::JobDeclarator; use mempool::error::JdsMempoolError; -use roles_logic_sv2::{parsers::AnyMessage as JdsMessages, utils::Mutex}; pub use rpc_sv2::Uri; use std::{ops::Sub, str::FromStr, sync::Arc}; +use stratum_common::roles_logic_sv2::{ + codec_sv2::{StandardEitherFrame, StandardSv2Frame}, + parsers::AnyMessage as JdsMessages, + utils::Mutex, +}; use tokio::{select, task}; use tracing::{error, info, warn}; diff --git a/roles/jd-server/src/lib/status.rs b/roles/jd-server/src/lib/status.rs index 55a1ec81ce..7562f702bf 100644 --- a/roles/jd-server/src/lib/status.rs +++ b/roles/jd-server/src/lib/status.rs @@ -8,7 +8,7 @@ //! //! This allows for centralized, consistent error handling across the application. -use roles_logic_sv2::parsers::Mining; +use stratum_common::roles_logic_sv2::parsers::Mining; use super::error::JdsError; @@ -162,7 +162,11 @@ mod tests { use super::*; use async_channel::{bounded, RecvError}; - use roles_logic_sv2::mining_sv2::OpenMiningChannelError; + use stratum_common::roles_logic_sv2::{ + self, + codec_sv2::{self, binary_sv2, noise_sv2}, + mining_sv2::OpenMiningChannelError, + }; #[tokio::test] async fn test_send_status_downstream_listener_shutdown() { diff --git a/roles/mining-proxy/Cargo.toml b/roles/mining-proxy/Cargo.toml index 6508e00d18..dac10c81c6 100644 --- a/roles/mining-proxy/Cargo.toml +++ b/roles/mining-proxy/Cargo.toml @@ -17,17 +17,13 @@ name = "mining_proxy_sv2" path = "src/lib/mod.rs" [dependencies] +stratum-common = { path = "../../common" , features = ["with_network_helpers"] } async-channel = "1.8.0" async-recursion = "0.3.2" -binary_sv2 = { path = "../../protocols/v2/binary-sv2" } buffer_sv2 = { path = "../../utils/buffer" } -codec_sv2 = { path = "../../protocols/v2/codec-sv2", features = ["noise_sv2", "with_buffer_pool"] } futures = "0.3.19" -network_helpers_sv2 = { path = "../roles-utils/network-helpers", features = ["with_buffer_pool"] } once_cell = "1.12.0" -roles_logic_sv2 = { path = "../../protocols/v2/roles-logic-sv2" } serde = { version = "1.0.89", features = ["derive", "alloc"], default-features = false } -stratum-common = { path = "../../common" } tokio = { version = "1.44.1", features = ["full"] } ext-config = { version = "0.14.0", features = ["toml"], package = "config" } tracing = {version = "0.1"} diff --git a/roles/mining-proxy/src/lib/downstream_mining.rs b/roles/mining-proxy/src/lib/downstream_mining.rs index a71c137678..77517792a4 100644 --- a/roles/mining-proxy/src/lib/downstream_mining.rs +++ b/roles/mining-proxy/src/lib/downstream_mining.rs @@ -8,19 +8,22 @@ use super::{ routing_logic::{CommonRouter, CommonRoutingLogic, MiningRouter, MiningRoutingLogic}, upstream_mining::{StdFrame as UpstreamFrame, UpstreamMiningNode}, }; -use codec_sv2::{StandardEitherFrame, StandardSv2Frame}; -use network_helpers_sv2::plain_connection::PlainConnection; -use roles_logic_sv2::{ - common_messages_sv2::{SetupConnection, SetupConnectionSuccess}, - common_properties::{CommonDownstreamData, IsDownstream, IsMiningDownstream}, - errors::Error, - handlers::{ - common::{ParseCommonMessagesFromDownstream, SendTo as SendToCommon}, - mining::{ParseMiningMessagesFromDownstream, SendTo, SupportedChannelTypes}, +use stratum_common::{ + network_helpers_sv2::plain_connection::PlainConnection, + roles_logic_sv2::{ + self, codec_sv2, + codec_sv2::{binary_sv2, StandardEitherFrame, StandardSv2Frame}, + common_messages_sv2::{SetupConnection, SetupConnectionSuccess}, + common_properties::{CommonDownstreamData, IsDownstream, IsMiningDownstream}, + errors::Error, + handlers::{ + common::{ParseCommonMessagesFromDownstream, SendTo as SendToCommon}, + mining::{ParseMiningMessagesFromDownstream, SendTo, SupportedChannelTypes}, + }, + mining_sv2::*, + parsers::{AnyMessage, Mining, MiningDeviceMessages}, + utils::Mutex, }, - mining_sv2::*, - parsers::{AnyMessage, Mining, MiningDeviceMessages}, - utils::Mutex, }; pub type Message = MiningDeviceMessages<'static>; diff --git a/roles/mining-proxy/src/lib/error.rs b/roles/mining-proxy/src/lib/error.rs index b28e60c001..8029af2f21 100644 --- a/roles/mining-proxy/src/lib/error.rs +++ b/roles/mining-proxy/src/lib/error.rs @@ -1,8 +1,7 @@ use async_channel::SendError; -use codec_sv2::StandardEitherFrame; use core::fmt; -use roles_logic_sv2::parsers::AnyMessage; use std::net::SocketAddr; +use stratum_common::roles_logic_sv2::{codec_sv2::StandardEitherFrame, parsers::AnyMessage}; pub type Message = AnyMessage<'static>; pub type EitherFrame = StandardEitherFrame; diff --git a/roles/mining-proxy/src/lib/mod.rs b/roles/mining-proxy/src/lib/mod.rs index 769b7ae759..bcfee738a4 100644 --- a/roles/mining-proxy/src/lib/mod.rs +++ b/roles/mining-proxy/src/lib/mod.rs @@ -5,11 +5,11 @@ pub mod selectors; pub mod upstream_mining; use once_cell::sync::OnceCell; -use roles_logic_sv2::utils::{GroupId, Id, Mutex}; use routing_logic::{CommonRoutingLogic, MiningProxyRoutingLogic, MiningRoutingLogic}; use selectors::GeneralMiningSelector; use serde::Deserialize; use std::{net::SocketAddr, sync::Arc}; +use stratum_common::roles_logic_sv2::utils::{GroupId, Id, Mutex}; use tokio::{net::TcpListener, sync::oneshot}; use tracing::info; use upstream_mining::UpstreamMiningNode; diff --git a/roles/mining-proxy/src/lib/routing_logic.rs b/roles/mining-proxy/src/lib/routing_logic.rs index 2d42dc81af..4aee83dcb8 100644 --- a/roles/mining-proxy/src/lib/routing_logic.rs +++ b/roles/mining-proxy/src/lib/routing_logic.rs @@ -30,7 +30,8 @@ use super::{ }, upstream_mining::HasDownstreamSelector, }; -use roles_logic_sv2::{ +use std::{collections::HashMap, fmt::Debug as D, marker::PhantomData, sync::Arc}; +use stratum_common::roles_logic_sv2::{ common_messages_sv2::{ has_requires_std_job, Protocol, SetupConnection, SetupConnectionSuccess, }, @@ -42,7 +43,6 @@ use roles_logic_sv2::{ utils::{Id, Mutex}, Error, }; -use std::{collections::HashMap, fmt::Debug as D, marker::PhantomData, sync::Arc}; /// Defines routing logic for common protocol messages. /// diff --git a/roles/mining-proxy/src/lib/selectors.rs b/roles/mining-proxy/src/lib/selectors.rs index 7a69c0fe01..e9db105dd6 100644 --- a/roles/mining-proxy/src/lib/selectors.rs +++ b/roles/mining-proxy/src/lib/selectors.rs @@ -5,12 +5,12 @@ //! send messages to. use nohash_hasher::BuildNoHashHasher; -use roles_logic_sv2::{ +use std::{collections::HashMap, fmt::Debug as D, sync::Arc}; +use stratum_common::roles_logic_sv2::{ common_properties::{IsDownstream, IsMiningDownstream, IsMiningUpstream, PairSettings}, utils::Mutex, Error, }; -use std::{collections::HashMap, fmt::Debug as D, sync::Arc}; /// Proxy selector for routing messages to downstream mining nodes. /// diff --git a/roles/mining-proxy/src/lib/upstream_mining.rs b/roles/mining-proxy/src/lib/upstream_mining.rs index b4d44f6c1a..13ccb1249d 100644 --- a/roles/mining-proxy/src/lib/upstream_mining.rs +++ b/roles/mining-proxy/src/lib/upstream_mining.rs @@ -15,26 +15,31 @@ use super::{ selectors::{DownstreamMiningSelector, ProxyDownstreamMiningSelector as Prs}, EXTRANONCE_RANGE_1_LENGTH, }; -use codec_sv2::{HandshakeRole, Initiator, StandardEitherFrame, StandardSv2Frame}; -use network_helpers_sv2::noise_connection::Connection; -use roles_logic_sv2::{ - channel_logic::{ - channel_factory::{ExtendedChannelKind, OnNewShare, ProxyExtendedChannelFactory, Share}, - proxy_group_channel::GroupChannels, - }, - common_messages_sv2::{Protocol, SetupConnection}, - common_properties::{ - IsMiningDownstream, IsMiningUpstream, IsUpstream, RequestIdMapper, UpstreamChannel, +use stratum_common::{ + network_helpers_sv2::noise_connection::Connection, + roles_logic_sv2::{ + bitcoin::TxOut, + channel_logic::{ + channel_factory::{ + ExtendedChannelKind, OnNewShare, ProxyExtendedChannelFactory, Share, + }, + proxy_group_channel::GroupChannels, + }, + codec_sv2, + codec_sv2::{HandshakeRole, Initiator, StandardEitherFrame, StandardSv2Frame}, + common_messages_sv2::{Protocol, SetupConnection}, + common_properties::{ + IsMiningDownstream, IsMiningUpstream, IsUpstream, RequestIdMapper, UpstreamChannel, + }, + errors::Error, + handlers::mining::{ParseMiningMessagesFromUpstream, SendTo, SupportedChannelTypes}, + job_dispatcher::GroupChannelJobDispatcher, + mining_sv2::*, + parsers::{AnyMessage, CommonMessages, Mining, MiningDeviceMessages}, + template_distribution_sv2::SubmitSolution, + utils::{GroupId, Id, Mutex}, }, - errors::Error, - handlers::mining::{ParseMiningMessagesFromUpstream, SendTo, SupportedChannelTypes}, - job_dispatcher::GroupChannelJobDispatcher, - mining_sv2::*, - parsers::{AnyMessage, CommonMessages, Mining, MiningDeviceMessages}, - template_distribution_sv2::SubmitSolution, - utils::{GroupId, Id, Mutex}, }; -use stratum_common::bitcoin::TxOut; pub type Message = AnyMessage<'static>; pub type StdFrame = StandardSv2Frame; diff --git a/roles/pool/Cargo.toml b/roles/pool/Cargo.toml index bd60449887..378a14fbba 100644 --- a/roles/pool/Cargo.toml +++ b/roles/pool/Cargo.toml @@ -18,15 +18,11 @@ path = "src/lib/mod.rs" [dependencies] async-channel = "1.5.1" -binary_sv2 = { path = "../../protocols/v2/binary-sv2" } +stratum-common = { path = "../../common", features = ["with_network_helpers"] } buffer_sv2 = { path = "../../utils/buffer" } -codec_sv2 = { path = "../../protocols/v2/codec-sv2", features = ["noise_sv2"] } -network_helpers_sv2 = { path = "../roles-utils/network-helpers", features =["with_buffer_pool"] } -noise_sv2 = { path = "../../protocols/v2/noise-sv2" } rand = "0.8.4" -roles_logic_sv2 = { path = "../../protocols/v2/roles-logic-sv2" } serde = { version = "1.0.89", features = ["derive", "alloc"], default-features = false } -stratum-common = { path = "../../common", features = ["bitcoin"] } +secp256k1 = { version = "0.28.2", default-features = false, features = ["alloc", "rand", "rand-std"] } tokio = { version = "1.44.1", features = ["full"] } ext-config = { version = "0.14.0", features = ["toml"], package = "config" } tracing = { version = "0.1" } diff --git a/roles/pool/src/lib/error.rs b/roles/pool/src/lib/error.rs index 2f5e21f830..5f03f65524 100644 --- a/roles/pool/src/lib/error.rs +++ b/roles/pool/src/lib/error.rs @@ -16,7 +16,11 @@ use std::{ sync::{MutexGuard, PoisonError}, }; -use roles_logic_sv2::parsers::Mining; +use stratum_common::roles_logic_sv2::{ + self, + codec_sv2::{self, binary_sv2, noise_sv2}, + parsers::Mining, +}; /// Represents various errors that can occur in the pool implementation. #[derive(std::fmt::Debug)] diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index ca1ebc9907..af4951c883 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -6,14 +6,19 @@ //! reacts to various mining-related messages received from a connected downstream miner. use super::super::mining_pool::Downstream; -use binary_sv2::Str0255; -use roles_logic_sv2::{ +use std::{ + convert::TryInto, + sync::{Arc, RwLock}, +}; +use stratum_common::roles_logic_sv2::{ + bitcoin::Amount, channels::server::{ error::{ExtendedChannelError, StandardChannelError}, extended::ExtendedChannel, share_accounting::{ShareValidationError, ShareValidationResult}, standard::StandardChannel, }, + codec_sv2::binary_sv2::Str0255, errors::Error, handlers::mining::{ParseMiningMessagesFromDownstream, SendTo, SupportedChannelTypes}, mining_sv2::*, @@ -21,11 +26,6 @@ use roles_logic_sv2::{ template_distribution_sv2::SubmitSolution, utils::Mutex, }; -use std::{ - convert::TryInto, - sync::{Arc, RwLock}, -}; -use stratum_common::bitcoin::Amount; use tracing::{error, info}; impl ParseMiningMessagesFromDownstream<()> for Downstream { diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 3de6b4e35b..87a195df89 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -26,23 +26,11 @@ use super::{ status, }; use async_channel::{Receiver, Sender}; -use binary_sv2::U256; -use codec_sv2::{HandshakeRole, Responder, StandardEitherFrame, StandardSv2Frame}; use config_helpers::CoinbaseOutputError; use error_handling::handle_result; use key_utils::SignatureService; -use network_helpers_sv2::noise_connection::Connection; use nohash_hasher::BuildNoHashHasher; -use roles_logic_sv2::{ - channels::server::{extended::ExtendedChannel, group::GroupChannel, standard::StandardChannel}, - common_properties::{CommonDownstreamData, IsDownstream, IsMiningDownstream}, - errors::Error, - handlers::mining::{ParseMiningMessagesFromDownstream, SendTo}, - mining_sv2::{ExtendedExtranonce, SetNewPrevHash as SetNewPrevHashMp, MAX_EXTRANONCE_LEN}, - parsers::{AnyMessage, Mining}, - template_distribution_sv2::{NewTemplate, SetNewPrevHash as SetNewPrevHashTdp, SubmitSolution}, - utils::{Id as IdFactory, Mutex}, -}; +use secp256k1; use std::{ collections::HashMap, convert::TryInto, @@ -50,8 +38,27 @@ use std::{ sync::{Arc, RwLock}, }; use stratum_common::{ - bitcoin::{Amount, ScriptBuf, TxOut}, - secp256k1, + network_helpers_sv2::noise_connection::Connection, + roles_logic_sv2::{ + self, + bitcoin::{Amount, ScriptBuf, TxOut}, + channels::server::{ + extended::ExtendedChannel, group::GroupChannel, standard::StandardChannel, + }, + codec_sv2, + codec_sv2::{ + binary_sv2::U256, HandshakeRole, Responder, StandardEitherFrame, StandardSv2Frame, + }, + common_properties::{CommonDownstreamData, IsDownstream, IsMiningDownstream}, + errors::Error, + handlers::mining::{ParseMiningMessagesFromDownstream, SendTo}, + mining_sv2::{ExtendedExtranonce, SetNewPrevHash as SetNewPrevHashMp, MAX_EXTRANONCE_LEN}, + parsers::{AnyMessage, Mining}, + template_distribution_sv2::{ + NewTemplate, SetNewPrevHash as SetNewPrevHashTdp, SubmitSolution, + }, + utils::{Id as IdFactory, Mutex}, + }, }; use tokio::{net::TcpListener, task}; use tracing::{debug, error, info, warn}; @@ -1120,15 +1127,15 @@ impl Pool { #[cfg(test)] mod test { - use binary_sv2::{B0255, B064K}; use ext_config::{Config, File, FileFormat}; use std::convert::TryInto; - use tracing::error; - - use stratum_common::{ - bitcoin, - bitcoin::{absolute::LockTime, consensus, transaction::Version, Transaction, Witness}, + use stratum_common::roles_logic_sv2::{ + bitcoin::{ + self, absolute::LockTime, consensus, transaction::Version, Transaction, Witness, + }, + codec_sv2::binary_sv2::{B0255, B064K}, }; + use tracing::error; use super::PoolConfig; diff --git a/roles/pool/src/lib/mining_pool/setup_connection.rs b/roles/pool/src/lib/mining_pool/setup_connection.rs index 3c7d2869d7..51ba8fb4c7 100644 --- a/roles/pool/src/lib/mining_pool/setup_connection.rs +++ b/roles/pool/src/lib/mining_pool/setup_connection.rs @@ -9,7 +9,9 @@ use super::super::{ mining_pool::{EitherFrame, StdFrame}, }; use async_channel::{Receiver, Sender}; -use roles_logic_sv2::{ +use std::{convert::TryInto, net::SocketAddr, sync::Arc}; +use stratum_common::roles_logic_sv2::{ + self, common_messages_sv2::{ has_requires_std_job, has_version_rolling, has_work_selection, SetupConnection, SetupConnectionSuccess, @@ -20,7 +22,6 @@ use roles_logic_sv2::{ parsers::{AnyMessage, CommonMessages}, utils::Mutex, }; -use std::{convert::TryInto, net::SocketAddr, sync::Arc}; use tracing::{debug, error, info}; /// Handles the `SetupConnection` message for downstream connections. diff --git a/roles/pool/src/lib/status.rs b/roles/pool/src/lib/status.rs index 58130c0cab..115b78948d 100644 --- a/roles/pool/src/lib/status.rs +++ b/roles/pool/src/lib/status.rs @@ -6,7 +6,7 @@ //! Centralizes and simplifies error handling across the system. /// Identifies which component sent a status update. -use roles_logic_sv2::parsers::Mining; +use stratum_common::roles_logic_sv2::{self, parsers::Mining}; use super::error::PoolError; diff --git a/roles/pool/src/lib/template_receiver/message_handler.rs b/roles/pool/src/lib/template_receiver/message_handler.rs index 2d611bf1e8..ab722a248c 100644 --- a/roles/pool/src/lib/template_receiver/message_handler.rs +++ b/roles/pool/src/lib/template_receiver/message_handler.rs @@ -3,14 +3,14 @@ //! Handles incoming template distribution messages from the Template Provider and forwards them //! as needed. use super::TemplateRx; -use roles_logic_sv2::{ +use std::sync::Arc; +use stratum_common::roles_logic_sv2::{ errors::Error, handlers::template_distribution::{ParseTemplateDistributionMessagesFromServer, SendTo}, parsers::TemplateDistribution, template_distribution_sv2::*, utils::Mutex, }; -use std::sync::Arc; use tracing::{debug, error, info}; impl ParseTemplateDistributionMessagesFromServer for TemplateRx { diff --git a/roles/pool/src/lib/template_receiver/mod.rs b/roles/pool/src/lib/template_receiver/mod.rs index 30c3014d9d..c8b6f9234d 100644 --- a/roles/pool/src/lib/template_receiver/mod.rs +++ b/roles/pool/src/lib/template_receiver/mod.rs @@ -13,19 +13,22 @@ use super::{ status, }; use async_channel::{Receiver, Sender}; -use codec_sv2::{HandshakeRole, Initiator}; use error_handling::handle_result; use key_utils::Secp256k1PublicKey; -use network_helpers_sv2::noise_connection::Connection; -use roles_logic_sv2::{ - handlers::template_distribution::ParseTemplateDistributionMessagesFromServer, - parsers::{AnyMessage, TemplateDistribution}, - template_distribution_sv2::{ - CoinbaseOutputConstraints, NewTemplate, SetNewPrevHash, SubmitSolution, +use std::{convert::TryInto, net::SocketAddr, sync::Arc}; +use stratum_common::{ + network_helpers_sv2::noise_connection::Connection, + roles_logic_sv2::{ + self, codec_sv2, + codec_sv2::{HandshakeRole, Initiator}, + handlers::template_distribution::ParseTemplateDistributionMessagesFromServer, + parsers::{AnyMessage, TemplateDistribution}, + template_distribution_sv2::{ + CoinbaseOutputConstraints, NewTemplate, SetNewPrevHash, SubmitSolution, + }, + utils::Mutex, }, - utils::Mutex, }; -use std::{convert::TryInto, net::SocketAddr, sync::Arc}; use tokio::{net::TcpStream, task}; use tracing::{info, warn}; diff --git a/roles/pool/src/lib/template_receiver/setup_connection.rs b/roles/pool/src/lib/template_receiver/setup_connection.rs index 59561dd933..1c176c7786 100644 --- a/roles/pool/src/lib/template_receiver/setup_connection.rs +++ b/roles/pool/src/lib/template_receiver/setup_connection.rs @@ -8,14 +8,15 @@ use super::super::{ mining_pool::{EitherFrame, StdFrame}, }; use async_channel::{Receiver, Sender}; -use roles_logic_sv2::{ +use std::{convert::TryInto, net::SocketAddr, sync::Arc}; +use stratum_common::roles_logic_sv2::{ + self, codec_sv2, common_messages_sv2::{Protocol, Reconnect, SetupConnection, SetupConnectionError}, errors::Error, handlers::common::{ParseCommonMessagesFromUpstream, SendTo}, parsers::{AnyMessage, CommonMessages}, utils::Mutex, }; -use std::{convert::TryInto, net::SocketAddr, sync::Arc}; use tracing::{error, info}; /// Handles the connection setup process with the Template Provider. diff --git a/roles/roles-utils/network-helpers/Cargo.toml b/roles/roles-utils/network-helpers/Cargo.toml index 7df0426cf0..e8cfe039b8 100644 --- a/roles/roles-utils/network-helpers/Cargo.toml +++ b/roles/roles-utils/network-helpers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "network_helpers_sv2" -version = "3.1.0" +version = "4.0.0" authors = ["The Stratum V2 Developers"] edition = "2018" description = "Networking utils for SV2 roles" @@ -17,9 +17,7 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] async-std = { version = "1.8.0", optional = true } async-channel = { version = "1.8.0", optional = true } tokio = { version = "1.44.1", features = ["full"] } -binary_sv2 = { path = "../../../protocols/v2/binary-sv2", version = "^3.0.0", optional = true } codec_sv2 = { path = "../../../protocols/v2/codec-sv2", version = "^2.0.0", features=["noise_sv2"], optional = true } -stratum-common = { path = "../../../common" } sv1_api = { path = "../../../protocols/v1/", version = "^1.0.0", optional = true } tracing = { version = "0.1" } futures = "0.3.28" @@ -27,7 +25,7 @@ tokio-util = { version = "0.7.10", default-features = false, features = ["codec" serde_json = { version = "1.0.138", default-features = false, optional = true } [features] -default = ["async-channel", "binary_sv2", "codec_sv2"] +default = ["async-channel", "codec_sv2"] with_buffer_pool = ["codec_sv2/with_buffer_pool"] sv1 = ["sv1_api", "tokio-util", "serde_json"] diff --git a/roles/roles-utils/network-helpers/src/lib.rs b/roles/roles-utils/network-helpers/src/lib.rs index 0447549872..10da0c1cd9 100644 --- a/roles/roles-utils/network-helpers/src/lib.rs +++ b/roles/roles-utils/network-helpers/src/lib.rs @@ -6,6 +6,8 @@ pub mod sv1_connection; use async_channel::{RecvError, SendError}; use codec_sv2::Error as CodecError; +pub use codec_sv2; + #[derive(Debug)] pub enum Error { HandshakeRemoteInvalidMessage, diff --git a/roles/roles-utils/network-helpers/src/noise_connection.rs b/roles/roles-utils/network-helpers/src/noise_connection.rs index 04fce8d1fd..69f3e944e2 100644 --- a/roles/roles-utils/network-helpers/src/noise_connection.rs +++ b/roles/roles-utils/network-helpers/src/noise_connection.rs @@ -1,8 +1,8 @@ #![allow(clippy::new_ret_no_self)] use crate::Error; use async_channel::{unbounded, Receiver, Sender}; -use binary_sv2::{Deserialize, GetSize, Serialize}; use codec_sv2::{ + binary_sv2::{Deserialize, GetSize, Serialize}, noise_sv2::{ELLSWIFT_ENCODING_SIZE, INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE}, HandShakeFrame, HandshakeRole, StandardEitherFrame, StandardNoiseDecoder, State, }; diff --git a/roles/roles-utils/network-helpers/src/plain_connection.rs b/roles/roles-utils/network-helpers/src/plain_connection.rs index a269f4424a..e70e8e9a40 100644 --- a/roles/roles-utils/network-helpers/src/plain_connection.rs +++ b/roles/roles-utils/network-helpers/src/plain_connection.rs @@ -1,5 +1,5 @@ use async_channel::{bounded, Receiver, Sender}; -use binary_sv2::{Deserialize, Serialize}; +use codec_sv2::binary_sv2::{Deserialize, Serialize}; use core::convert::TryInto; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, @@ -7,8 +7,7 @@ use tokio::{ task, }; -use binary_sv2::GetSize; -use codec_sv2::{Error::MissingBytes, StandardDecoder, StandardEitherFrame}; +use codec_sv2::{binary_sv2::GetSize, Error::MissingBytes, StandardDecoder, StandardEitherFrame}; use tracing::{error, trace}; #[derive(Debug)] diff --git a/roles/roles-utils/rpc/Cargo.toml b/roles/roles-utils/rpc/Cargo.toml index ea23134654..9d6eb60512 100644 --- a/roles/roles-utils/rpc/Cargo.toml +++ b/roles/roles-utils/rpc/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -stratum-common = { path = "../../../common", features=["bitcoin"], version = "^2.0.0" } +stratum-common = { path = "../../../common" } serde = { version = "1.0.89", features = ["derive", "alloc"], default-features = false } serde_json = { version = "1.0", default-features = false, features = ["alloc","raw_value"] } hex = "0.4.3" diff --git a/roles/roles-utils/rpc/src/mini_rpc_client.rs b/roles/roles-utils/rpc/src/mini_rpc_client.rs index aa7e59aa5c..95bfb62651 100644 --- a/roles/roles-utils/rpc/src/mini_rpc_client.rs +++ b/roles/roles-utils/rpc/src/mini_rpc_client.rs @@ -14,7 +14,9 @@ use hyper_util::{ }; use serde::{Deserialize, Serialize}; use serde_json::json; -use stratum_common::bitcoin::{consensus::encode::deserialize as consensus_decode, Transaction}; +use stratum_common::roles_logic_sv2::bitcoin::{ + consensus::encode::deserialize as consensus_decode, Transaction, +}; use super::BlockHash; diff --git a/roles/test-utils/mining-device-sv1/Cargo.toml b/roles/test-utils/mining-device-sv1/Cargo.toml index 69cc37441a..b79541552e 100644 --- a/roles/test-utils/mining-device-sv1/Cargo.toml +++ b/roles/test-utils/mining-device-sv1/Cargo.toml @@ -18,9 +18,8 @@ name = "mining_device_sv1" path = "src/lib.rs" [dependencies] -stratum-common = { path = "../../../common" } +stratum-common = { path = "../../../common" } async-channel = "1.5.1" -roles_logic_sv2 = { path = "../../../protocols/v2/roles-logic-sv2" } serde = { version = "1.0.89", default-features = false, features = ["derive", "alloc"] } serde_json = { version = "1.0.64", default-features = false, features = ["alloc"] } v1 = { path="../../../protocols/v1", package="sv1_api" } diff --git a/roles/test-utils/mining-device-sv1/src/client.rs b/roles/test-utils/mining-device-sv1/src/client.rs index 3b6ff89218..b26e532d49 100644 --- a/roles/test-utils/mining-device-sv1/src/client.rs +++ b/roles/test-utils/mining-device-sv1/src/client.rs @@ -3,7 +3,6 @@ use async_channel::{unbounded, Receiver, Sender}; use num_bigint::BigUint; use num_traits::FromPrimitive; use primitive_types::U256; -use roles_logic_sv2::utils::Mutex; use std::{ convert::TryInto, net::SocketAddr, @@ -11,6 +10,7 @@ use std::{ sync::Arc, time::{self, Duration}, }; +use stratum_common::roles_logic_sv2::utils::Mutex; use tokio::{ io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, net::TcpStream, diff --git a/roles/test-utils/mining-device-sv1/src/job.rs b/roles/test-utils/mining-device-sv1/src/job.rs index 1d6b3d2bcd..ead04c5909 100644 --- a/roles/test-utils/mining-device-sv1/src/job.rs +++ b/roles/test-utils/mining-device-sv1/src/job.rs @@ -1,4 +1,5 @@ use std::convert::TryInto; +use stratum_common::roles_logic_sv2; use v1::server_to_client; /// Represents a new Job built from an incoming `mining.notify` message from the Upstream server. diff --git a/roles/test-utils/mining-device-sv1/src/miner.rs b/roles/test-utils/mining-device-sv1/src/miner.rs index ecd31eb496..45585421ef 100644 --- a/roles/test-utils/mining-device-sv1/src/miner.rs +++ b/roles/test-utils/mining-device-sv1/src/miner.rs @@ -1,7 +1,7 @@ use crate::job::Job; use primitive_types::U256; use std::convert::TryInto; -use stratum_common::bitcoin::{ +use stratum_common::roles_logic_sv2::bitcoin::{ blockdata::block::{Header, Version}, hash_types::{BlockHash, TxMerkleNode}, hashes::{sha256d::Hash as DHash, Hash}, diff --git a/roles/test-utils/mining-device/Cargo.toml b/roles/test-utils/mining-device/Cargo.toml index a9d6e386e9..87a4f6aab6 100644 --- a/roles/test-utils/mining-device/Cargo.toml +++ b/roles/test-utils/mining-device/Cargo.toml @@ -20,13 +20,9 @@ path = "src/lib/mod.rs" [dependencies] -codec_sv2 = { path = "../../../protocols/v2/codec-sv2", features=["noise_sv2"] } -roles_logic_sv2 = { path = "../../../protocols/v2/roles-logic-sv2" } -stratum-common = { path = "../../../common" } +stratum-common = { path = "../../../common", features = ["with_network_helpers"] } async-channel = "1.5.1" -binary_sv2 = { path = "../../../protocols/v2/binary-sv2" } -network_helpers_sv2 = { path = "../../roles-utils/network-helpers" } -buffer_sv2 = { path = "../../../utils/buffer"} +buffer_sv2 = { path = "../../../utils/buffer" } async-recursion = "0.3.2" rand = "0.8.4" futures = "0.3.5" diff --git a/roles/test-utils/mining-device/src/lib/mod.rs b/roles/test-utils/mining-device/src/lib/mod.rs index ea17b8ed4f..e78a97384f 100644 --- a/roles/test-utils/mining-device/src/lib/mod.rs +++ b/roles/test-utils/mining-device/src/lib/mod.rs @@ -1,21 +1,8 @@ #![allow(clippy::option_map_unit_fn)] use async_channel::{Receiver, Sender}; -use codec_sv2::{Initiator, StandardEitherFrame, StandardSv2Frame}; use key_utils::Secp256k1PublicKey; -use network_helpers_sv2::noise_connection::Connection; use primitive_types::U256; use rand::{thread_rng, Rng}; -use roles_logic_sv2::{ - common_messages_sv2::{Protocol, SetupConnection, SetupConnectionSuccess}, - errors::Error, - handlers::{ - common::ParseCommonMessagesFromUpstream, - mining::{ParseMiningMessagesFromUpstream, SendTo, SupportedChannelTypes}, - }, - mining_sv2::*, - parsers::{Mining, MiningDeviceMessages}, - utils::{Id, Mutex}, -}; use std::{ net::{SocketAddr, ToSocketAddrs}, sync::{ @@ -25,8 +12,23 @@ use std::{ thread::available_parallelism, time::{Duration, Instant}, }; -use stratum_common::bitcoin::{ - blockdata::block::Header, hash_types::BlockHash, hashes::Hash, CompactTarget, +use stratum_common::{ + network_helpers_sv2::noise_connection::Connection, + roles_logic_sv2::{ + self, + bitcoin::{blockdata::block::Header, hash_types::BlockHash, hashes::Hash, CompactTarget}, + codec_sv2, + codec_sv2::{Initiator, StandardEitherFrame, StandardSv2Frame}, + common_messages_sv2::{Protocol, SetupConnection, SetupConnectionSuccess}, + errors::Error, + handlers::{ + common::ParseCommonMessagesFromUpstream, + mining::{ParseMiningMessagesFromUpstream, SendTo, SupportedChannelTypes}, + }, + mining_sv2::*, + parsers::{Mining, MiningDeviceMessages}, + utils::{Id, Mutex}, + }, }; use tokio::net::TcpStream; use tracing::{debug, error, info}; @@ -92,9 +94,8 @@ pub type StdFrame = StandardSv2Frame; pub type EitherFrame = StandardEitherFrame; struct SetupConnectionHandler {} -use roles_logic_sv2::common_messages_sv2::Reconnect; use std::convert::TryInto; -use stratum_common::bitcoin::block::Version; +use stratum_common::roles_logic_sv2::{bitcoin::block::Version, common_messages_sv2::Reconnect}; impl SetupConnectionHandler { pub fn new() -> Self { diff --git a/roles/translator/Cargo.toml b/roles/translator/Cargo.toml index 302c5bb64b..714ddafaf4 100644 --- a/roles/translator/Cargo.toml +++ b/roles/translator/Cargo.toml @@ -20,16 +20,11 @@ name = "translator_sv2" path = "src/main.rs" [dependencies] -stratum-common = { path = "../../common" } +stratum-common = { path = "../../common", features = ["with_network_helpers"] } async-channel = "1.5.1" async-recursion = "0.3.2" -binary_sv2 = { path = "../../protocols/v2/binary-sv2" } buffer_sv2 = { path = "../../utils/buffer" } -codec_sv2 = { path = "../../protocols/v2/codec-sv2", features = ["noise_sv2", "with_buffer_pool"] } -framing_sv2 = { path = "../../protocols/v2/framing-sv2" } -network_helpers_sv2 = { path = "../roles-utils/network-helpers", features=["with_buffer_pool"] } once_cell = "1.12.0" -roles_logic_sv2 = { path = "../../protocols/v2/roles-logic-sv2" } serde = { version = "1.0.89", default-features = false, features = ["derive", "alloc"] } serde_json = { version = "1.0.64", default-features = false, features = ["alloc"] } futures = "0.3.25" diff --git a/roles/translator/src/lib/downstream_sv1/diff_management.rs b/roles/translator/src/lib/downstream_sv1/diff_management.rs index 6831bf63d9..e6b6c9c178 100644 --- a/roles/translator/src/lib/downstream_sv1/diff_management.rs +++ b/roles/translator/src/lib/downstream_sv1/diff_management.rs @@ -14,11 +14,12 @@ use super::{Downstream, DownstreamMessages, SetDownstreamTarget}; use super::super::error::{Error, ProxyResult}; use primitive_types::U256; -use roles_logic_sv2::{ +use std::{ops::Div, sync::Arc}; +use stratum_common::roles_logic_sv2::{ + codec_sv2::binary_sv2, mining_sv2::Target, utils::{hash_rate_to_target, Mutex}, }; -use std::{ops::Div, sync::Arc}; use tracing::debug; use v1::json_rpc; @@ -243,14 +244,18 @@ mod test { use crate::config::{DownstreamDifficultyConfig, UpstreamDifficultyConfig}; use async_channel::unbounded; - use binary_sv2::U256; use rand::{thread_rng, Rng}; - use roles_logic_sv2::{mining_sv2::Target, utils::Mutex}; use sha2::{Digest, Sha256}; use std::{ sync::Arc, time::{Duration, Instant}, }; + use stratum_common::roles_logic_sv2::{ + self, + codec_sv2::binary_sv2::{self, U256}, + mining_sv2::Target, + utils::Mutex, + }; use crate::downstream_sv1::Downstream; diff --git a/roles/translator/src/lib/downstream_sv1/downstream.rs b/roles/translator/src/lib/downstream_sv1/downstream.rs index 48cba8970b..7ccbcc9cd3 100644 --- a/roles/translator/src/lib/downstream_sv1/downstream.rs +++ b/roles/translator/src/lib/downstream_sv1/downstream.rs @@ -36,7 +36,8 @@ use tokio::{ use super::{kill, DownstreamMessages, SubmitShareWithChannelId, SUBSCRIBE_TIMEOUT_SECS}; -use roles_logic_sv2::{ +use stratum_common::roles_logic_sv2::{ + self, common_properties::{IsDownstream, IsMiningDownstream}, mining_sv2::Target, utils::{hash_rate_to_target, Mutex}, @@ -723,8 +724,8 @@ impl IsDownstream for Downstream { #[cfg(test)] mod tests { - use binary_sv2::U256; use roles_logic_sv2::mining_sv2::Target; + use stratum_common::roles_logic_sv2::codec_sv2::binary_sv2::U256; use super::*; diff --git a/roles/translator/src/lib/downstream_sv1/mod.rs b/roles/translator/src/lib/downstream_sv1/mod.rs index f0847acb92..a6190e911f 100644 --- a/roles/translator/src/lib/downstream_sv1/mod.rs +++ b/roles/translator/src/lib/downstream_sv1/mod.rs @@ -11,7 +11,7 @@ //! - [`diff_management`]: (Declared here, likely contains downstream difficulty logic) //! - [`downstream`]: Defines the core [`Downstream`] struct and its functionalities. -use roles_logic_sv2::mining_sv2::Target; +use stratum_common::roles_logic_sv2::mining_sv2::Target; use v1::{client_to_server::Submit, utils::HexU32Be}; pub mod diff_management; pub mod downstream; diff --git a/roles/translator/src/lib/error.rs b/roles/translator/src/lib/error.rs index 03c6ff7ea6..18f1cc3611 100644 --- a/roles/translator/src/lib/error.rs +++ b/roles/translator/src/lib/error.rs @@ -8,14 +8,15 @@ //! - A specific `ChannelSendError` enum for errors occurring during message sending over //! asynchronous channels. -use codec_sv2::Frame; use ext_config::ConfigError; -use roles_logic_sv2::{ +use std::{fmt, sync::PoisonError}; +use stratum_common::roles_logic_sv2::{ + self, + codec_sv2::{self, binary_sv2, framing_sv2, Frame}, mining_sv2::{ExtendedExtranonce, NewExtendedMiningJob, SetCustomMiningJob}, parsers::{AnyMessage, Mining}, vardiff::error::VardiffError, }; -use std::{fmt, sync::PoisonError}; use v1::server_to_client::{Notify, SetDifficulty}; pub type ProxyResult<'a, T> = core::result::Result>; diff --git a/roles/translator/src/lib/mod.rs b/roles/translator/src/lib/mod.rs index 26eca7dc25..dc5527fe7a 100644 --- a/roles/translator/src/lib/mod.rs +++ b/roles/translator/src/lib/mod.rs @@ -13,13 +13,13 @@ use async_channel::{bounded, unbounded}; use futures::FutureExt; use rand::Rng; -pub use roles_logic_sv2::utils::Mutex; use status::Status; use std::{ net::{IpAddr, SocketAddr}, str::FromStr, sync::Arc, }; +pub use stratum_common::roles_logic_sv2::utils::Mutex; use tokio::{ select, diff --git a/roles/translator/src/lib/proxy/bridge.rs b/roles/translator/src/lib/proxy/bridge.rs index 5790d31d5a..65e6f11cf4 100644 --- a/roles/translator/src/lib/proxy/bridge.rs +++ b/roles/translator/src/lib/proxy/bridge.rs @@ -27,7 +27,8 @@ use super::super::{ }; use async_channel::{Receiver, Sender}; use error_handling::handle_result; -use roles_logic_sv2::{ +use std::sync::Arc; +use stratum_common::roles_logic_sv2::{ channel_logic::channel_factory::{ ExtendedChannelKind, OnNewShare, ProxyExtendedChannelFactory, Share, }, @@ -38,7 +39,6 @@ use roles_logic_sv2::{ utils::{GroupId, Mutex}, Error as RolesLogicError, }; -use std::sync::Arc; use tokio::{sync::broadcast, task::AbortHandle}; use tracing::{debug, error, info, warn}; use v1::{client_to_server::Submit, server_to_client, utils::HexU32Be}; @@ -588,7 +588,10 @@ pub struct OpenSv1Downstream { mod test { use super::*; use async_channel::bounded; - use stratum_common::bitcoin::{absolute::LockTime, consensus, transaction::Version}; + use stratum_common::roles_logic_sv2::{ + bitcoin::{absolute::LockTime, consensus, transaction::Version}, + codec_sv2::binary_sv2, + }; pub mod test_utils { use super::*; @@ -654,9 +657,8 @@ mod test { #[test] fn test_version_bits_insert() { - use stratum_common::{ - bitcoin, - bitcoin::{blockdata::witness::Witness, hashes::Hash}, + use stratum_common::roles_logic_sv2::bitcoin::{ + self, blockdata::witness::Witness, hashes::Hash, }; let extranonces = ExtendedExtranonce::new(0..6, 6..8, 8..16, None) diff --git a/roles/translator/src/lib/proxy/next_mining_notify.rs b/roles/translator/src/lib/proxy/next_mining_notify.rs index 14abc715ef..e9a4d08627 100644 --- a/roles/translator/src/lib/proxy/next_mining_notify.rs +++ b/roles/translator/src/lib/proxy/next_mining_notify.rs @@ -1,6 +1,6 @@ //! Provides functionality to convert Stratum V2 job into a //! Stratum V1 `mining.notify` message. -use roles_logic_sv2::{ +use stratum_common::roles_logic_sv2::{ job_creator::extended_job_to_non_segwit, mining_sv2::{NewExtendedMiningJob, SetNewPrevHash}, }; diff --git a/roles/translator/src/lib/status.rs b/roles/translator/src/lib/status.rs index 879697bdf2..083a161a74 100644 --- a/roles/translator/src/lib/status.rs +++ b/roles/translator/src/lib/status.rs @@ -8,6 +8,8 @@ //! //! This allows for centralized, consistent error handling across the application. +use stratum_common::roles_logic_sv2; + use crate::error::{self, Error}; /// Identifies the component that originated a [`Status`] update. diff --git a/roles/translator/src/lib/upstream_sv2/diff_management.rs b/roles/translator/src/lib/upstream_sv2/diff_management.rs index 941f55123f..8da5f33b60 100644 --- a/roles/translator/src/lib/upstream_sv2/diff_management.rs +++ b/roles/translator/src/lib/upstream_sv2/diff_management.rs @@ -14,11 +14,11 @@ use super::super::{ error::ProxyResult, upstream_sv2::{EitherFrame, Message, StdFrame}, }; -use binary_sv2::U256; -use roles_logic_sv2::{ - mining_sv2::UpdateChannel, parsers::Mining, utils::Mutex, Error as RolesLogicError, -}; use std::{sync::Arc, time::Duration}; +use stratum_common::roles_logic_sv2::{ + codec_sv2::binary_sv2::U256, mining_sv2::UpdateChannel, parsers::Mining, utils::Mutex, + Error as RolesLogicError, +}; impl Upstream { /// Attempts to update the upstream channel's nominal hashrate if the configured diff --git a/roles/translator/src/lib/upstream_sv2/mod.rs b/roles/translator/src/lib/upstream_sv2/mod.rs index 64f24acd32..3ade09a11d 100644 --- a/roles/translator/src/lib/upstream_sv2/mod.rs +++ b/roles/translator/src/lib/upstream_sv2/mod.rs @@ -8,8 +8,10 @@ //! - [`upstream_connection`]: Handles the underlying connection details and frame //! sending/receiving. -use codec_sv2::{StandardEitherFrame, StandardSv2Frame}; -use roles_logic_sv2::parsers::AnyMessage; +use stratum_common::roles_logic_sv2::{ + codec_sv2::{StandardEitherFrame, StandardSv2Frame}, + parsers::AnyMessage, +}; pub mod diff_management; pub mod upstream; diff --git a/roles/translator/src/lib/upstream_sv2/upstream.rs b/roles/translator/src/lib/upstream_sv2/upstream.rs index 841daf05e5..d7d3c011aa 100644 --- a/roles/translator/src/lib/upstream_sv2/upstream.rs +++ b/roles/translator/src/lib/upstream_sv2/upstream.rs @@ -28,31 +28,33 @@ use crate::{ upstream_sv2::{EitherFrame, Message, StdFrame, UpstreamConnection}, }; use async_channel::{Receiver, Sender}; -use binary_sv2::u256_from_int; -use codec_sv2::{HandshakeRole, Initiator}; use error_handling::handle_result; use key_utils::Secp256k1PublicKey; -use network_helpers_sv2::noise_connection::Connection; -use roles_logic_sv2::{ - common_messages_sv2::{Protocol, SetupConnection}, - common_properties::{IsMiningUpstream, IsUpstream}, - handlers::{ - common::{ParseCommonMessagesFromUpstream, SendTo as SendToCommon}, - mining::{ParseMiningMessagesFromUpstream, SendTo}, - }, - mining_sv2::{ - ExtendedExtranonce, Extranonce, NewExtendedMiningJob, OpenExtendedMiningChannel, - SetNewPrevHash, SubmitSharesExtended, - }, - parsers::Mining, - utils::Mutex, - Error as RolesLogicError, - Error::NoUpstreamsConnected, -}; use std::{ net::SocketAddr, sync::{atomic::AtomicBool, Arc}, }; +use stratum_common::{ + network_helpers_sv2::noise_connection::Connection, + roles_logic_sv2::{ + self, + codec_sv2::{self, binary_sv2::u256_from_int, framing_sv2, HandshakeRole, Initiator}, + common_messages_sv2::{Protocol, SetupConnection}, + common_properties::{IsMiningUpstream, IsUpstream}, + handlers::{ + common::{ParseCommonMessagesFromUpstream, SendTo as SendToCommon}, + mining::{ParseMiningMessagesFromUpstream, SendTo}, + }, + mining_sv2::{ + ExtendedExtranonce, Extranonce, NewExtendedMiningJob, OpenExtendedMiningChannel, + SetNewPrevHash, SubmitSharesExtended, + }, + parsers::Mining, + utils::Mutex, + Error as RolesLogicError, + Error::NoUpstreamsConnected, + }, +}; use tokio::{ net::TcpStream, task::AbortHandle, @@ -60,11 +62,10 @@ use tokio::{ }; use tracing::{debug, error, info, warn}; -use roles_logic_sv2::{ - common_messages_sv2::Reconnect, handlers::mining::SupportedChannelTypes, +use stratum_common::roles_logic_sv2::{ + bitcoin::BlockHash, common_messages_sv2::Reconnect, handlers::mining::SupportedChannelTypes, mining_sv2::SetGroupChannel, }; -use stratum_common::bitcoin::BlockHash; /// Atomic boolean flag used for synchronization between receiving a new job /// and handling a new previous hash. Indicates whether a `NewExtendedMiningJob` diff --git a/test/integration-tests/Cargo.lock b/test/integration-tests/Cargo.lock index a59069cd4a..ceb5385fd1 100644 --- a/test/integration-tests/Cargo.lock +++ b/test/integration-tests/Cargo.lock @@ -515,7 +515,6 @@ dependencies = [ "framing_sv2", "noise_sv2", "rand 0.8.5", - "stratum-common", "tracing", ] @@ -530,7 +529,6 @@ name = "common_messages_sv2" version = "5.1.0" dependencies = [ "binary_sv2", - "stratum-common", ] [[package]] @@ -831,7 +829,6 @@ dependencies = [ "binary_sv2", "buffer_sv2", "noise_sv2", - "stratum-common", ] [[package]] @@ -1204,8 +1201,6 @@ name = "integration_tests_sv2" version = "0.1.0" dependencies = [ "async-channel", - "binary_sv2", - "codec_sv2", "config-helpers", "corepc-node", "flate2", @@ -1216,11 +1211,9 @@ dependencies = [ "mining_device_sv1", "mining_proxy_sv2", "minreq", - "network_helpers_sv2", "once_cell", "pool_sv2", "rand 0.9.0", - "roles_logic_sv2", "stratum-common", "sv1_api", "tar", @@ -1248,20 +1241,16 @@ version = "0.1.4" dependencies = [ "async-channel", "async-recursion 0.3.2", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "config", "config-helpers", "error_handling", - "framing_sv2", "futures", "key-utils", - "network_helpers_sv2", "nohash-hasher", "primitive-types", - "roles_logic_sv2", + "secp256k1 0.28.2", "serde", "stratum-common", "tokio", @@ -1274,21 +1263,16 @@ name = "jd_server" version = "0.1.3" dependencies = [ "async-channel", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "config", "config-helpers", "error_handling", "hashbrown 0.11.2", "hex", "key-utils", - "network_helpers_sv2", "nohash-hasher", - "noise_sv2", "rand 0.8.5", - "roles_logic_sv2", "rpc_sv2", "serde", "serde_json", @@ -1303,7 +1287,6 @@ name = "job_declaration_sv2" version = "4.0.0" dependencies = [ "binary_sv2", - "stratum-common", ] [[package]] @@ -1403,16 +1386,12 @@ version = "0.1.3" dependencies = [ "async-channel", "async-recursion 0.3.2", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "futures", "key-utils", - "network_helpers_sv2", "primitive-types", "rand 0.8.5", - "roles_logic_sv2", "sha2 0.10.8", "stratum-common", "tokio", @@ -1428,7 +1407,6 @@ dependencies = [ "num-bigint", "num-traits", "primitive-types", - "roles_logic_sv2", "serde", "serde_json", "stratum-common", @@ -1444,17 +1422,13 @@ version = "0.1.3" dependencies = [ "async-channel", "async-recursion 0.3.2", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "config", "futures", "key-utils", - "network_helpers_sv2", "nohash-hasher", "once_cell", - "roles_logic_sv2", "serde", "stratum-common", "tokio", @@ -1467,7 +1441,6 @@ name = "mining_sv2" version = "4.0.0" dependencies = [ "binary_sv2", - "stratum-common", ] [[package]] @@ -1517,14 +1490,12 @@ dependencies = [ [[package]] name = "network_helpers_sv2" -version = "3.1.0" +version = "4.0.0" dependencies = [ "async-channel", - "binary_sv2", "codec_sv2", "futures", "serde_json", - "stratum-common", "sv1_api", "tokio", "tokio-util", @@ -1546,7 +1517,6 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "secp256k1 0.28.2", - "stratum-common", ] [[package]] @@ -1777,19 +1747,15 @@ version = "0.1.3" dependencies = [ "async-channel", "async-recursion 1.1.1", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "config", "config-helpers", "error_handling", "key-utils", - "network_helpers_sv2", "nohash-hasher", - "noise_sv2", "rand 0.8.5", - "roles_logic_sv2", + "secp256k1 0.28.2", "serde", "stratum-common", "tokio", @@ -1938,16 +1904,15 @@ dependencies = [ name = "roles_logic_sv2" version = "3.0.0" dependencies = [ - "binary_sv2", + "bitcoin", "chacha20poly1305", + "codec_sv2", "common_messages_sv2", - "framing_sv2", "hex-conservative 0.3.0", "job_declaration_sv2", "mining_sv2", "nohash-hasher", "primitive-types", - "stratum-common", "template_distribution_sv2", "tracing", ] @@ -2231,10 +2196,10 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stratum-common" -version = "2.0.0" +version = "3.0.0" dependencies = [ - "bitcoin", - "secp256k1 0.28.2", + "network_helpers_sv2", + "roles_logic_sv2", ] [[package]] @@ -2318,7 +2283,6 @@ name = "template_distribution_sv2" version = "3.1.0" dependencies = [ "binary_sv2", - "stratum-common", ] [[package]] @@ -2506,20 +2470,15 @@ version = "1.0.0" dependencies = [ "async-channel", "async-recursion 0.3.2", - "binary_sv2", "buffer_sv2", "clap", - "codec_sv2", "config", "error_handling", - "framing_sv2", "futures", "key-utils", - "network_helpers_sv2", "once_cell", "primitive-types", "rand 0.8.5", - "roles_logic_sv2", "serde", "serde_json", "stratum-common", diff --git a/test/integration-tests/Cargo.toml b/test/integration-tests/Cargo.toml index e2e7ce470f..e64b6d2f2b 100644 --- a/test/integration-tests/Cargo.toml +++ b/test/integration-tests/Cargo.toml @@ -22,19 +22,15 @@ tokio = { version="1.44.1", default-features = false, features = ["tracing"] } tracing = { version = "0.1.41", default-features = false } tracing-subscriber = { version = "0.3.19", default-features = false } -binary_sv2 = { path = "../../protocols/v2/binary-sv2" } -codec_sv2 = { path = "../../protocols/v2/codec-sv2", features = ["noise_sv2"] } jd_client = { path = "../../roles/jd-client" } jd_server = { path = "../../roles/jd-server" } key-utils = { path = "../../utils/key-utils" } mining_device = { path = "../../roles/test-utils/mining-device" } mining_device_sv1 = { path = "../../roles/test-utils/mining-device-sv1" } mining_proxy_sv2 = { path = "../../roles/mining-proxy" } -network_helpers_sv2 = { path = "../../roles/roles-utils/network-helpers", features = ["with_buffer_pool"] } pool_sv2 = { path = "../../roles/pool" } -roles_logic_sv2 = { path = "../../protocols/v2/roles-logic-sv2" } -stratum-common = { path = "../../common" } config-helpers = { path = "../../roles/roles-utils/config-helpers" } +stratum-common = { path = "../../common" , features = ["with_network_helpers", "sv1"]} translator_sv2 = { path = "../../roles/translator" } sv1_api = { path = "../../protocols/v1", optional = true } @@ -43,4 +39,4 @@ path = "lib/mod.rs" [features] default = [] -sv1 = ["sv1_api", "network_helpers_sv2/sv1"] +sv1 = ["sv1_api", "stratum-common/sv1"] diff --git a/test/integration-tests/lib/interceptor.rs b/test/integration-tests/lib/interceptor.rs index bdf93ddab4..fd9686750c 100644 --- a/test/integration-tests/lib/interceptor.rs +++ b/test/integration-tests/lib/interceptor.rs @@ -1,5 +1,5 @@ use crate::types::MsgType; -use roles_logic_sv2::parsers::AnyMessage; +use stratum_common::roles_logic_sv2::parsers::AnyMessage; #[derive(Debug, Clone, PartialEq, Eq)] pub enum MessageDirection { diff --git a/test/integration-tests/lib/message_aggregator.rs b/test/integration-tests/lib/message_aggregator.rs index 1bcd9d2f91..d5b17159c9 100644 --- a/test/integration-tests/lib/message_aggregator.rs +++ b/test/integration-tests/lib/message_aggregator.rs @@ -1,5 +1,5 @@ -use roles_logic_sv2::{parsers::AnyMessage, utils::Mutex}; use std::{collections::VecDeque, sync::Arc}; +use stratum_common::roles_logic_sv2::{parsers::AnyMessage, utils::Mutex}; use crate::types::MsgType; diff --git a/test/integration-tests/lib/mock_roles.rs b/test/integration-tests/lib/mock_roles.rs index 495e39a45b..59a2d1e36a 100644 --- a/test/integration-tests/lib/mock_roles.rs +++ b/test/integration-tests/lib/mock_roles.rs @@ -4,9 +4,11 @@ use crate::{ utils::{create_downstream, create_upstream, message_from_frame, wait_for_client}, }; use async_channel::Sender; -use codec_sv2::{StandardEitherFrame, Sv2Frame}; -use roles_logic_sv2::parsers::AnyMessage; use std::net::SocketAddr; +use stratum_common::roles_logic_sv2::{ + codec_sv2::{StandardEitherFrame, Sv2Frame}, + parsers::AnyMessage, +}; use tokio::net::TcpStream; pub struct MockDownstream { @@ -107,12 +109,12 @@ impl MockUpstream { mod tests { use super::*; use crate::start_template_provider; - use codec_sv2::{StandardEitherFrame, Sv2Frame}; - use roles_logic_sv2::{ + use std::{convert::TryInto, net::TcpListener}; + use stratum_common::roles_logic_sv2::{ + codec_sv2::{StandardEitherFrame, Sv2Frame}, common_messages_sv2::{Protocol, SetupConnection, SetupConnectionSuccess, *}, parsers::CommonMessages, }; - use std::{convert::TryInto, net::TcpListener}; #[tokio::test] async fn test_mock_downstream() { diff --git a/test/integration-tests/lib/mod.rs b/test/integration-tests/lib/mod.rs index 0ab57bb9f1..e3dd2a18fb 100644 --- a/test/integration-tests/lib/mod.rs +++ b/test/integration-tests/lib/mod.rs @@ -273,7 +273,7 @@ pub fn start_sv2_translator(upstream: SocketAddr) -> (TranslatorSv2, SocketAddr) } pub fn measure_hashrate(duration_secs: u64) -> f64 { - use stratum_common::bitcoin::hashes::{sha256d, Hash, HashEngine}; + use stratum_common::roles_logic_sv2::bitcoin::hashes::{sha256d, Hash, HashEngine}; let mut share = { let mut rng = rng(); diff --git a/test/integration-tests/lib/sniffer.rs b/test/integration-tests/lib/sniffer.rs index 16b7b194e6..a742d050e5 100644 --- a/test/integration-tests/lib/sniffer.rs +++ b/test/integration-tests/lib/sniffer.rs @@ -7,8 +7,8 @@ use crate::{ wait_for_client, }, }; -use roles_logic_sv2::parsers::AnyMessage; use std::net::SocketAddr; +use stratum_common::roles_logic_sv2::parsers::AnyMessage; use tokio::{net::TcpStream, select}; /// Allows to intercept messages sent between two roles. diff --git a/test/integration-tests/lib/sv1_sniffer.rs b/test/integration-tests/lib/sv1_sniffer.rs index 002ed4ddf6..d9e2e97aa4 100644 --- a/test/integration-tests/lib/sv1_sniffer.rs +++ b/test/integration-tests/lib/sv1_sniffer.rs @@ -1,8 +1,8 @@ #![cfg(feature = "sv1")] use crate::interceptor::MessageDirection; use async_channel::{Receiver, Sender}; -use network_helpers_sv2::sv1_connection::ConnectionSV1; use std::{collections::VecDeque, net::SocketAddr, sync::Arc}; +use stratum_common::network_helpers_sv2::sv1_connection::ConnectionSV1; use tokio::{ net::{TcpListener, TcpStream}, select, diff --git a/test/integration-tests/lib/template_provider.rs b/test/integration-tests/lib/template_provider.rs index 901cf4f769..24115bfb42 100644 --- a/test/integration-tests/lib/template_provider.rs +++ b/test/integration-tests/lib/template_provider.rs @@ -1,6 +1,6 @@ use corepc_node::{Conf, ConnectParams, Node}; use std::{env, fs::create_dir_all, path::PathBuf}; -use stratum_common::bitcoin::{Address, Amount, Txid}; +use stratum_common::roles_logic_sv2::bitcoin::{Address, Amount, Txid}; use crate::utils::{http, tarball}; diff --git a/test/integration-tests/lib/types.rs b/test/integration-tests/lib/types.rs index a69a22a4fc..e7ff1aa7ab 100644 --- a/test/integration-tests/lib/types.rs +++ b/test/integration-tests/lib/types.rs @@ -1,5 +1,4 @@ -use codec_sv2::StandardEitherFrame; -use roles_logic_sv2::parsers::AnyMessage; +use stratum_common::roles_logic_sv2::{codec_sv2::StandardEitherFrame, parsers::AnyMessage}; pub type MessageFrame = StandardEitherFrame>; pub type MsgType = u8; diff --git a/test/integration-tests/lib/utils.rs b/test/integration-tests/lib/utils.rs index 4f77afe808..b2ee2d8e7e 100644 --- a/test/integration-tests/lib/utils.rs +++ b/test/integration-tests/lib/utils.rs @@ -5,28 +5,33 @@ use crate::{ types::{MessageFrame, MsgType}, }; use async_channel::{Receiver, Sender}; -use codec_sv2::{ - framing_sv2::framing::Frame, HandshakeRole, Initiator, Responder, StandardEitherFrame, Sv2Frame, -}; use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; -use network_helpers_sv2::noise_connection::Connection; use once_cell::sync::Lazy; -use roles_logic_sv2::parsers::{ - message_type_to_name, AnyMessage, CommonMessages, IsSv2Message, - JobDeclaration::{ - AllocateMiningJobToken, AllocateMiningJobTokenSuccess, DeclareMiningJob, - DeclareMiningJobError, DeclareMiningJobSuccess, ProvideMissingTransactions, - ProvideMissingTransactionsSuccess, PushSolution, - }, - TemplateDistribution, - TemplateDistribution::CoinbaseOutputConstraints, -}; use std::{ collections::HashSet, convert::TryInto, net::{SocketAddr, TcpListener}, sync::Mutex, }; +use stratum_common::{ + network_helpers_sv2::noise_connection::Connection, + roles_logic_sv2::{ + codec_sv2::{ + framing_sv2::framing::Frame, HandshakeRole, Initiator, Responder, StandardEitherFrame, + Sv2Frame, + }, + parsers::{ + message_type_to_name, AnyMessage, CommonMessages, IsSv2Message, + JobDeclaration::{ + AllocateMiningJobToken, AllocateMiningJobTokenSuccess, DeclareMiningJob, + DeclareMiningJobError, DeclareMiningJobSuccess, ProvideMissingTransactions, + ProvideMissingTransactionsSuccess, PushSolution, + }, + TemplateDistribution, + TemplateDistribution::CoinbaseOutputConstraints, + }, + }, +}; // prevents get_available_port from ever returning the same port twice static UNIQUE_PORTS: Lazy>> = Lazy::new(|| Mutex::new(HashSet::new())); diff --git a/test/integration-tests/tests/jd_integration.rs b/test/integration-tests/tests/jd_integration.rs index ca0e3ff4cc..abd7b5a6e8 100644 --- a/test/integration-tests/tests/jd_integration.rs +++ b/test/integration-tests/tests/jd_integration.rs @@ -1,10 +1,11 @@ // This file contains integration tests for the `JDC/S` module. -use binary_sv2::{Seq064K, B032, U256}; use integration_tests_sv2::{ interceptor::{IgnoreMessage, MessageDirection, ReplaceMessage}, *, }; -use roles_logic_sv2::{ +use stratum_common::roles_logic_sv2::{ + self, + codec_sv2::binary_sv2::{Seq064K, B032, U256}, common_messages_sv2::*, job_declaration_sv2::{ProvideMissingTransactionsSuccess, PushSolution, *}, parsers::AnyMessage, diff --git a/test/integration-tests/tests/jd_provide_missing_transaction.rs b/test/integration-tests/tests/jd_provide_missing_transaction.rs index f59f8b19aa..1c988a315d 100644 --- a/test/integration-tests/tests/jd_provide_missing_transaction.rs +++ b/test/integration-tests/tests/jd_provide_missing_transaction.rs @@ -1,5 +1,5 @@ use integration_tests_sv2::{interceptor::MessageDirection, *}; -use roles_logic_sv2::job_declaration_sv2::*; +use stratum_common::roles_logic_sv2::job_declaration_sv2::*; #[tokio::test] async fn jds_ask_for_missing_transactions() { diff --git a/test/integration-tests/tests/jd_tproxy_integration.rs b/test/integration-tests/tests/jd_tproxy_integration.rs index 77cdf5111c..0c98041b41 100644 --- a/test/integration-tests/tests/jd_tproxy_integration.rs +++ b/test/integration-tests/tests/jd_tproxy_integration.rs @@ -1,5 +1,5 @@ use integration_tests_sv2::{interceptor::MessageDirection, *}; -use roles_logic_sv2::{common_messages_sv2::*, mining_sv2::*}; +use stratum_common::roles_logic_sv2::{common_messages_sv2::*, mining_sv2::*}; #[tokio::test] async fn jd_tproxy_integration() { diff --git a/test/integration-tests/tests/jdc_block_propogation.rs b/test/integration-tests/tests/jdc_block_propogation.rs index 3bc9ff66e9..7b80ac2432 100644 --- a/test/integration-tests/tests/jdc_block_propogation.rs +++ b/test/integration-tests/tests/jdc_block_propogation.rs @@ -2,7 +2,7 @@ use integration_tests_sv2::{ interceptor::{IgnoreMessage, MessageDirection}, *, }; -use roles_logic_sv2::{job_declaration_sv2::*, template_distribution_sv2::*}; +use stratum_common::roles_logic_sv2::{job_declaration_sv2::*, template_distribution_sv2::*}; // Block propogated from JDC to TP #[tokio::test] diff --git a/test/integration-tests/tests/jdc_fallback.rs b/test/integration-tests/tests/jdc_fallback.rs index c748b4cb51..30eac6d3f6 100644 --- a/test/integration-tests/tests/jdc_fallback.rs +++ b/test/integration-tests/tests/jdc_fallback.rs @@ -2,12 +2,12 @@ use integration_tests_sv2::{ interceptor::{MessageDirection, ReplaceMessage}, *, }; -use roles_logic_sv2::{ +use std::convert::TryInto; +use stratum_common::roles_logic_sv2::{ common_messages_sv2::*, mining_sv2::{SubmitSharesError, *}, parsers::{AnyMessage, Mining}, }; -use std::convert::TryInto; // Tests whether JDC will switch to a new pool after receiving a `SubmitSharesError` message from // the currently connected pool. diff --git a/test/integration-tests/tests/jds_block_propogation.rs b/test/integration-tests/tests/jds_block_propogation.rs index 31158060b9..e1efdc27d9 100644 --- a/test/integration-tests/tests/jds_block_propogation.rs +++ b/test/integration-tests/tests/jds_block_propogation.rs @@ -2,7 +2,7 @@ use integration_tests_sv2::{ interceptor::{IgnoreMessage, MessageDirection}, *, }; -use roles_logic_sv2::{job_declaration_sv2::*, template_distribution_sv2::*}; +use stratum_common::roles_logic_sv2::{job_declaration_sv2::*, template_distribution_sv2::*}; // Block propogated from JDS to TP #[tokio::test] diff --git a/test/integration-tests/tests/pool_integration.rs b/test/integration-tests/tests/pool_integration.rs index 0d000065ce..855e4cdf78 100644 --- a/test/integration-tests/tests/pool_integration.rs +++ b/test/integration-tests/tests/pool_integration.rs @@ -2,7 +2,7 @@ // // `PoolSv2` is a module that implements the Pool role in the Stratum V2 protocol. use integration_tests_sv2::{interceptor::MessageDirection, *}; -use roles_logic_sv2::{ +use stratum_common::roles_logic_sv2::{ common_messages_sv2::{Protocol, SetupConnection, *}, mining_sv2::*, parsers::{AnyMessage, CommonMessages, Mining, TemplateDistribution}, diff --git a/test/integration-tests/tests/sniffer_integration.rs b/test/integration-tests/tests/sniffer_integration.rs index 335fa31bbd..def396bc9c 100644 --- a/test/integration-tests/tests/sniffer_integration.rs +++ b/test/integration-tests/tests/sniffer_integration.rs @@ -3,12 +3,12 @@ use integration_tests_sv2::{ interceptor::{IgnoreMessage, MessageDirection, ReplaceMessage}, *, }; -use roles_logic_sv2::{ +use std::convert::TryInto; +use stratum_common::roles_logic_sv2::{ common_messages_sv2::{Protocol, SetupConnection, SetupConnectionSuccess, *}, parsers::{AnyMessage, CommonMessages}, template_distribution_sv2::*, }; -use std::convert::TryInto; // This test aims to assert that Sniffer is able to intercept and replace/ignore messages. // TP -> sniffer_a -> sniffer_b -> Pool diff --git a/test/integration-tests/tests/sv2_mining_device.rs b/test/integration-tests/tests/sv2_mining_device.rs index 79073f6db9..e1e92fdefc 100644 --- a/test/integration-tests/tests/sv2_mining_device.rs +++ b/test/integration-tests/tests/sv2_mining_device.rs @@ -1,5 +1,5 @@ use integration_tests_sv2::{interceptor::MessageDirection, *}; -use roles_logic_sv2::common_messages_sv2::*; +use stratum_common::roles_logic_sv2::common_messages_sv2::*; #[tokio::test] async fn sv2_mining_device_and_pool_success() { diff --git a/test/integration-tests/tests/translator_integration.rs b/test/integration-tests/tests/translator_integration.rs index d08ab3b1a9..1c42bd4734 100644 --- a/test/integration-tests/tests/translator_integration.rs +++ b/test/integration-tests/tests/translator_integration.rs @@ -1,6 +1,6 @@ // This file contains integration tests for the `TranslatorSv2` module. use integration_tests_sv2::{interceptor::MessageDirection, *}; -use roles_logic_sv2::{ +use stratum_common::roles_logic_sv2::{ common_messages_sv2::*, mining_sv2::*, parsers::{AnyMessage, CommonMessages, Mining}, diff --git a/utils/Cargo.lock b/utils/Cargo.lock index 10f8528362..42862c4f2e 100644 --- a/utils/Cargo.lock +++ b/utils/Cargo.lock @@ -81,8 +81,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" dependencies = [ - "bitcoin-internals", - "bitcoin_hashes", + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.0", ] [[package]] @@ -91,6 +91,21 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +[[package]] +name = "binary_codec_sv2" +version = "2.0.0" +dependencies = [ + "buffer_sv2", +] + +[[package]] +name = "binary_sv2" +version = "3.0.0" +dependencies = [ + "binary_codec_sv2", + "derive_codec_sv2", +] + [[package]] name = "bip32_derivation" version = "1.0.0" @@ -107,15 +122,21 @@ checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" dependencies = [ "base58ck", "bech32", - "bitcoin-internals", + "bitcoin-internals 0.3.0", "bitcoin-io", "bitcoin-units", - "bitcoin_hashes", - "hex-conservative", + "bitcoin_hashes 0.14.0", + "hex-conservative 0.2.1", "hex_lit", "secp256k1 0.29.1", ] +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + [[package]] name = "bitcoin-internals" version = "0.3.0" @@ -134,7 +155,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" dependencies = [ - "bitcoin-internals", + "bitcoin-internals 0.3.0", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals 0.2.0", + "hex-conservative 0.1.2", ] [[package]] @@ -144,7 +175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ "bitcoin-io", - "hex-conservative", + "hex-conservative 0.2.1", ] [[package]] @@ -153,6 +184,18 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -187,6 +230,12 @@ version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + [[package]] name = "byteorder" version = "1.5.0" @@ -214,6 +263,30 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + [[package]] name = "cipher" version = "0.4.4" @@ -222,6 +295,7 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", + "zeroize", ] [[package]] @@ -235,6 +309,45 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "codec_sv2" +version = "2.1.0" +dependencies = [ + "binary_sv2", + "buffer_sv2", + "framing_sv2", + "noise_sv2", + "rand", + "tracing", +] + +[[package]] +name = "common_messages_sv2" +version = "5.1.0" +dependencies = [ + "binary_sv2", +] + +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -305,6 +418,12 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + [[package]] name = "crypto-common" version = "0.1.6" @@ -312,6 +431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] @@ -345,6 +465,13 @@ dependencies = [ "cipher", ] +[[package]] +name = "derive_codec_sv2" +version = "1.1.1" +dependencies = [ + "binary_codec_sv2", +] + [[package]] name = "digest" version = "0.9.0" @@ -360,10 +487,43 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "error_handling" version = "1.0.0" +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "framing_sv2" +version = "5.1.0" +dependencies = [ + "binary_sv2", + "buffer_sv2", + "noise_sv2", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "generic-array" version = "0.14.7" @@ -411,6 +571,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -420,6 +586,18 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" + [[package]] name = "hex-conservative" version = "0.2.1" @@ -429,6 +607,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-conservative" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afe881d0527571892c4034822e59bb10c6c991cce6abe8199b6f5cf10766f55" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex_lit" version = "0.1.1" @@ -441,6 +628,36 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678" +[[package]] +name = "impl-codec" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d40b9d5e17727407e55028eafc22b2dc68781786e6d7eb8a21103f5058e3a14" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown 0.15.4", +] + [[package]] name = "inout" version = "0.1.4" @@ -465,6 +682,13 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +[[package]] +name = "job_declaration_sv2" +version = "4.0.0" +dependencies = [ + "binary_sv2", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -511,6 +735,30 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "mining_sv2" +version = "4.0.0" +dependencies = [ + "binary_sv2", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "noise_sv2" +version = "1.4.0" +dependencies = [ + "aes-gcm", + "chacha20poly1305", + "rand", + "rand_chacha", + "secp256k1 0.28.2", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -538,6 +786,40 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "plotters" version = "0.3.7" @@ -566,6 +848,17 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "polyval" version = "0.6.2" @@ -587,24 +880,50 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "primitive-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -684,6 +1003,29 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "roles_logic_sv2" +version = "3.0.0" +dependencies = [ + "bitcoin", + "chacha20poly1305", + "codec_sv2", + "common_messages_sv2", + "hex-conservative 0.3.0", + "job_declaration_sv2", + "mining_sv2", + "nohash-hasher", + "primitive-types", + "template_distribution_sv2", + "tracing", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + [[package]] name = "rustversion" version = "1.0.19" @@ -711,6 +1053,7 @@ version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" dependencies = [ + "bitcoin_hashes 0.13.0", "rand", "secp256k1-sys 0.9.2", ] @@ -721,7 +1064,7 @@ version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.0", "secp256k1-sys 0.10.1", ] @@ -745,9 +1088,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -764,9 +1107,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -804,12 +1147,17 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stratum-common" -version = "2.0.0" +version = "3.0.0" dependencies = [ - "bitcoin", - "secp256k1 0.28.2", + "roles_logic_sv2", ] [[package]] @@ -829,6 +1177,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "template_distribution_sv2" +version = "3.1.0" +dependencies = [ + "binary_sv2", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -853,16 +1214,76 @@ name = "toml" version = "0.5.6" source = "git+https://github.com/diondokter/toml-rs?rev=c4161aa#c4161aa70202b3992dbec79b76e7a8659713b604" dependencies = [ - "hashbrown", + "hashbrown 0.7.2", "serde", ] +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + [[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unicode-ident" version = "1.0.17" @@ -875,6 +1296,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "universal-hash" version = "0.5.1" @@ -1079,6 +1506,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -1099,3 +1544,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/utils/bip32-key-derivation/Cargo.toml b/utils/bip32-key-derivation/Cargo.toml index 47116d07e9..3faf95753a 100644 --- a/utils/bip32-key-derivation/Cargo.toml +++ b/utils/bip32-key-derivation/Cargo.toml @@ -20,7 +20,8 @@ name = "bip32_derivation-bin" path = "src/main.rs" [dependencies] -stratum-common = { path = "../../common", features=["bitcoin"], version = "^2.0.0" } +stratum-common = { path = "../../common" } + [dev-dependencies] toml = { version = "0.5.6", git = "https://github.com/diondokter/toml-rs", default-features = false, rev = "c4161aa" } diff --git a/utils/bip32-key-derivation/src/lib.rs b/utils/bip32-key-derivation/src/lib.rs index a96e1d6045..bdcb37a515 100644 --- a/utils/bip32-key-derivation/src/lib.rs +++ b/utils/bip32-key-derivation/src/lib.rs @@ -1,5 +1,5 @@ use std::str::FromStr; -use stratum_common::bitcoin::{ +use stratum_common::roles_logic_sv2::bitcoin::{ bip32::{DerivationPath, Error, Xpub}, secp256k1::Secp256k1, }; diff --git a/utils/bip32-key-derivation/src/main.rs b/utils/bip32-key-derivation/src/main.rs index 52c97f0a65..9bcbae4107 100644 --- a/utils/bip32-key-derivation/src/main.rs +++ b/utils/bip32-key-derivation/src/main.rs @@ -1,6 +1,6 @@ use bip32_derivation::derive_child_public_key; use std::{env, str::FromStr}; -use stratum_common::bitcoin::bip32::Xpub; +use stratum_common::roles_logic_sv2::bitcoin::bip32::Xpub; fn main() { let args: Vec = env::args().collect(); From 195fad68ba6b80fb9a277d7b4eafb468bfbd2cd8 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 19 Jun 2025 13:32:56 -0400 Subject: [PATCH 013/338] update imports in `encrypted.rs`, an example inside coded_sv2 --- protocols/v2/codec-sv2/examples/encrypted.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/protocols/v2/codec-sv2/examples/encrypted.rs b/protocols/v2/codec-sv2/examples/encrypted.rs index f006d5d2bc..a28898796f 100644 --- a/protocols/v2/codec-sv2/examples/encrypted.rs +++ b/protocols/v2/codec-sv2/examples/encrypted.rs @@ -24,14 +24,16 @@ use codec_sv2::{ }; #[cfg(feature = "noise_sv2")] use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; + +#[cfg(feature = "noise_sv2")] +use noise_sv2::{ELLSWIFT_ENCODING_SIZE, INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE}; + use std::convert::TryInto; #[cfg(feature = "noise_sv2")] use std::{ io::{Read, Write}, net::{TcpListener, TcpStream}, }; -#[cfg(feature = "noise_sv2")] -use stratum_common::{ELLSWIFT_ENCODING_SIZE, INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE}; // Arbitrary message type. // Supported Sv2 message types are listed in the [Sv2 Spec Message From 17de43df1fb8c796faea9b4a2a1799c846e7c23e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 4 Jun 2025 18:42:31 +0530 Subject: [PATCH 014/338] make vardiff implementation sync --- protocols/v2/roles-logic-sv2/src/vardiff/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/v2/roles-logic-sv2/src/vardiff/mod.rs b/protocols/v2/roles-logic-sv2/src/vardiff/mod.rs index e5976c6dfe..ff8a9e6682 100644 --- a/protocols/v2/roles-logic-sv2/src/vardiff/mod.rs +++ b/protocols/v2/roles-logic-sv2/src/vardiff/mod.rs @@ -8,7 +8,7 @@ pub mod error; pub mod test; /// Trait defining the interface for a Vardiff implementation. -pub trait Vardiff: Debug + Send { +pub trait Vardiff: Debug + Send + Sync { /// Gets the timestamp of the last update. fn last_update_timestamp(&self) -> u64; From 981e32639d133807991d829f11098b751b2efdf7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 4 Jun 2025 18:42:48 +0530 Subject: [PATCH 015/338] add vardiff error variant in PoolError --- roles/pool/src/lib/error.rs | 14 ++++++++++++-- roles/pool/src/lib/status.rs | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/roles/pool/src/lib/error.rs b/roles/pool/src/lib/error.rs index 5f03f65524..2fcbf6adb4 100644 --- a/roles/pool/src/lib/error.rs +++ b/roles/pool/src/lib/error.rs @@ -19,8 +19,8 @@ use std::{ use stratum_common::roles_logic_sv2::{ self, codec_sv2::{self, binary_sv2, noise_sv2}, - parsers::Mining, -}; + {parsers::Mining, +}, vardiff::error::VardiffError}; /// Represents various errors that can occur in the pool implementation. #[derive(std::fmt::Debug)] @@ -51,6 +51,13 @@ pub enum PoolError { Custom(String), /// Error related to the SV2 protocol, including an error code and a `Mining` message. Sv2ProtocolError((u32, Mining<'static>)), + Vardiff(VardiffError), +} + +impl From for PoolError { + fn from(value: VardiffError) -> Self { + PoolError::Vardiff(value) + } } impl std::fmt::Display for PoolError { @@ -72,6 +79,9 @@ impl std::fmt::Display for PoolError { Sv2ProtocolError(ref e) => { write!(f, "Received Sv2 Protocol Error from upstream: `{:?}`", e) } + PoolError::Vardiff(ref e) => { + write!(f, "Received Vardiff Error : {:?}", e) + } } } } diff --git a/roles/pool/src/lib/status.rs b/roles/pool/src/lib/status.rs index 115b78948d..302567ace4 100644 --- a/roles/pool/src/lib/status.rs +++ b/roles/pool/src/lib/status.rs @@ -167,5 +167,8 @@ pub async fn handle_error(sender: &Sender, e: PoolError) -> error_handling::Erro PoolError::Sv2ProtocolError(_) => { send_status(sender, e, error_handling::ErrorBranch::Break).await } + PoolError::Vardiff(_) => { + send_status(sender, e, error_handling::ErrorBranch::Continue).await + } } } From d3983918f9e7bdd3611c79086d7d1241a5953801 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 4 Jun 2025 18:43:16 +0530 Subject: [PATCH 016/338] This commit adds vardiff to pool: 1. Adapt message handlers to update vardiff state based on message received. 2. Spawn a downstream `SetTarget` message sender, which initiates on new channel opening, and checks for change in hashrate every 60seconds. --- .../src/lib/mining_pool/message_handler.rs | 39 ++++++++ roles/pool/src/lib/mining_pool/mod.rs | 97 +++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index af4951c883..b718765173 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -25,6 +25,7 @@ use stratum_common::roles_logic_sv2::{ parsers::Mining, template_distribution_sv2::SubmitSolution, utils::Mutex, + VardiffState, }; use tracing::{error, info}; @@ -200,9 +201,14 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { let messages = messages.into_iter().map(SendTo::Respond).collect(); + let vardiff = VardiffState::new(self.shares_per_minute, incoming.nominal_hash_rate)?; + self.standard_channels .insert(channel_id, Arc::new(RwLock::new(standard_channel.clone()))); + self.vardiff + .insert(channel_id, Arc::new(RwLock::new(Box::new(vardiff)))); + if let Some(group_channel_guard) = &self.group_channel { let mut group_channel = group_channel_guard .write() @@ -389,8 +395,12 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { let messages = messages.into_iter().map(SendTo::Respond).collect(); + let vardiff = VardiffState::new(self.shares_per_minute, m.nominal_hash_rate)?; + self.extended_channels .insert(channel_id, Arc::new(RwLock::new(extended_channel.clone()))); + self.vardiff + .insert(channel_id, Arc::new(RwLock::new(Box::new(vardiff)))); Ok(SendTo::Multiple(messages)) } @@ -414,6 +424,13 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { let is_standard_channel = self.standard_channels.contains_key(&channel_id); let is_extended_channel = self.extended_channels.contains_key(&channel_id); + let mut vardiff = self + .vardiff + .get(&channel_id) + .expect("Vardiff must exist") + .write() + .map_err(|e| Error::PoisonLock(e.to_string()))?; + if is_standard_channel { let mut standard_channel = self .standard_channels @@ -460,6 +477,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { } } } + _ = vardiff.set_hashrate(new_nominal_hash_rate); let new_target = standard_channel.get_target(); let set_target = SetTarget { channel_id, @@ -512,6 +530,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { } } } + _ = vardiff.set_hashrate(m.nominal_hash_rate); let new_target = extended_channel.get_target(); let set_target = SetTarget { channel_id, @@ -572,9 +591,17 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { .write() .map_err(|e| Error::PoisonLock(e.to_string()))?; + let mut vardiff = self + .vardiff + .get(&channel_id) + .expect("Vardiff must exist") + .write() + .map_err(|e| Error::PoisonLock(e.to_string()))?; + let res = standard_channel.validate_share(m.clone()); match res { Ok(ShareValidationResult::Valid) => { + vardiff.increment_shares_since_last_update(); info!( "SubmitSharesStandard: valid share | channel_id: {}, sequence_number: {} ☑️", channel_id, m.sequence_number @@ -586,6 +613,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { new_submits_accepted_count, new_shares_sum, )) => { + vardiff.increment_shares_since_last_update(); let success = SubmitSharesSuccess { channel_id, last_sequence_number, @@ -596,6 +624,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { Ok(SendTo::Respond(Mining::SubmitSharesSuccess(success))) } Ok(ShareValidationResult::BlockFound(template_id, coinbase)) => { + vardiff.increment_shares_since_last_update(); info!("SubmitSharesStandard: 💰 Block Found!!! 💰"); // if we have a template id (i.e.: this was not a custom job) // we can propagate the solution to the TP @@ -724,9 +753,17 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { .write() .map_err(|e| Error::PoisonLock(e.to_string()))?; + let mut vardiff = self + .vardiff + .get(&channel_id) + .expect("Vardiff must exist") + .write() + .map_err(|e| Error::PoisonLock(e.to_string()))?; + let res = extended_channel.validate_share(m.clone()); match res { Ok(ShareValidationResult::Valid) => { + vardiff.increment_shares_since_last_update(); info!( "SubmitSharesExtended: valid share | channel_id: {}, sequence_number: {} ☑️", channel_id, m.sequence_number @@ -738,6 +775,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { new_submits_accepted_count, new_shares_sum, )) => { + vardiff.increment_shares_since_last_update(); let success = SubmitSharesSuccess { channel_id, last_sequence_number, @@ -748,6 +786,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { Ok(SendTo::Respond(Mining::SubmitSharesSuccess(success))) } Ok(ShareValidationResult::BlockFound(template_id, coinbase)) => { + vardiff.increment_shares_since_last_update(); info!("SubmitSharesExtended: 💰 Block Found!!! 💰"); // if we have a template id (i.e.: this was not a custom job) // we can propagate the solution to the TP diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 87a195df89..ae4bf3d320 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -36,6 +36,7 @@ use std::{ convert::TryInto, net::SocketAddr, sync::{Arc, RwLock}, + time::Duration, }; use stratum_common::{ network_helpers_sv2::noise_connection::Connection, @@ -60,6 +61,9 @@ use stratum_common::{ utils::{Id as IdFactory, Mutex}, }, }; + +use roles_logic_sv2::Vardiff; + use tokio::{net::TcpListener, task}; use tracing::{debug, error, info, warn}; @@ -121,6 +125,7 @@ pub struct Downstream { extended_channels: HashMap>>>, // A map of all standard channels, keyed by their ID. standard_channels: HashMap>>>, + vardiff: HashMap>>>, // naive approach: // we create one group channel for the connection // and add all standard channels to this same single group channel @@ -260,6 +265,7 @@ impl Downstream { channel_id_factory, extended_channels: HashMap::new(), standard_channels: HashMap::new(), + vardiff: HashMap::new(), group_channel, extranonce_prefix_factory_extended, extranonce_prefix_factory_standard, @@ -368,6 +374,36 @@ impl Downstream { ) -> PoolResult<()> { match send_to { Ok(SendTo::Respond(message)) => { + match &message { + Mining::OpenExtendedMiningChannelSuccess(m) => { + let (vardiff, sender) = self_.safe_lock(|downstream| { + let vardiff = downstream + .vardiff + .get(&m.channel_id) + .expect("Vardiff should be present") + .clone(); + let sender = downstream.sender.clone(); + (vardiff, sender) + })?; + spawn_vardiff_loop(m.channel_id, vardiff, sender); + } + + Mining::OpenStandardMiningChannelSuccess(m) => { + let (vardiff, sender) = self_.safe_lock(|downstream| { + let vardiff = downstream + .vardiff + .get(&m.channel_id) + .expect("Vardiff should be present") + .clone(); + let sender = downstream.sender.clone(); + (vardiff, sender) + })?; + spawn_vardiff_loop(m.channel_id, vardiff, sender); + } + + _ => {} + } + debug!("Sending to downstream: {:?}", message); // returning an error will send the error to the main thread, // and the main thread will drop the downstream from the pool @@ -1124,6 +1160,67 @@ impl Pool { self.downstreams.remove(&downstream_id); } } +pub async fn send_set_target_downstream( + sender: Sender, + channel_id: u32, + vardiff: Arc>>, +) -> Result<(), PoolError> { + debug!("Sleeping 60s before sending SetTarget for channel_id={channel_id}"); + tokio::time::sleep(Duration::from_secs(60)).await; + + debug!("Attempting try_vardiff for channel_id={channel_id}"); + let new_hashrate = match vardiff.write() { + Ok(mut v) => v.try_vardiff(), + Err(e) => { + error!("Failed to acquire write lock on vardiff for channel_id={channel_id}: {e}"); + return Err(PoolError::PoisonLock(e.to_string())); + } + }?; + + if new_hashrate.is_some() { + debug!("New hashrate detected for channel_id={channel_id}, preparing SetTarget message"); + let target = match vardiff.read() { + Ok(v) => v.target(), + Err(e) => { + error!("Failed to acquire read lock on vardiff for channel_id={channel_id}: {e}"); + return Err(PoolError::PoisonLock(e.to_string())); + } + }; + + let target_message = SetTarget { + channel_id, + maximum_target: target.into(), + }; + + let mining_msg = Mining::SetTarget(target_message); + let sv2_frame: StdFrame = AnyMessage::Mining(mining_msg).try_into()?; + + sender.send(sv2_frame.into()).await?; + } else { + debug!("No hashrate adjustment needed for channel_id={channel_id}"); + } + + Ok(()) +} + +pub fn spawn_vardiff_loop( + channel_id: u32, + vardiff: Arc>>, + sender: Sender, +) { + info!("Spawning vardiff loop for channel_id={channel_id}"); + tokio::spawn(async move { + loop { + if let Err(e) = + send_set_target_downstream(sender.clone(), channel_id, vardiff.clone()).await + { + warn!("Vardiff loop exiting for channel_id={channel_id} due to error: {e}"); + break; + } + } + info!("Vardiff loop terminated for channel_id={channel_id}"); + }); +} #[cfg(test)] mod test { From a8a165167a103859865d02054ed542a227ac8891 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 6 Jun 2025 16:03:33 +0530 Subject: [PATCH 017/338] add setters for target and nominal hashrate in extended channel --- .../v2/roles-logic-sv2/src/channels/server/extended.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs index b01ecc3c93..a3044f31f8 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs @@ -183,6 +183,10 @@ impl<'a> ExtendedChannel<'a> { &self.target } + pub fn set_target(&mut self, target: Target) { + self.target = target; + } + pub fn get_future_template_to_job_id(&self) -> &HashMap { &self.future_template_to_job_id } @@ -191,6 +195,10 @@ impl<'a> ExtendedChannel<'a> { self.nominal_hashrate } + pub fn set_nominal_hashrate(&mut self, hashrate: f32) { + self.nominal_hashrate = hashrate; + } + /// Updates the channel's nominal hashrate and target. /// /// If requested_max_target is None, we use the cached value in the channel state. From 0671bfa5261949ee7f10bf7e13f25a86e7a50053 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 6 Jun 2025 16:08:55 +0530 Subject: [PATCH 018/338] update shares count when a share is received --- roles/pool/src/lib/mining_pool/message_handler.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index b718765173..3e687dd554 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -599,9 +599,9 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { .map_err(|e| Error::PoisonLock(e.to_string()))?; let res = standard_channel.validate_share(m.clone()); + vardiff.increment_shares_since_last_update(); match res { Ok(ShareValidationResult::Valid) => { - vardiff.increment_shares_since_last_update(); info!( "SubmitSharesStandard: valid share | channel_id: {}, sequence_number: {} ☑️", channel_id, m.sequence_number @@ -613,7 +613,6 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { new_submits_accepted_count, new_shares_sum, )) => { - vardiff.increment_shares_since_last_update(); let success = SubmitSharesSuccess { channel_id, last_sequence_number, @@ -624,7 +623,6 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { Ok(SendTo::Respond(Mining::SubmitSharesSuccess(success))) } Ok(ShareValidationResult::BlockFound(template_id, coinbase)) => { - vardiff.increment_shares_since_last_update(); info!("SubmitSharesStandard: 💰 Block Found!!! 💰"); // if we have a template id (i.e.: this was not a custom job) // we can propagate the solution to the TP @@ -761,9 +759,9 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { .map_err(|e| Error::PoisonLock(e.to_string()))?; let res = extended_channel.validate_share(m.clone()); + vardiff.increment_shares_since_last_update(); match res { Ok(ShareValidationResult::Valid) => { - vardiff.increment_shares_since_last_update(); info!( "SubmitSharesExtended: valid share | channel_id: {}, sequence_number: {} ☑️", channel_id, m.sequence_number @@ -775,7 +773,6 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { new_submits_accepted_count, new_shares_sum, )) => { - vardiff.increment_shares_since_last_update(); let success = SubmitSharesSuccess { channel_id, last_sequence_number, @@ -786,7 +783,6 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { Ok(SendTo::Respond(Mining::SubmitSharesSuccess(success))) } Ok(ShareValidationResult::BlockFound(template_id, coinbase)) => { - vardiff.increment_shares_since_last_update(); info!("SubmitSharesExtended: 💰 Block Found!!! 💰"); // if we have a template id (i.e.: this was not a custom job) // we can propagate the solution to the TP From 7cc8bdd2e9fb97e1a8c7db445b2541f7ebe53945 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 9 Jun 2025 17:33:49 +0530 Subject: [PATCH 019/338] changed log from warn to debug --- protocols/v2/roles-logic-sv2/src/vardiff/classic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/vardiff/classic.rs b/protocols/v2/roles-logic-sv2/src/vardiff/classic.rs index 86cbdbcd9d..961d506e81 100644 --- a/protocols/v2/roles-logic-sv2/src/vardiff/classic.rs +++ b/protocols/v2/roles-logic-sv2/src/vardiff/classic.rs @@ -1,6 +1,6 @@ use crate::utils::hash_rate_from_target; use mining_sv2::Target; -use tracing::{debug, warn}; +use tracing::debug; /// Default minimum hashrate (H/s) if not specified. const DEFAULT_MIN_HASHRATE: f32 = 1.0; @@ -132,7 +132,7 @@ impl Vardiff for VardiffState { ) { Ok(hashrate) => hashrate as f32, Err(e) => { - warn!( + debug!( target: "vardiff", "Target->Hashrate conversion failed: {:?}. Falling back using previous hashrate and realized_shares_per_minute", e ); From eb49636dbbc444c270cd8de3c2c5795019adcd13 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 16 Jun 2025 17:19:36 +0530 Subject: [PATCH 020/338] update pool with new try_vardiff method signature, and spawn a single vardiff monitor per client --- .../src/channels/server/extended.rs | 4 + .../src/channels/server/standard.rs | 4 + .../src/lib/mining_pool/message_handler.rs | 13 +- roles/pool/src/lib/mining_pool/mod.rs | 215 ++++++++++++------ 4 files changed, 150 insertions(+), 86 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs index a3044f31f8..38689f6c87 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs @@ -140,6 +140,10 @@ impl<'a> ExtendedChannel<'a> { self.chain_tip.as_ref() } + pub fn get_shares_per_minute(&self) -> f32 { + self.expected_share_per_minute + } + /// Only for testing purposes, not meant to be used in real apps. #[cfg(test)] fn set_chain_tip(&mut self, chain_tip: ChainTip) { diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs index dc4d814004..d488375243 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs @@ -233,6 +233,10 @@ impl<'a> StandardChannel<'a> { &self.stale_jobs } + pub fn get_shares_per_minute(&self) -> f32 { + self.expected_share_per_minute + } + pub fn get_chain_tip(&self) -> Option<&ChainTip> { self.chain_tip.as_ref() } diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index 3e687dd554..a29eb4ddaf 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -201,7 +201,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { let messages = messages.into_iter().map(SendTo::Respond).collect(); - let vardiff = VardiffState::new(self.shares_per_minute, incoming.nominal_hash_rate)?; + let vardiff = VardiffState::new()?; self.standard_channels .insert(channel_id, Arc::new(RwLock::new(standard_channel.clone()))); @@ -395,7 +395,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { let messages = messages.into_iter().map(SendTo::Respond).collect(); - let vardiff = VardiffState::new(self.shares_per_minute, m.nominal_hash_rate)?; + let vardiff = VardiffState::new()?; self.extended_channels .insert(channel_id, Arc::new(RwLock::new(extended_channel.clone()))); @@ -424,13 +424,6 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { let is_standard_channel = self.standard_channels.contains_key(&channel_id); let is_extended_channel = self.extended_channels.contains_key(&channel_id); - let mut vardiff = self - .vardiff - .get(&channel_id) - .expect("Vardiff must exist") - .write() - .map_err(|e| Error::PoisonLock(e.to_string()))?; - if is_standard_channel { let mut standard_channel = self .standard_channels @@ -477,7 +470,6 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { } } } - _ = vardiff.set_hashrate(new_nominal_hash_rate); let new_target = standard_channel.get_target(); let set_target = SetTarget { channel_id, @@ -530,7 +522,6 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { } } } - _ = vardiff.set_hashrate(m.nominal_hash_rate); let new_target = extended_channel.get_target(); let set_target = SetTarget { channel_id, diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index ae4bf3d320..207f5e0cea 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -259,7 +259,7 @@ impl Downstream { let self_ = Arc::new(Mutex::new(Downstream { id, receiver, - sender, + sender: sender.clone(), downstream_data, solution_sender, channel_id_factory, @@ -276,6 +276,8 @@ impl Downstream { empty_pool_coinbase_outputs, })); + tokio::spawn(spawn_vardiff_loop(self_.clone(), sender, id)); + let cloned = self_.clone(); // Spawn a dedicated task to continuously receive and process messages from this downstream. @@ -374,36 +376,6 @@ impl Downstream { ) -> PoolResult<()> { match send_to { Ok(SendTo::Respond(message)) => { - match &message { - Mining::OpenExtendedMiningChannelSuccess(m) => { - let (vardiff, sender) = self_.safe_lock(|downstream| { - let vardiff = downstream - .vardiff - .get(&m.channel_id) - .expect("Vardiff should be present") - .clone(); - let sender = downstream.sender.clone(); - (vardiff, sender) - })?; - spawn_vardiff_loop(m.channel_id, vardiff, sender); - } - - Mining::OpenStandardMiningChannelSuccess(m) => { - let (vardiff, sender) = self_.safe_lock(|downstream| { - let vardiff = downstream - .vardiff - .get(&m.channel_id) - .expect("Vardiff should be present") - .clone(); - let sender = downstream.sender.clone(); - (vardiff, sender) - })?; - spawn_vardiff_loop(m.channel_id, vardiff, sender); - } - - _ => {} - } - debug!("Sending to downstream: {:?}", message); // returning an error will send the error to the main thread, // and the main thread will drop the downstream from the pool @@ -1160,66 +1132,159 @@ impl Pool { self.downstreams.remove(&downstream_id); } } -pub async fn send_set_target_downstream( + +async fn send_set_target_downstream( sender: Sender, channel_id: u32, - vardiff: Arc>>, + target: Target, ) -> Result<(), PoolError> { - debug!("Sleeping 60s before sending SetTarget for channel_id={channel_id}"); - tokio::time::sleep(Duration::from_secs(60)).await; - - debug!("Attempting try_vardiff for channel_id={channel_id}"); - let new_hashrate = match vardiff.write() { - Ok(mut v) => v.try_vardiff(), - Err(e) => { - error!("Failed to acquire write lock on vardiff for channel_id={channel_id}: {e}"); - return Err(PoolError::PoisonLock(e.to_string())); - } - }?; + debug!("Attempting to send `SetTarget` for channel_id={channel_id}"); - if new_hashrate.is_some() { - debug!("New hashrate detected for channel_id={channel_id}, preparing SetTarget message"); - let target = match vardiff.read() { - Ok(v) => v.target(), - Err(e) => { - error!("Failed to acquire read lock on vardiff for channel_id={channel_id}: {e}"); - return Err(PoolError::PoisonLock(e.to_string())); - } - }; + let target_message = SetTarget { + channel_id, + maximum_target: target.into(), + }; - let target_message = SetTarget { - channel_id, - maximum_target: target.into(), - }; + let mining_msg = Mining::SetTarget(target_message); - let mining_msg = Mining::SetTarget(target_message); - let sv2_frame: StdFrame = AnyMessage::Mining(mining_msg).try_into()?; + info!("Set Target Message: {:?}", mining_msg); - sender.send(sv2_frame.into()).await?; - } else { - debug!("No hashrate adjustment needed for channel_id={channel_id}"); - } + let sv2_frame: StdFrame = AnyMessage::Mining(mining_msg).try_into()?; + + sender.send(sv2_frame.into()).await?; Ok(()) } -pub fn spawn_vardiff_loop( +fn process_extended_channel( + channel_id: u32, + channel: Arc>>, + vardiff_state: Arc>>, + updates: &mut Vec<(u32, Target)>, +) { + let Ok(mut channel_atomic) = channel.write() else { + debug!("Failed to lock extended channel {channel_id}"); + return; + }; + + let Ok(mut vardiff_atomic) = vardiff_state.write() else { + debug!("Failed to lock vardiff state for extended channel {channel_id}"); + return; + }; + + let hashrate = channel_atomic.get_nominal_hashrate(); + let target = channel_atomic.get_target(); + let shares_per_minute = channel_atomic.get_shares_per_minute(); + + let Ok(new_hashrate_opt) = vardiff_atomic.try_vardiff(hashrate, target, shares_per_minute) + else { + debug!("Vardiff computation failed for extended channel {channel_id}"); + return; + }; + + if let Some(new_hashrate) = new_hashrate_opt { + if let Ok(()) = channel_atomic.update_channel(new_hashrate, None) { + let updated_target = channel_atomic.get_target(); + updates.push((channel_id, updated_target.clone())); + + debug!( + "Updated target for extended channel_id={channel_id} to {:?}", + updated_target + ); + } else { + warn!("Failed to update extended channel {channel_id}"); + } + } +} + +fn process_standard_channel( channel_id: u32, - vardiff: Arc>>, + channel: Arc>>, + vardiff_state: &Arc>>, + updates: &mut Vec<(u32, Target)>, +) { + let Ok(mut channel_atomic) = channel.write() else { + debug!("Failed to lock standard channel {channel_id}"); + return; + }; + + let Ok(mut vardiff_atomic) = vardiff_state.write() else { + debug!("Failed to lock vardiff state for standard channel {channel_id}"); + return; + }; + + let hashrate = channel_atomic.get_nominal_hashrate(); + let target = channel_atomic.get_target(); + let shares_per_minute = channel_atomic.get_shares_per_minute(); + + let Ok(new_hashrate_opt) = vardiff_atomic.try_vardiff(hashrate, target, shares_per_minute) + else { + debug!("Vardiff computation failed for standard channel {channel_id}"); + return; + }; + + if let Some(new_hashrate) = new_hashrate_opt { + if let Ok(()) = channel_atomic.update_channel(new_hashrate, None) { + let updated_target = channel_atomic.get_target(); + updates.push((channel_id, updated_target.clone())); + + debug!( + "Updated target for standard channel_id={channel_id} to {:?}", + updated_target + ); + } else { + warn!("Failed to update standard channel {channel_id}"); + } + } +} + +/// This method implements the pool's variable difficulty logic for a single downstream. +/// A downstream can have multiple active channels connected to the pool. +/// Every 60 seconds, this method updates the difficulty state for each channel belonging to the +/// downstream. +async fn spawn_vardiff_loop( + downstream: Arc>, sender: Sender, + downstream_id: u32, ) { - info!("Spawning vardiff loop for channel_id={channel_id}"); - tokio::spawn(async move { - loop { - if let Err(e) = - send_set_target_downstream(sender.clone(), channel_id, vardiff.clone()).await - { - warn!("Vardiff loop exiting for channel_id={channel_id} due to error: {e}"); - break; + info!("Spawning vardiff adjustment loop for downstream: {downstream_id}"); + + loop { + tokio::time::sleep(Duration::from_secs(60)).await; + info!("Starting vardiff updates for downstream: {downstream_id}"); + let mut updates = Vec::new(); + + _ = downstream.safe_lock(|d| { + for (channel_id, vardiff_state) in &d.vardiff { + if let Some(channel) = d.extended_channels.get(channel_id) { + process_extended_channel( + *channel_id, + channel.clone(), + vardiff_state.clone(), + &mut updates, + ); + } + + if let Some(channel) = d.standard_channels.get(channel_id) { + process_standard_channel( + *channel_id, + channel.clone(), + vardiff_state, + &mut updates, + ); + } + } + }); + + for (channel_id, target) in updates { + if let Err(e) = send_set_target_downstream(sender.clone(), channel_id, target).await { + warn!( + "Failed to send SetTarget message downstream for channel {channel_id}: {:?}", + e + ); } } - info!("Vardiff loop terminated for channel_id={channel_id}"); - }); + } } #[cfg(test)] From 96596f7bdd0d0c1baf122f29af088d14fbe0f464 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 16 Jun 2025 23:58:20 +0530 Subject: [PATCH 021/338] break out of monitor loop upon disconnection from client --- roles/pool/src/lib/mining_pool/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 207f5e0cea..df43015609 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -1249,7 +1249,7 @@ async fn spawn_vardiff_loop( ) { info!("Spawning vardiff adjustment loop for downstream: {downstream_id}"); - loop { + 'vardiff_loop: loop { tokio::time::sleep(Duration::from_secs(60)).await; info!("Starting vardiff updates for downstream: {downstream_id}"); let mut updates = Vec::new(); @@ -1278,10 +1278,11 @@ async fn spawn_vardiff_loop( for (channel_id, target) in updates { if let Err(e) = send_set_target_downstream(sender.clone(), channel_id, target).await { - warn!( + error!( "Failed to send SetTarget message downstream for channel {channel_id}: {:?}", e ); + break 'vardiff_loop; } } } From eb3055854d3507653ec2af11fd716bc1145b48f2 Mon Sep 17 00:00:00 2001 From: plebhash Date: Wed, 18 Jun 2025 12:06:38 -0300 Subject: [PATCH 022/338] kill sender channel when client disconnects and use that to stop vardiff spawned loop --- roles/pool/src/lib/mining_pool/mod.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index df43015609..342e517e78 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -276,7 +276,7 @@ impl Downstream { empty_pool_coinbase_outputs, })); - tokio::spawn(spawn_vardiff_loop(self_.clone(), sender, id)); + tokio::spawn(spawn_vardiff_loop(self_.clone(), sender.clone(), id)); let cloned = self_.clone(); @@ -324,11 +324,13 @@ impl Downstream { .map_err(|e| PoolError::PoisonLock(e.to_string())); handle_result!(status_tx, res); error!("Downstream {} disconnected", id); + break; } } } warn!("Downstream connection dropped"); + sender.close(); }); Ok(self_) } @@ -1250,8 +1252,14 @@ async fn spawn_vardiff_loop( info!("Spawning vardiff adjustment loop for downstream: {downstream_id}"); 'vardiff_loop: loop { + if sender.is_closed() { + debug!("Downstream {downstream_id} closed, stopping vardiff loop"); + break; + } + tokio::time::sleep(Duration::from_secs(60)).await; - info!("Starting vardiff updates for downstream: {downstream_id}"); + + debug!("Starting vardiff updates for downstream: {downstream_id}"); let mut updates = Vec::new(); _ = downstream.safe_lock(|d| { From be306cd2e1438487e5a71ad210bf9c9123273764 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 19 Jun 2025 14:59:50 +0530 Subject: [PATCH 023/338] make method and variable name more contextual --- roles/pool/src/lib/mining_pool/mod.rs | 42 +++++++++++++-------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 342e517e78..325e13437a 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -1149,7 +1149,7 @@ async fn send_set_target_downstream( let mining_msg = Mining::SetTarget(target_message); - info!("Set Target Message: {:?}", mining_msg); + info!("Sending SetTarget message to downstream: {:?}", mining_msg); let sv2_frame: StdFrame = AnyMessage::Mining(mining_msg).try_into()?; @@ -1158,35 +1158,35 @@ async fn send_set_target_downstream( Ok(()) } -fn process_extended_channel( +fn run_vardiff_on_extended_channel( channel_id: u32, channel: Arc>>, vardiff_state: Arc>>, updates: &mut Vec<(u32, Target)>, ) { - let Ok(mut channel_atomic) = channel.write() else { + let Ok(mut channel_state) = channel.write() else { debug!("Failed to lock extended channel {channel_id}"); return; }; - let Ok(mut vardiff_atomic) = vardiff_state.write() else { + let Ok(mut vardiff_state) = vardiff_state.write() else { debug!("Failed to lock vardiff state for extended channel {channel_id}"); return; }; - let hashrate = channel_atomic.get_nominal_hashrate(); - let target = channel_atomic.get_target(); - let shares_per_minute = channel_atomic.get_shares_per_minute(); + let hashrate = channel_state.get_nominal_hashrate(); + let target = channel_state.get_target(); + let shares_per_minute = channel_state.get_shares_per_minute(); - let Ok(new_hashrate_opt) = vardiff_atomic.try_vardiff(hashrate, target, shares_per_minute) + let Ok(new_hashrate_opt) = vardiff_state.try_vardiff(hashrate, target, shares_per_minute) else { debug!("Vardiff computation failed for extended channel {channel_id}"); return; }; if let Some(new_hashrate) = new_hashrate_opt { - if let Ok(()) = channel_atomic.update_channel(new_hashrate, None) { - let updated_target = channel_atomic.get_target(); + if let Ok(()) = channel_state.update_channel(new_hashrate, None) { + let updated_target = channel_state.get_target(); updates.push((channel_id, updated_target.clone())); debug!( @@ -1199,35 +1199,35 @@ fn process_extended_channel( } } -fn process_standard_channel( +fn run_vardiff_on_standard_channel( channel_id: u32, channel: Arc>>, vardiff_state: &Arc>>, updates: &mut Vec<(u32, Target)>, ) { - let Ok(mut channel_atomic) = channel.write() else { + let Ok(mut channel_state) = channel.write() else { debug!("Failed to lock standard channel {channel_id}"); return; }; - let Ok(mut vardiff_atomic) = vardiff_state.write() else { + let Ok(mut vardiff_state) = vardiff_state.write() else { debug!("Failed to lock vardiff state for standard channel {channel_id}"); return; }; - let hashrate = channel_atomic.get_nominal_hashrate(); - let target = channel_atomic.get_target(); - let shares_per_minute = channel_atomic.get_shares_per_minute(); + let hashrate = channel_state.get_nominal_hashrate(); + let target = channel_state.get_target(); + let shares_per_minute = channel_state.get_shares_per_minute(); - let Ok(new_hashrate_opt) = vardiff_atomic.try_vardiff(hashrate, target, shares_per_minute) + let Ok(new_hashrate_opt) = vardiff_state.try_vardiff(hashrate, target, shares_per_minute) else { debug!("Vardiff computation failed for standard channel {channel_id}"); return; }; if let Some(new_hashrate) = new_hashrate_opt { - if let Ok(()) = channel_atomic.update_channel(new_hashrate, None) { - let updated_target = channel_atomic.get_target(); + if let Ok(()) = channel_state.update_channel(new_hashrate, None) { + let updated_target = channel_state.get_target(); updates.push((channel_id, updated_target.clone())); debug!( @@ -1265,7 +1265,7 @@ async fn spawn_vardiff_loop( _ = downstream.safe_lock(|d| { for (channel_id, vardiff_state) in &d.vardiff { if let Some(channel) = d.extended_channels.get(channel_id) { - process_extended_channel( + run_vardiff_on_extended_channel( *channel_id, channel.clone(), vardiff_state.clone(), @@ -1274,7 +1274,7 @@ async fn spawn_vardiff_loop( } if let Some(channel) = d.standard_channels.get(channel_id) { - process_standard_channel( + run_vardiff_on_standard_channel( *channel_id, channel.clone(), vardiff_state, From dc01c449c9a33b2485eeb9440fc2549052b79cf8 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 21 Jun 2025 14:05:43 +0530 Subject: [PATCH 024/338] add import fixes --- roles/pool/src/lib/error.rs | 5 +++-- roles/pool/src/lib/mining_pool/mod.rs | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/roles/pool/src/lib/error.rs b/roles/pool/src/lib/error.rs index 2fcbf6adb4..73c7490189 100644 --- a/roles/pool/src/lib/error.rs +++ b/roles/pool/src/lib/error.rs @@ -19,8 +19,9 @@ use std::{ use stratum_common::roles_logic_sv2::{ self, codec_sv2::{self, binary_sv2, noise_sv2}, - {parsers::Mining, -}, vardiff::error::VardiffError}; + parsers::Mining, + vardiff::error::VardiffError, +}; /// Represents various errors that can occur in the pool implementation. #[derive(std::fmt::Debug)] diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 325e13437a..68438e4bad 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -46,14 +46,16 @@ use stratum_common::{ channels::server::{ extended::ExtendedChannel, group::GroupChannel, standard::StandardChannel, }, - codec_sv2, codec_sv2::{ - binary_sv2::U256, HandshakeRole, Responder, StandardEitherFrame, StandardSv2Frame, + self, binary_sv2::U256, HandshakeRole, Responder, StandardEitherFrame, StandardSv2Frame, }, common_properties::{CommonDownstreamData, IsDownstream, IsMiningDownstream}, errors::Error, handlers::mining::{ParseMiningMessagesFromDownstream, SendTo}, - mining_sv2::{ExtendedExtranonce, SetNewPrevHash as SetNewPrevHashMp, MAX_EXTRANONCE_LEN}, + mining_sv2::{ + ExtendedExtranonce, SetNewPrevHash as SetNewPrevHashMp, SetTarget, Target, + MAX_EXTRANONCE_LEN, + }, parsers::{AnyMessage, Mining}, template_distribution_sv2::{ NewTemplate, SetNewPrevHash as SetNewPrevHashTdp, SubmitSolution, From 52606ba61a876aac59a8d0467a36f1baac39ed8c Mon Sep 17 00:00:00 2001 From: plebhash Date: Fri, 20 Jun 2025 17:57:21 -0300 Subject: [PATCH 025/338] run integration tests via cargo nextest --- .github/workflows/integration-tests.yaml | 7 +++++-- test/integration-tests/.config/nextest.toml | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 test/integration-tests/.config/nextest.toml diff --git a/.github/workflows/integration-tests.yaml b/.github/workflows/integration-tests.yaml index 0829786c7e..b653d704e2 100644 --- a/.github/workflows/integration-tests.yaml +++ b/.github/workflows/integration-tests.yaml @@ -24,10 +24,13 @@ jobs: toolchain: stable override: true + - name: Install cargo-nextest + run: cargo install cargo-nextest --locked + - name: Roles Integration Tests run: | - RUST_BACKTRACE=1 RUST_LOG=debug cargo test --manifest-path=test/integration-tests/Cargo.toml --verbose --test '*' -- --nocapture + RUST_BACKTRACE=1 RUST_LOG=debug cargo nextest run --manifest-path=test/integration-tests/Cargo.toml --nocapture - name: SV1 Integration Tests run: | - RUST_BACKTRACE=1 RUST_LOG=debug cargo test --manifest-path=test/integration-tests/Cargo.toml --verbose --test 'sv1' --features sv1 -- --nocapture + RUST_BACKTRACE=1 RUST_LOG=debug cargo nextest run --manifest-path=test/integration-tests/Cargo.toml --features sv1 --nocapture diff --git a/test/integration-tests/.config/nextest.toml b/test/integration-tests/.config/nextest.toml new file mode 100644 index 0000000000..5f5d26a09c --- /dev/null +++ b/test/integration-tests/.config/nextest.toml @@ -0,0 +1,17 @@ +[profile.default] + +# SRI has flaky integration tests, which we are ok to live with for now +# but if a test fails more than 3 times, it's safe to assume it's failing deterministically +# and that's a reliable indication that we shouldn't merge this PR +retries = { backoff = "fixed", count = 3, delay = "2s" } + +# only run one test at a time, which allows a human-friendly experience for inspecting logs +test-threads = 1 + +# label as slow if a test runs for more than 60s +# kill it after 120s +slow-timeout = { period = "60s", terminate-after = 2 } + +# display status for all levels (pass, fail, flaky, slow, etc) +status-level = "all" +final-status-level = "all" \ No newline at end of file From 62663600aea9c86392e8a30dbfb95c735fd6fa24 Mon Sep 17 00:00:00 2001 From: plebhash Date: Fri, 20 Jun 2025 18:00:08 -0300 Subject: [PATCH 026/338] unify integration test jobs the dedicated Sv1 job also runs all the other tests, therefore it's redundant to have two separate commands --- .github/workflows/integration-tests.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/integration-tests.yaml b/.github/workflows/integration-tests.yaml index b653d704e2..b2c95722ba 100644 --- a/.github/workflows/integration-tests.yaml +++ b/.github/workflows/integration-tests.yaml @@ -27,10 +27,6 @@ jobs: - name: Install cargo-nextest run: cargo install cargo-nextest --locked - - name: Roles Integration Tests - run: | - RUST_BACKTRACE=1 RUST_LOG=debug cargo nextest run --manifest-path=test/integration-tests/Cargo.toml --nocapture - - - name: SV1 Integration Tests + - name: Integration Tests run: | RUST_BACKTRACE=1 RUST_LOG=debug cargo nextest run --manifest-path=test/integration-tests/Cargo.toml --features sv1 --nocapture From 210f0725384510bc2fc9d6ed8236a35418a78c62 Mon Sep 17 00:00:00 2001 From: plebhash Date: Fri, 6 Jun 2025 15:10:44 -0300 Subject: [PATCH 027/338] implement channels::server::jobs::ExtendedJob::into_custom_job --- .../v2/roles-logic-sv2/src/channels/server/jobs/extended.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs index adbe18ae0b..0c994e7332 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs @@ -81,7 +81,7 @@ impl<'a> ExtendedJob<'a> { let coinbase_tx_prefix = self.get_coinbase_tx_prefix().inner_as_ref(); let coinbase_tx_suffix = self.get_coinbase_tx_suffix().inner_as_ref(); - let mut serialized_coinbase = Vec::new(); + let mut serialized_coinbase: Vec = vec![]; serialized_coinbase.extend(coinbase_tx_prefix); serialized_coinbase.extend(vec![0; MAX_EXTRANONCE_LEN]); serialized_coinbase.extend(coinbase_tx_suffix); @@ -99,9 +99,9 @@ impl<'a> ExtendedJob<'a> { // because chain_tip is where prev_hash and nbits are coming from if job_min_ntime < chain_tip.min_ntime() { return Err(ExtendedJobError::InvalidMinNTime); + } else { + job_min_ntime } - - job_min_ntime } else { // future jobs are not allowed to be converted into `SetCustomMiningJob` messages return Err(ExtendedJobError::FutureJobNotAllowed); From 3e5a80ba331ce283a476707fc3b016d00a95e42e Mon Sep 17 00:00:00 2001 From: plebhash Date: Thu, 12 Jun 2025 18:21:06 -0300 Subject: [PATCH 028/338] remove SetcustomMiningJob.coinbase_tx_value_remaining following this spec change: https://github.com/stratum-mining/sv2-spec/pull/138 --- protocols/v2/subprotocols/mining/src/set_custom_mining_job.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/protocols/v2/subprotocols/mining/src/set_custom_mining_job.rs b/protocols/v2/subprotocols/mining/src/set_custom_mining_job.rs index 5b03b577cf..7a9c74629f 100644 --- a/protocols/v2/subprotocols/mining/src/set_custom_mining_job.rs +++ b/protocols/v2/subprotocols/mining/src/set_custom_mining_job.rs @@ -44,9 +44,6 @@ pub struct SetCustomMiningJob<'decoder> { pub coinbase_prefix: B0255<'decoder>, /// The coinbase transaction input’s nSequence field. pub coinbase_tx_input_n_sequence: u32, - /// The value, in satoshis, available for spending in coinbase outputs added by the client. - /// Includes both transaction fees and block subsidy. - pub coinbase_tx_value_remaining: u64, /// All the outputs that will be included in the coinbase txs pub coinbase_tx_outputs: B064K<'decoder>, /// The `locktime` field in the coinbase transaction. From 24277ef1a174c2eba5b327c2c7b845971e27556f Mon Sep 17 00:00:00 2001 From: plebhash Date: Thu, 12 Jun 2025 18:21:43 -0300 Subject: [PATCH 029/338] adapt roles_logic_sv2 to removal of SetcustomMiningJob.coinbase_tx_value_remaining --- .../roles-logic-sv2/src/channels/server/jobs/extended.rs | 2 -- .../roles-logic-sv2/src/channels/server/jobs/factory.rs | 1 - protocols/v2/roles-logic-sv2/src/job_creator.rs | 8 +++++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs index 0c994e7332..0a053a05c9 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs @@ -123,7 +123,6 @@ impl<'a> ExtendedJob<'a> { let coinbase_tx_version = deserialized_coinbase.version.0 as u32; let coinbase_tx_locktime = deserialized_coinbase.lock_time.to_consensus_u32(); let coinbase_tx_input_n_sequence = deserialized_coinbase.input[0].sequence.0 as u32; - let coinbase_tx_value_remaining = 0; // this will be removed soon let mut serialized_outputs = Vec::new(); for output in &deserialized_coinbase.output { @@ -145,7 +144,6 @@ impl<'a> ExtendedJob<'a> { coinbase_tx_version, coinbase_prefix, coinbase_tx_input_n_sequence, - coinbase_tx_value_remaining, coinbase_tx_outputs, coinbase_tx_locktime, merkle_path: self.get_merkle_path().clone(), diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs index ef7138a1b2..4a45025d3a 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs @@ -521,7 +521,6 @@ mod tests { coinbase_tx_version: 2, coinbase_prefix: vec![82, 0].try_into().unwrap(), coinbase_tx_input_n_sequence: 4294967295, - coinbase_tx_value_remaining: 0, coinbase_tx_outputs: vec![ 0, 242, 5, 42, 1, 0, 0, 0, 22, 0, 20, 235, 225, 183, 220, 194, 147, 204, 170, 14, 231, 67, 168, 111, 137, 223, 130, 88, 194, 8, 252, 0, 0, 0, 0, 0, 0, 0, 0, 38, 106, diff --git a/protocols/v2/roles-logic-sv2/src/job_creator.rs b/protocols/v2/roles-logic-sv2/src/job_creator.rs index 258669d109..b090947c68 100644 --- a/protocols/v2/roles-logic-sv2/src/job_creator.rs +++ b/protocols/v2/roles-logic-sv2/src/job_creator.rs @@ -146,6 +146,12 @@ pub fn extended_job_from_custom_job( ) -> Result, Error> { let mut outputs = tx_outputs_to_costum_scripts(referenced_job.coinbase_tx_outputs.clone().as_ref()); + + let mut template_value = 0; + for output in &outputs { + template_value += output.value.to_sat(); + } + let mut template = NewTemplate { template_id: 0, future_template: false, @@ -153,7 +159,7 @@ pub fn extended_job_from_custom_job( coinbase_tx_version: referenced_job.coinbase_tx_version, coinbase_prefix: referenced_job.coinbase_prefix.clone(), coinbase_tx_input_sequence: referenced_job.coinbase_tx_input_n_sequence, - coinbase_tx_value_remaining: referenced_job.coinbase_tx_value_remaining, + coinbase_tx_value_remaining: template_value, coinbase_tx_outputs_count: outputs.len() as u32, coinbase_tx_outputs: referenced_job.coinbase_tx_outputs.clone(), coinbase_tx_locktime: referenced_job.coinbase_tx_locktime, From f053b4e6bbe1337fba85dc660473f5baa3077c00 Mon Sep 17 00:00:00 2001 From: plebhash Date: Tue, 17 Jun 2025 15:35:15 -0300 Subject: [PATCH 030/338] stop JDS from serializing AllocateMiningJobToken.Success.coinbase_tx_outputs as a single output --- roles/jd-server/src/lib/job_declarator/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/jd-server/src/lib/job_declarator/mod.rs b/roles/jd-server/src/lib/job_declarator/mod.rs index d563988128..a1dc0fa404 100644 --- a/roles/jd-server/src/lib/job_declarator/mod.rs +++ b/roles/jd-server/src/lib/job_declarator/mod.rs @@ -132,7 +132,7 @@ impl JobDeclaratorDownstream { }; config .get_txout() - .expect("Invalid coinbase output in config")[0] + .expect("Invalid coinbase output in config") .consensus_encode(&mut coinbase_output) .expect("Invalid coinbase output in config"); let coinbase_output_sigops = config From b61844a89067ecf76a32028ec03b5a0a3dc78d3d Mon Sep 17 00:00:00 2001 From: plebhash Date: Thu, 12 Jun 2025 18:22:03 -0300 Subject: [PATCH 031/338] adapt JDC to removal of SetcustomMiningJob.coinbase_tx_value_remaining --- roles/jd-client/src/lib/downstream.rs | 15 +++++++------ roles/jd-client/src/lib/job_declarator/mod.rs | 2 -- .../src/lib/template_receiver/mod.rs | 22 ++++++++++++++----- .../src/lib/upstream_sv2/upstream.rs | 2 -- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/roles/jd-client/src/lib/downstream.rs b/roles/jd-client/src/lib/downstream.rs index a6d4614881..8ecf9ea6bd 100644 --- a/roles/jd-client/src/lib/downstream.rs +++ b/roles/jd-client/src/lib/downstream.rs @@ -30,7 +30,7 @@ use super::{ use async_channel::{bounded, Receiver, SendError, Sender}; use stratum_common::roles_logic_sv2::{ self, - bitcoin::{consensus::Decodable, TxOut}, + bitcoin::{consensus::deserialize, Amount, TxOut}, channel_logic::channel_factory::{OnNewShare, PoolChannelFactory, Share}, codec_sv2, common_messages_sv2::{SetupConnection, SetupConnectionSuccess}, @@ -491,7 +491,7 @@ impl DownstreamMiningNode { pub async fn on_new_template( self_mutex: &Arc>, mut new_template: NewTemplate<'static>, - pool_output: &[u8], + pool_outputs: &[u8], ) -> Result<(), Error> { // Check if a channel is open. If not, just set the flag and return. if !self_mutex.safe_lock(|s| s.status.have_channel()).unwrap() { @@ -499,17 +499,18 @@ impl DownstreamMiningNode { return Ok(()); } - // Decode the pool's coinbase output. - let mut pool_out = &pool_output[0..]; - let pool_output = - TxOut::consensus_decode(&mut pool_out).expect("Upstream sent an invalid coinbase"); + let mut deserialized_outputs: Vec = deserialize(pool_outputs).unwrap(); + + // we know the first output is where the template revenue must + // be allocated + deserialized_outputs[0].value = Amount::from_sat(new_template.coinbase_tx_value_remaining); // Update the channel factory with the new template and pool outputs and get messages to // send downstream. let to_send = self_mutex .safe_lock(|s| { let channel = s.status.get_channel(); - channel.update_pool_outputs(vec![pool_output]); + channel.update_pool_outputs(deserialized_outputs); channel.on_new_template(&mut new_template) }) .unwrap()?; diff --git a/roles/jd-client/src/lib/job_declarator/mod.rs b/roles/jd-client/src/lib/job_declarator/mod.rs index bd31a2f114..2f936ab835 100644 --- a/roles/jd-client/src/lib/job_declarator/mod.rs +++ b/roles/jd-client/src/lib/job_declarator/mod.rs @@ -432,7 +432,6 @@ impl JobDeclarator { template.coinbase_tx_version, template.coinbase_prefix, template.coinbase_tx_input_sequence, - template.coinbase_tx_value_remaining, pool_outs, template.coinbase_tx_locktime, template.template_id @@ -553,7 +552,6 @@ impl JobDeclarator { template.coinbase_tx_version, template.coinbase_prefix, template.coinbase_tx_input_sequence, - template.coinbase_tx_value_remaining, pool_outs, template.coinbase_tx_locktime, template.template_id, diff --git a/roles/jd-client/src/lib/template_receiver/mod.rs b/roles/jd-client/src/lib/template_receiver/mod.rs index c0d5685826..e59f36d1d6 100644 --- a/roles/jd-client/src/lib/template_receiver/mod.rs +++ b/roles/jd-client/src/lib/template_receiver/mod.rs @@ -16,8 +16,8 @@ use stratum_common::{ roles_logic_sv2::{ self, bitcoin::{ - consensus::{deserialize, Encodable}, - Transaction, TxOut, + consensus::{deserialize, serialize, Encodable}, + Amount, Transaction, TxOut, }, codec_sv2::{HandshakeRole, Initiator, StandardEitherFrame, StandardSv2Frame}, handlers::{template_distribution::ParseTemplateDistributionMessagesFromServer, SendTo_}, @@ -301,13 +301,13 @@ impl TemplateRx { .unwrap(); // Get the pool's coinbase output from the last token. let token = last_token.clone().unwrap(); - let pool_output = token.coinbase_output.to_vec(); + let pool_outputs = token.coinbase_output.to_vec(); // Notify the downstream mining node about the new template. super::downstream::DownstreamMiningNode::on_new_template( &down, m.clone(), - &pool_output[..], + &pool_outputs[..], ) .await .unwrap(); @@ -359,7 +359,17 @@ impl TemplateRx { // Extract mining token and pool coinbase output from the token. let mining_token = token.mining_job_token.to_vec(); - let pool_coinbase_out = token.coinbase_output.to_vec(); + let pool_coinbase_outputs = token.coinbase_output.to_vec(); + + let mut deserialized_outputs: Vec = + deserialize(&pool_coinbase_outputs).unwrap(); + + // we know the first output is where the template revenue must + // be allocated + deserialized_outputs[0].value = + Amount::from_sat(m.coinbase_tx_value_remaining); + + let reserialized_outputs = serialize(&deserialized_outputs); // If connected to a pool, notify the Job Declarator with the // complete template information (including transactions). @@ -370,7 +380,7 @@ impl TemplateRx { mining_token, transactions_data, excess_data, - pool_coinbase_out, + reserialized_outputs, ) .await; } diff --git a/roles/jd-client/src/lib/upstream_sv2/upstream.rs b/roles/jd-client/src/lib/upstream_sv2/upstream.rs index 1b4e7eae56..6391115197 100644 --- a/roles/jd-client/src/lib/upstream_sv2/upstream.rs +++ b/roles/jd-client/src/lib/upstream_sv2/upstream.rs @@ -338,7 +338,6 @@ impl Upstream { coinbase_tx_version: u32, coinbase_prefix: binary_sv2::B0255<'static>, coinbase_tx_input_n_sequence: u32, - coinbase_tx_value_remaining: u64, coinbase_tx_outs: Vec, coinbase_tx_locktime: u32, template_id: u64, @@ -374,7 +373,6 @@ impl Upstream { coinbase_tx_version, coinbase_prefix, coinbase_tx_input_n_sequence, - coinbase_tx_value_remaining, coinbase_tx_outputs: coinbase_tx_outs.try_into().unwrap(), coinbase_tx_locktime, merkle_path, From 206b2378aebbc444962e2721358cb7ef1bf7156d Mon Sep 17 00:00:00 2001 From: plebhash Date: Sat, 21 Jun 2025 16:31:15 -0300 Subject: [PATCH 032/338] remove redundant fields from AllocateMiningJobToken.Success removed from spec via https://github.com/stratum-mining/sv2-spec/pull/140 --- .../job-declaration/src/allocate_mining_job_token.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs b/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs index f8e2ecf1ca..443bb06dcd 100644 --- a/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs +++ b/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs @@ -25,12 +25,6 @@ pub struct AllocateMiningJobTokenSuccess<'decoder> { /// A token that makes the JDC eligible for committing a mining job for approval/transactions /// declaration or for identifying custom mining job on mining connection. pub mining_job_token: B0255<'decoder>, - /// The maximum additional serialized bytes which the JDS will add in coinbase transaction - /// outputs. - pub coinbase_output_max_additional_size: u32, - /// The maximum additional sigops which the JDS will add in coinbase transaction - /// outputs. - pub coinbase_output_max_additional_sigops: u16, /// Bitcoin transaction outputs added by JDS. pub coinbase_output: B064K<'decoder>, } From 73eb53b3e896848304833a8e41e3712605dcd23a Mon Sep 17 00:00:00 2001 From: plebhash Date: Sat, 21 Jun 2025 16:35:20 -0300 Subject: [PATCH 033/338] adapt JDS to removal of redundant fields from AllocateMiningJobToken.Success --- roles/jd-server/src/lib/job_declarator/message_handler.rs | 2 -- roles/jd-server/src/lib/job_declarator/mod.rs | 7 ------- 2 files changed, 9 deletions(-) diff --git a/roles/jd-server/src/lib/job_declarator/message_handler.rs b/roles/jd-server/src/lib/job_declarator/message_handler.rs index 254600bc23..da050e3aee 100644 --- a/roles/jd-server/src/lib/job_declarator/message_handler.rs +++ b/roles/jd-server/src/lib/job_declarator/message_handler.rs @@ -59,9 +59,7 @@ impl ParseJobDeclarationMessagesFromDownstream for JobDeclaratorDownstream { let message_success = AllocateMiningJobTokenSuccess { request_id: message.request_id, mining_job_token: token.to_le_bytes().to_vec().try_into().unwrap(), - coinbase_output_max_additional_size: 100, coinbase_output: self.coinbase_output.clone().try_into().unwrap(), - coinbase_output_max_additional_sigops: self.coinbase_output_sigops, }; let message_enum = JobDeclaration::AllocateMiningJobTokenSuccess(message_success); info!( diff --git a/roles/jd-server/src/lib/job_declarator/mod.rs b/roles/jd-server/src/lib/job_declarator/mod.rs index a1dc0fa404..64a23a572b 100644 --- a/roles/jd-server/src/lib/job_declarator/mod.rs +++ b/roles/jd-server/src/lib/job_declarator/mod.rs @@ -97,7 +97,6 @@ pub struct JobDeclaratorDownstream { #[allow(dead_code)] // TODO: use coinbase output coinbase_output: Vec, - coinbase_output_sigops: u16, token_to_job_map: HashMap, BuildNoHashHasher>, tokens: Id, public_key: Secp256k1PublicKey, @@ -135,18 +134,12 @@ impl JobDeclaratorDownstream { .expect("Invalid coinbase output in config") .consensus_encode(&mut coinbase_output) .expect("Invalid coinbase output in config"); - let coinbase_output_sigops = config - .get_txout() - .expect("Invalid coinbase output in config")[0] - .script_pubkey - .count_sigops() as u16; Self { full_template_mode_required, receiver, sender, coinbase_output, - coinbase_output_sigops, token_to_job_map, tokens, public_key: *config.authority_public_key(), From 891e04e7fd434ef8eea8629d753c0a8c0ece5249 Mon Sep 17 00:00:00 2001 From: plebhash Date: Sat, 21 Jun 2025 16:46:09 -0300 Subject: [PATCH 034/338] adapt JDC to removal of redundant fields from AllocateMiningJobToken.Success --- .../src/lib/template_receiver/mod.rs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/roles/jd-client/src/lib/template_receiver/mod.rs b/roles/jd-client/src/lib/template_receiver/mod.rs index e59f36d1d6..13d25d6e87 100644 --- a/roles/jd-client/src/lib/template_receiver/mod.rs +++ b/roles/jd-client/src/lib/template_receiver/mod.rs @@ -17,7 +17,7 @@ use stratum_common::{ self, bitcoin::{ consensus::{deserialize, serialize, Encodable}, - Amount, Transaction, TxOut, + Amount, TxOut, }, codec_sv2::{HandshakeRole, Initiator, StandardEitherFrame, StandardSv2Frame}, handlers::{template_distribution::ParseTemplateDistributionMessagesFromServer, SendTo_}, @@ -194,19 +194,10 @@ impl TemplateRx { JobDeclarator::get_last_token(&jd).await } else { // This is when JDC is doing solo mining - let deserialized_miner_coinbase_output: Transaction = - deserialize(miner_coinbase_output).expect("Invalid coinbase output"); - let miner_coinbase_output_sigops = deserialized_miner_coinbase_output - .output - .iter() - .map(|output| output.script_pubkey.count_sigops() as u16) - .sum::(); AllocateMiningJobTokenSuccess { request_id: 0, mining_job_token: vec![0; 32].try_into().unwrap(), - coinbase_output_max_additional_size: 100, - coinbase_output_max_additional_sigops: miner_coinbase_output_sigops, coinbase_output: miner_coinbase_output.to_vec().try_into().unwrap(), } } @@ -252,16 +243,25 @@ impl TemplateRx { // Send CoinbaseOutputConstraints to the Template Provider if not already sent. if !coinbase_output_constraints_sent { coinbase_output_constraints_sent = true; + + let jds_coinbase_outputs = + last_token.clone().unwrap().coinbase_output.to_vec(); + let deserialized_jds_coinbase_outputs: Vec = + deserialize(&jds_coinbase_outputs).expect("Invalid coinbase output"); + + let mut coinbase_output_max_additional_size = 0; + let mut coinbase_output_max_additional_sigops = 0; + + for output in deserialized_jds_coinbase_outputs { + coinbase_output_max_additional_size += output.size(); + coinbase_output_max_additional_sigops += + output.script_pubkey.count_sigops() as u16; + } + Self::send_coinbase_output_constraints( &self_mutex, - last_token - .clone() - .unwrap() - .coinbase_output_max_additional_size, - last_token - .clone() - .unwrap() - .coinbase_output_max_additional_sigops, + coinbase_output_max_additional_size as u32, + coinbase_output_max_additional_sigops, ) .await; } From 0b4d0485dc4fbcfd3984b212b095800ec6651737 Mon Sep 17 00:00:00 2001 From: plebhash Date: Sat, 21 Jun 2025 17:18:23 -0300 Subject: [PATCH 035/338] rename AllocateMiningJobToken.Succes.coinbase_tx_outputs with plural spec change introduced via https://github.com/stratum-mining/sv2-spec/pull/138 --- .../job-declaration/src/allocate_mining_job_token.rs | 2 +- roles/jd-client/src/lib/template_receiver/mod.rs | 8 ++++---- roles/jd-server/src/lib/job_declarator/message_handler.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs b/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs index 443bb06dcd..4329fb52af 100644 --- a/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs +++ b/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs @@ -26,5 +26,5 @@ pub struct AllocateMiningJobTokenSuccess<'decoder> { /// declaration or for identifying custom mining job on mining connection. pub mining_job_token: B0255<'decoder>, /// Bitcoin transaction outputs added by JDS. - pub coinbase_output: B064K<'decoder>, + pub coinbase_outputs: B064K<'decoder>, } diff --git a/roles/jd-client/src/lib/template_receiver/mod.rs b/roles/jd-client/src/lib/template_receiver/mod.rs index 13d25d6e87..635779b690 100644 --- a/roles/jd-client/src/lib/template_receiver/mod.rs +++ b/roles/jd-client/src/lib/template_receiver/mod.rs @@ -198,7 +198,7 @@ impl TemplateRx { AllocateMiningJobTokenSuccess { request_id: 0, mining_job_token: vec![0; 32].try_into().unwrap(), - coinbase_output: miner_coinbase_output.to_vec().try_into().unwrap(), + coinbase_outputs: miner_coinbase_output.to_vec().try_into().unwrap(), } } } @@ -245,7 +245,7 @@ impl TemplateRx { coinbase_output_constraints_sent = true; let jds_coinbase_outputs = - last_token.clone().unwrap().coinbase_output.to_vec(); + last_token.clone().unwrap().coinbase_outputs.to_vec(); let deserialized_jds_coinbase_outputs: Vec = deserialize(&jds_coinbase_outputs).expect("Invalid coinbase output"); @@ -301,7 +301,7 @@ impl TemplateRx { .unwrap(); // Get the pool's coinbase output from the last token. let token = last_token.clone().unwrap(); - let pool_outputs = token.coinbase_output.to_vec(); + let pool_outputs = token.coinbase_outputs.to_vec(); // Notify the downstream mining node about the new template. super::downstream::DownstreamMiningNode::on_new_template( @@ -359,7 +359,7 @@ impl TemplateRx { // Extract mining token and pool coinbase output from the token. let mining_token = token.mining_job_token.to_vec(); - let pool_coinbase_outputs = token.coinbase_output.to_vec(); + let pool_coinbase_outputs = token.coinbase_outputs.to_vec(); let mut deserialized_outputs: Vec = deserialize(&pool_coinbase_outputs).unwrap(); diff --git a/roles/jd-server/src/lib/job_declarator/message_handler.rs b/roles/jd-server/src/lib/job_declarator/message_handler.rs index da050e3aee..0ed61ba36f 100644 --- a/roles/jd-server/src/lib/job_declarator/message_handler.rs +++ b/roles/jd-server/src/lib/job_declarator/message_handler.rs @@ -59,7 +59,7 @@ impl ParseJobDeclarationMessagesFromDownstream for JobDeclaratorDownstream { let message_success = AllocateMiningJobTokenSuccess { request_id: message.request_id, mining_job_token: token.to_le_bytes().to_vec().try_into().unwrap(), - coinbase_output: self.coinbase_output.clone().try_into().unwrap(), + coinbase_outputs: self.coinbase_output.clone().try_into().unwrap(), }; let message_enum = JobDeclaration::AllocateMiningJobTokenSuccess(message_success); info!( From 7b6b137d19b3e4dc8c9eeda898feff4726077380 Mon Sep 17 00:00:00 2001 From: plebhash Date: Thu, 19 Jun 2025 14:34:22 -0300 Subject: [PATCH 036/338] cover crate examples on CI --- .github/workflows/test.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 734c4552d7..3b92a4152d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -44,6 +44,19 @@ jobs: run: | cargo run --manifest-path=protocols/v2/framing-sv2/Cargo.toml --example sv2_frame + - name: Run codec-sv2 examples + run: | + cargo run --manifest-path=protocols/v2/codec-sv2/Cargo.toml --example unencrypted + cargo run --manifest-path=protocols/v2/codec-sv2/Cargo.toml --example encrypted --features=noise_sv2 + + - name: Run binary-sv2 examples + run: | + cargo run --manifest-path=protocols/v2/binary-sv2/Cargo.toml --example encode_decode + + - name: Run noise-sv2 examples + run: | + cargo run --manifest-path=protocols/v2/noise-sv2/Cargo.toml --example handshake + - name: fuzz tests run: | if [ ${{ matrix.os }} == "ubuntu-latest" ]; then From 1745a2ee1415e63cc79b40869a1a2ead601c4ee2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 25 Jun 2025 09:52:23 +0530 Subject: [PATCH 037/338] remove redundant state field in connection struct and make it leaner --- .../roles-utils/network-helpers/src/noise_connection.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/roles/roles-utils/network-helpers/src/noise_connection.rs b/roles/roles-utils/network-helpers/src/noise_connection.rs index 69f3e944e2..0ef00bfefc 100644 --- a/roles/roles-utils/network-helpers/src/noise_connection.rs +++ b/roles/roles-utils/network-helpers/src/noise_connection.rs @@ -17,10 +17,7 @@ use tokio::{ }; use tracing::{debug, error}; -#[derive(Debug)] -pub struct Connection { - pub state: codec_sv2::State, -} +pub struct Connection; async fn send_message<'a, Message: Serialize + Deserialize<'a> + GetSize + Send + 'static>( writer: &mut OwnedWriteHalf, @@ -142,11 +139,11 @@ impl Connection { // Spawn Reader let read_state = state.clone(); - Self::spawn_reader(reader, read_state, address, sender_incoming.clone()); + Self::spawn_reader(reader, read_state, address, sender_incoming); // Spawn Writer let write_state = state; - Self::spawn_writer(writer, write_state, address, receiver_outgoing.clone()); + Self::spawn_writer(writer, write_state, address, receiver_outgoing); Ok((receiver_incoming, sender_outgoing)) } From 13a8a3e430bc50fc0a8248b10923becb4ad6dbea Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 25 Jun 2025 09:52:46 +0530 Subject: [PATCH 038/338] add connection state --- .../network-helpers/src/noise_connection.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/roles/roles-utils/network-helpers/src/noise_connection.rs b/roles/roles-utils/network-helpers/src/noise_connection.rs index 0ef00bfefc..1fff7793e9 100644 --- a/roles/roles-utils/network-helpers/src/noise_connection.rs +++ b/roles/roles-utils/network-helpers/src/noise_connection.rs @@ -19,6 +19,22 @@ use tracing::{debug, error}; pub struct Connection; +struct ConnectionState { + sender_incoming: Sender>, + receiver_incoming: Receiver>, + sender_outgoing: Sender>, + receiver_outgoing: Receiver>, +} + +impl ConnectionState { + fn close_all(&self) { + self.sender_incoming.close(); + self.receiver_incoming.close(); + self.sender_outgoing.close(); + self.receiver_outgoing.close(); + } +} + async fn send_message<'a, Message: Serialize + Deserialize<'a> + GetSize + Send + 'static>( writer: &mut OwnedWriteHalf, msg: StandardEitherFrame, From 537345db6d009bfc1bed3a82a7419f6785a4c7c3 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 25 Jun 2025 10:22:30 +0530 Subject: [PATCH 039/338] explicitly close all connection --- .../network-helpers/src/noise_connection.rs | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/roles/roles-utils/network-helpers/src/noise_connection.rs b/roles/roles-utils/network-helpers/src/noise_connection.rs index 1fff7793e9..f32549afa3 100644 --- a/roles/roles-utils/network-helpers/src/noise_connection.rs +++ b/roles/roles-utils/network-helpers/src/noise_connection.rs @@ -6,7 +6,7 @@ use codec_sv2::{ noise_sv2::{ELLSWIFT_ENCODING_SIZE, INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE}, HandShakeFrame, HandshakeRole, StandardEitherFrame, StandardNoiseDecoder, State, }; -use std::convert::TryInto; +use std::{convert::TryInto, sync::Arc}; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::{ @@ -153,13 +153,20 @@ impl Connection { let (sender_incoming, receiver_incoming) = unbounded(); let (sender_outgoing, receiver_outgoing) = unbounded(); + let conn_state = Arc::new(ConnectionState { + sender_incoming, + receiver_incoming: receiver_incoming.clone(), + sender_outgoing: sender_outgoing.clone(), + receiver_outgoing, + }); + // Spawn Reader let read_state = state.clone(); - Self::spawn_reader(reader, read_state, address, sender_incoming); + Self::spawn_reader(reader, read_state, address, conn_state.clone()); // Spawn Writer let write_state = state; - Self::spawn_writer(writer, write_state, address, receiver_outgoing); + Self::spawn_writer(writer, write_state, address, conn_state); Ok((receiver_incoming, sender_outgoing)) } @@ -168,8 +175,9 @@ impl Connection { mut reader: OwnedReadHalf, mut reader_state: State, address: std::net::SocketAddr, - sender_incoming: Sender>, + conn_state: Arc>, ) -> task::JoinHandle<()> { + let sender_incoming = conn_state.sender_incoming.clone(); task::spawn(async move { let mut decoder = StandardNoiseDecoder::::new(); loop { @@ -177,6 +185,7 @@ impl Connection { Ok(frame) => { if sender_incoming.send(frame).await.is_err() { error!("Shutting down reader for {}", address); + conn_state.close_all(); break; } } @@ -185,7 +194,7 @@ impl Connection { } Err(e) => { error!("Reader shutting down due to error: {:?}", e); - sender_incoming.close(); + conn_state.close_all(); break; } } @@ -197,8 +206,9 @@ impl Connection { mut writer: OwnedWriteHalf, mut write_state: State, address: std::net::SocketAddr, - receiver_outgoing: Receiver>, + conn_state: Arc>, ) -> task::JoinHandle<()> { + let receiver_outgoing = conn_state.receiver_outgoing.clone(); task::spawn(async move { let mut encoder = codec_sv2::NoiseEncoder::::new(); while let Ok(frame) = receiver_outgoing.recv().await { @@ -207,10 +217,12 @@ impl Connection { { error!("Error while writing to client {}: {:?}", address, e); let _ = writer.shutdown().await; + conn_state.close_all(); break; } } let _ = writer.shutdown().await; + conn_state.close_all(); }) } } From a2e86a5295a9152596c319647f8a82abc7b1e8cf Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Tue, 24 Jun 2025 13:54:25 -0400 Subject: [PATCH 040/338] update coinbase output length calculation to sum sizes of outputs --- roles/pool/src/lib/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/roles/pool/src/lib/mod.rs b/roles/pool/src/lib/mod.rs index e8872b8695..b44f6640a4 100644 --- a/roles/pool/src/lib/mod.rs +++ b/roles/pool/src/lib/mod.rs @@ -72,7 +72,10 @@ impl PoolSv2 { // Prepare coinbase output information required by TemplateRx. let coinbase_output_result = get_coinbase_output(&config)?; - let coinbase_output_len = coinbase_output_result.len() as u32; + let coinbase_output_len = coinbase_output_result + .iter() + .map(|output| output.size() as u32) + .sum(); let tp_authority_public_key = config.tp_authority_public_key().cloned(); let coinbase_output_sigops = coinbase_output_result .iter() From 09c8b6fe7019e5efe290f3278e28518acccca8fb Mon Sep 17 00:00:00 2001 From: GitGab19 Date: Thu, 19 Jun 2025 11:24:26 +0200 Subject: [PATCH 041/338] Fix arithmetic overflow in hash_rate_from_target function by using checked_add for target_plus_one --- protocols/v2/roles-logic-sv2/src/utils.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/utils.rs b/protocols/v2/roles-logic-sv2/src/utils.rs index 84fd0ca2ce..081f6a9a05 100644 --- a/protocols/v2/roles-logic-sv2/src/utils.rs +++ b/protocols/v2/roles-logic-sv2/src/utils.rs @@ -409,8 +409,9 @@ pub fn hash_rate_from_target(target: U256<'static>, share_per_min: f64) -> Resul return Err(Error::HashrateError(InputError::DivisionByZero)); } let shares_occurrency_frequence = from_u128_to_u256(shares_occurrency_frequence); - let target_plus_one = - U256Primitive::from_big_endian(target_arr.as_ref()) + U256Primitive::one(); + let target_plus_one = U256Primitive::from_big_endian(target_arr.as_ref()) + .checked_add(U256Primitive::one()) + .ok_or(Error::HashrateError(InputError::ArithmeticOverflow))?; let denominator = target_plus_one .checked_mul(shares_occurrency_frequence) .and_then(|e| e.checked_div(U256Primitive::from(100))) From a25eb0905f0ab32bbfed79f14fad580dfa917a8e Mon Sep 17 00:00:00 2001 From: GitGab19 Date: Thu, 19 Jun 2025 11:26:42 +0200 Subject: [PATCH 042/338] Add test for hash_rate_from_target with maximum target value This test checks for an ArithmeticOverflow error when the maximum U256 target is used in the hash_rate_from_target function, ensuring robustness against overflow scenarios. --- protocols/v2/roles-logic-sv2/src/utils.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/protocols/v2/roles-logic-sv2/src/utils.rs b/protocols/v2/roles-logic-sv2/src/utils.rs index 081f6a9a05..c4d2dbbb8e 100644 --- a/protocols/v2/roles-logic-sv2/src/utils.rs +++ b/protocols/v2/roles-logic-sv2/src/utils.rs @@ -1190,4 +1190,26 @@ mod tests { max_difficulty ); } + + #[test] + fn test_hash_rate_from_target_with_max_target() { + use codec_sv2::binary_sv2::U256; + // This is the maximum value for a 256-bit unsigned integer + let max_u128 = 340282366920938463463374607431768211455u128; + // Compose the bytes for U256::MAX + let mut max_bytes = [0u8; 32]; + max_bytes[..16].copy_from_slice(&max_u128.to_be_bytes()); + max_bytes[16..].copy_from_slice(&max_u128.to_be_bytes()); + let target = U256::from(max_bytes); + let share_per_min = 4.0; + let result = hash_rate_from_target(target, share_per_min); + assert!( + matches!( + result, + Err(Error::HashrateError(InputError::ArithmeticOverflow)) + ), + "Expected ArithmeticOverflow error, got: {:?}", + result + ); + } } From a9a2a3b93629617d01b873ed40b33013461e4295 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 26 Jun 2025 14:57:14 -0400 Subject: [PATCH 043/338] run clippy on the entire codebase --- benches/benches/src/sv1/lib/client.rs | 11 ++- protocols/v1/examples/client_and_server.rs | 14 ++-- protocols/v1/src/error.rs | 22 +++--- protocols/v1/src/methods/client_to_server.rs | 10 +-- protocols/v1/src/methods/server_to_client.rs | 4 +- protocols/v1/src/utils.rs | 2 +- .../v2/binary-sv2/derive_codec/src/lib.rs | 4 +- protocols/v2/binary-sv2/src/lib.rs | 3 +- protocols/v2/codec-sv2/src/error.rs | 12 ++-- protocols/v2/framing-sv2/src/error.rs | 5 +- protocols/v2/roles-logic-sv2/src/errors.rs | 71 +++++++++---------- .../v2/roles-logic-sv2/src/job_dispatcher.rs | 2 +- protocols/v2/roles-logic-sv2/src/parsers.rs | 3 +- protocols/v2/roles-logic-sv2/src/utils.rs | 2 +- .../roles-logic-sv2/src/vardiff/test/mod.rs | 5 +- protocols/v2/sv2-ffi/src/lib.rs | 8 +-- roles/jd-client/src/lib/error.rs | 28 ++++---- roles/jd-client/src/lib/mod.rs | 2 +- .../src/lib/template_receiver/mod.rs | 2 +- .../src/lib/upstream_sv2/upstream.rs | 2 +- roles/jd-server/src/lib/error.rs | 26 +++---- roles/mining-proxy/src/lib/error.rs | 6 +- roles/pool/src/lib/error.rs | 28 ++++---- roles/pool/src/lib/mining_pool/mod.rs | 3 +- .../network-helpers/src/sv1_connection.rs | 2 +- roles/roles-utils/rpc/src/mini_rpc_client.rs | 2 +- roles/test-utils/mining-device/src/lib/mod.rs | 2 +- .../src/lib/downstream_sv1/diff_management.rs | 4 +- .../src/lib/downstream_sv1/downstream.rs | 2 +- roles/translator/src/lib/error.rs | 40 +++++------ roles/translator/src/lib/mod.rs | 2 +- .../src/lib/upstream_sv2/upstream.rs | 3 +- roles/translator/src/main.rs | 2 +- .../lib/template_provider.rs | 26 +++---- test/integration-tests/lib/utils.rs | 5 +- .../examples/variable_sized_messages.rs | 2 +- utils/key-utils/src/main.rs | 4 +- 37 files changed, 176 insertions(+), 195 deletions(-) diff --git a/benches/benches/src/sv1/lib/client.rs b/benches/benches/src/sv1/lib/client.rs index 3ef5fc38ef..806495b1e1 100644 --- a/benches/benches/src/sv1/lib/client.rs +++ b/benches/benches/src/sv1/lib/client.rs @@ -3,7 +3,6 @@ //! messages. It also provides a trait implementation for handling server messages and managing //! client state. -use std::fmt::Write; use v1::{ client_to_server, error::Error, @@ -27,7 +26,9 @@ pub struct Client { impl Client { pub fn new(client_id: u32) -> Client { - let client = Client { + + + Client { client_id, extranonce1: extranonce_from_hex("00000000"), extranonce2_size: 4, @@ -37,9 +38,7 @@ impl Client { last_notify: None, sented_authorize_request: vec![], authorized: vec![], - }; - - client + } } // this is what we want to benchmark @@ -262,6 +261,6 @@ mod utils { } pub fn encode_hex(bytes: &[u8]) -> String { - bytes.iter().map(|b| format!("{:02x}", b)).collect() + bytes.iter().map(|b| format!("{b:02x}")).collect() } } diff --git a/protocols/v1/examples/client_and_server.rs b/protocols/v1/examples/client_and_server.rs index e99f047693..4812ed0c3e 100644 --- a/protocols/v1/examples/client_and_server.rs +++ b/protocols/v1/examples/client_and_server.rs @@ -86,12 +86,12 @@ fn server_pool_listen(listener: TcpListener) { loop { match listener.accept() { Ok((stream, addr)) => { - println!("SERVER - Accepting from: {}", addr); + println!("SERVER - Accepting from: {addr}"); let server = Server::new(stream); let _ = Arc::new(Mutex::new(server)); } Err(e) => { - eprintln!("SERVER - Accept error: {}", e); + eprintln!("SERVER - Accept error: {e}"); break; } } @@ -139,7 +139,7 @@ impl Server<'_> { thread::spawn(move || loop { if let Ok(mut self_) = cloned.try_lock() { if let Ok(line) = self_.receiver_incoming.try_recv() { - println!("SERVER - message: {}", line); + println!("SERVER - message: {line}"); let message: Result = serde_json::from_str(&line); if let Ok(message) = message { if let Ok(Some(resp)) = self_.handle_message(message) { @@ -171,7 +171,7 @@ impl Server<'_> { run_time -= notify_time as i32; if run_time <= 0 { - println!("Test Success - ran for {} seconds", TEST_DURATION); + println!("Test Success - ran for {TEST_DURATION} seconds"); exit(0) } } @@ -305,7 +305,7 @@ impl Client<'static> { thread::sleep(Duration::from_secs(1)); match TcpStream::connect(socket) { Ok(st) => { - println!("CLIENT - connected to server at {}", socket); + println!("CLIENT - connected to server at {socket}"); let (sender_incoming, receiver_incoming) = mpsc::channel::(); let (sender_outgoing, receiver_outgoing) = mpsc::channel::(); @@ -560,7 +560,7 @@ impl<'a> IsClient<'a> for Client<'a> { &mut self, message: Message, ) -> Result, Error<'a>> { - println!("{:?}", message); + println!("{message:?}"); Ok(None) } } @@ -621,7 +621,7 @@ mod utils { pub fn encode_hex(bytes: &[u8]) -> String { let mut s = String::with_capacity(bytes.len() * 2); for &b in bytes { - write!(&mut s, "{:02x}", b).unwrap(); + write!(&mut s, "{b:02x}").unwrap(); } s } diff --git a/protocols/v1/src/error.rs b/protocols/v1/src/error.rs index a4dbaa342d..8d725d71f4 100644 --- a/protocols/v1/src/error.rs +++ b/protocols/v1/src/error.rs @@ -38,15 +38,14 @@ impl std::fmt::Display for Error<'_> { match self { Error::BadBytesConvert(ref e) => write!( f, - "Bad U256 or B032 conversion (U256 length must be exactly 32 bytes; B032 length must be <= 32 bytes): {:?}", - e + "Bad U256 or B032 conversion (U256 length must be exactly 32 bytes; B032 length must be <= 32 bytes): {e:?}" ), - Error::BTCHashError(ref e) => write!(f, "Bitcoin Hashes Error: `{:?}`", e), - Error::HexError(ref e) => write!(f, "Bad hex encode/decode: `{:?}`", e), + Error::BTCHashError(ref e) => write!(f, "Bitcoin Hashes Error: `{e:?}`"), + Error::HexError(ref e) => write!(f, "Bad hex encode/decode: `{e:?}`"), Error::IncorrectClientStatus(s) => { - write!(f, "Client status is incompatible with message: `{}`", s) + write!(f, "Client status is incompatible with message: `{s}`") } - Error::Infallible(ref e) => write!(f, "Infallible error{:?}", e), + Error::Infallible(ref e) => write!(f, "Infallible error{e:?}"), Error::InvalidJsonRpcMessageKind => write!( f, "Server received a `json_rpc` response when it should only receive requests" @@ -54,8 +53,7 @@ impl std::fmt::Display for Error<'_> { Error::InvalidReceiver(ref e) => write!( f, "Client received an invalid message that was intended to be sent from the - client to the server, NOT from the server to the client. Invalid message: `{:?}`", - e + client to the server, NOT from the server to the client. Invalid message: `{e:?}`" ), Error::InvalidSubmission => { write!(f, "Server received an invalid `mining.submit` message.") @@ -63,16 +61,14 @@ impl std::fmt::Display for Error<'_> { Error::Method(ref e) => { write!( f, - "Error converting valid `json_rpc` SV1 message: `{:?}`", - e + "Error converting valid `json_rpc` SV1 message: `{e:?}`" ) } Error::UnauthorizedClient(id) => write!( f, - "Client with id `{}` expected to be authorized but is unauthorized.", - id + "Client with id `{id}` expected to be authorized but is unauthorized." ), - Error::UnknownID(e) => write!(f, "Server did not recognize the client id: `{}`.", e), + Error::UnknownID(e) => write!(f, "Server did not recognize the client id: `{e}`."), Error::InvalidVersionMask(e) => write!(f, "First 3 bits of version rolling mask must be 0 and last 13 bits of version rolling mask must be 0. Version rolling mask is: `{:b}`.", e.0), } } diff --git a/protocols/v1/src/methods/client_to_server.rs b/protocols/v1/src/methods/client_to_server.rs index 6d44d9c8a9..d924f38156 100644 --- a/protocols/v1/src/methods/client_to_server.rs +++ b/protocols/v1/src/methods/client_to_server.rs @@ -226,12 +226,12 @@ impl Arbitrary for Submit<'static> { fn arbitrary(g: &mut Gen) -> Self { let mut extra = Vec::::arbitrary(g); extra.resize(32, 0); - println!("\nEXTRA: {:?}\n", extra); + println!("\nEXTRA: {extra:?}\n"); let bits = Option::::arbitrary(g); - println!("\nBITS: {:?}\n", bits); + println!("\nBITS: {bits:?}\n"); let extra: Extranonce = extra.try_into().unwrap(); let bits = bits.map(HexU32Be); - println!("\nBITS: {:?}\n", bits); + println!("\nBITS: {bits:?}\n"); Submit { user_name: String::arbitrary(g), job_id: String::arbitrary(g), @@ -248,12 +248,12 @@ impl Arbitrary for Submit<'static> { #[quickcheck_macros::quickcheck] fn submit_from_to_json_rpc(submit: Submit<'static>) -> bool { let message = Into::::into(submit.clone()); - println!("\nMESSAGE: {:?}\n", message); + println!("\nMESSAGE: {message:?}\n"); let request = match message { Message::StandardRequest(s) => s, _ => panic!(), }; - println!("\nREQUEST: {:?}\n", request); + println!("\nREQUEST: {request:?}\n"); submit == TryInto::::try_into(request).unwrap() } diff --git a/protocols/v1/src/methods/server_to_client.rs b/protocols/v1/src/methods/server_to_client.rs index 009a41bea8..d4cf61d5f2 100644 --- a/protocols/v1/src/methods/server_to_client.rs +++ b/protocols/v1/src/methods/server_to_client.rs @@ -563,7 +563,7 @@ fn configure_response_parsing_all_fields() { }"#; let client_response = serde_json::from_str(client_response_str).unwrap(); let server_configure = Configure::try_from(&client_response).unwrap(); - println!("{:?}", server_configure); + println!("{server_configure:?}"); let version_rolling = server_configure.version_rolling.unwrap(); assert!(version_rolling.version_rolling); @@ -584,7 +584,7 @@ fn configure_response_parsing_no_vr_min_bit_count() { }"#; let client_response = serde_json::from_str(client_response_str).unwrap(); let server_configure = Configure::try_from(&client_response).unwrap(); - println!("{:?}", server_configure); + println!("{server_configure:?}"); let version_rolling = server_configure.version_rolling.unwrap(); assert!(version_rolling.version_rolling); diff --git a/protocols/v1/src/utils.rs b/protocols/v1/src/utils.rs index bcd4a206c6..ec4d960764 100644 --- a/protocols/v1/src/utils.rs +++ b/protocols/v1/src/utils.rs @@ -44,7 +44,7 @@ impl<'a> From> for Value { /// FIXME: find a nicer solution fn hex_decode(s: &str) -> Result, Error<'static>> { if s.len() % 2 != 0 { - Ok(hex::decode(format!("0{}", s))?) + Ok(hex::decode(format!("0{s}"))?) } else { Ok(hex::decode(s)?) } diff --git a/protocols/v2/binary-sv2/derive_codec/src/lib.rs b/protocols/v2/binary-sv2/derive_codec/src/lib.rs index 06be328cde..37da055224 100644 --- a/protocols/v2/binary-sv2/derive_codec/src/lib.rs +++ b/protocols/v2/binary-sv2/derive_codec/src/lib.rs @@ -342,10 +342,10 @@ fn get_struct_properties(item: TokenStream) -> ParsedStruct { break; } TokenTree::Punct(p) => { - struct_generics = format!("{}{}", struct_generics, p); + struct_generics = format!("{struct_generics}{p}"); } TokenTree::Ident(i) => { - struct_generics = format!("{}{}", struct_generics, i); + struct_generics = format!("{struct_generics}{i}"); } // Never executed at runtime it ok to panic _ => panic!("Struct {} has no fields", struct_name), diff --git a/protocols/v2/binary-sv2/src/lib.rs b/protocols/v2/binary-sv2/src/lib.rs index 61d490f059..95ec5152e7 100644 --- a/protocols/v2/binary-sv2/src/lib.rs +++ b/protocols/v2/binary-sv2/src/lib.rs @@ -620,8 +620,7 @@ mod test { c: 32, }; let len = expected.get_size(); - let mut buffer = Vec::new(); - buffer.resize(len, 0); + let mut buffer = vec![0; len]; to_writer(expected.clone(), &mut buffer).unwrap(); let deserialized: Test = from_bytes(&mut buffer[..]).unwrap(); assert_eq!(deserialized, expected); diff --git a/protocols/v2/codec-sv2/src/error.rs b/protocols/v2/codec-sv2/src/error.rs index 60137647e5..8d57ecfb34 100644 --- a/protocols/v2/codec-sv2/src/error.rs +++ b/protocols/v2/codec-sv2/src/error.rs @@ -62,10 +62,10 @@ impl fmt::Display for Error { use Error::*; match self { #[cfg(feature = "noise_sv2")] - AeadError(e) => write!(f, "Aead Error: `{:?}`", e), - BinarySv2Error(e) => write!(f, "Binary Sv2 Error: `{:?}`", e), - FramingError(e) => write!(f, "Framing error in codec: `{:?}`", e), - FramingSv2Error(e) => write!(f, "Framing Sv2 Error: `{:?}`", e), + AeadError(e) => write!(f, "Aead Error: `{e:?}`"), + BinarySv2Error(e) => write!(f, "Binary Sv2 Error: `{e:?}`"), + FramingError(e) => write!(f, "Framing error in codec: `{e:?}`"), + FramingSv2Error(e) => write!(f, "Framing Sv2 Error: `{e:?}`"), #[cfg(feature = "noise_sv2")] InvalidStepForInitiator => write!( f, @@ -76,9 +76,9 @@ impl fmt::Display for Error { f, "This noise handshake step can not be executed by a responder" ), - MissingBytes(u) => write!(f, "Missing `{}` Noise bytes", u), + MissingBytes(u) => write!(f, "Missing `{u}` Noise bytes"), #[cfg(feature = "noise_sv2")] - NoiseSv2Error(e) => write!(f, "Noise SV2 Error: `{:?}`", e), + NoiseSv2Error(e) => write!(f, "Noise SV2 Error: `{e:?}`"), #[cfg(feature = "noise_sv2")] NotInHandShakeState => write!( f, diff --git a/protocols/v2/framing-sv2/src/error.rs b/protocols/v2/framing-sv2/src/error.rs index bf6941da50..557af6bb06 100644 --- a/protocols/v2/framing-sv2/src/error.rs +++ b/protocols/v2/framing-sv2/src/error.rs @@ -23,7 +23,7 @@ impl fmt::Display for Error { use Error::*; match self { BinarySv2Error(ref e) => { - write!(f, "BinarySv2Error: `{:?}`", e) + write!(f, "BinarySv2Error: `{e:?}`") } ExpectedHandshakeFrame => { write!(f, "Expected `HandshakeFrame`, received `Sv2Frame`") @@ -34,8 +34,7 @@ impl fmt::Display for Error { UnexpectedHeaderLength(actual_size) => { write!( f, - "Unexpected `Header` length: `{}`, should be equal or more to {}", - actual_size, SV2_FRAME_HEADER_SIZE + "Unexpected `Header` length: `{actual_size}`, should be equal or more to {SV2_FRAME_HEADER_SIZE}" ) } } diff --git a/protocols/v2/roles-logic-sv2/src/errors.rs b/protocols/v2/roles-logic-sv2/src/errors.rs index 6b2d1825a2..194f38eca3 100644 --- a/protocols/v2/roles-logic-sv2/src/errors.rs +++ b/protocols/v2/roles-logic-sv2/src/errors.rs @@ -167,8 +167,7 @@ impl Display for Error { BadPayloadSize => write!(f, "Payload is too big to fit into the frame"), BinarySv2Error(v) => write!( f, - "BinarySv2Error: error in serializing/deserializing binary format {:?}", - v + "BinarySv2Error: error in serializing/deserializing binary format {v:?}" ), DownstreamDown => { write!( @@ -176,25 +175,25 @@ impl Display for Error { "Downstream is not connected anymore" ) } - ExpectedLen32(l) => write!(f, "Expected length of 32, but received length of {}", l), + ExpectedLen32(l) => write!(f, "Expected length of 32, but received length of {l}"), NoGroupsFound => write!( f, "A channel was attempted to be added to an Upstream, but no groups are specified" ), - UnexpectedMessage(type_) => write!(f, "Error: Unexpected message received. Recv m type: {:x}", type_), + UnexpectedMessage(type_) => write!(f, "Error: Unexpected message received. Recv m type: {type_:x}"), NoGroupIdOnExtendedChannel => write!(f, "Extended channels do not have group IDs"), NoPairableUpstream(a) => { - write!(f, "No pairable upstream node: {:?}", a) + write!(f, "No pairable upstream node: {a:?}") } NoCompatibleUpstream(a) => { - write!(f, "No compatible upstream node: {:?}", a) + write!(f, "No compatible upstream node: {a:?}") } NoFutureJobs => write!(f, "GroupChannelJobDispatcher does not have any future jobs"), NoDownstreamsConnected => write!(f, "NoDownstreamsConnected"), PrevHashRequireNonExistentJobId(id) => { - write!(f, "PrevHashRequireNonExistentJobId {}", id) + write!(f, "PrevHashRequireNonExistentJobId {id}") } - RequestIdNotMapped(id) => write!(f, "RequestIdNotMapped {}", id), + RequestIdNotMapped(id) => write!(f, "RequestIdNotMapped {id}"), NoUpstreamsConnected => write!(f, "There are no upstream connected"), UnexpectedPoolMessage => write!(f, "Unexpected `PoolMessage` type"), UnimplementedProtocol => write!( @@ -203,16 +202,14 @@ impl Display for Error { ), UnknownRequestId(id) => write!( f, - "Upstream is answering with a wrong request ID {} or + "Upstream is answering with a wrong request ID {id} or DownstreamMiningSelector::on_open_standard_channel_request has not been called - before relaying open channel request to upstream", - id + before relaying open channel request to upstream" ), InvalidExtranonceSize(required_min, requested) => { write!( f, - "Invalid extranonce size: required min {}, requested {}", - required_min, requested + "Invalid extranonce size: required min {required_min}, requested {requested}" ) }, NoMoreExtranonces => write!(f, "No more extranonces"), @@ -228,37 +225,37 @@ impl Display for Error { VersionTooBig => write!(f, "We are trying to construct a block header with version bigger than i32::MAX"), TxVersionTooBig => write!(f, "Tx version can not be greater than i32::MAX"), TxVersionTooLow => write!(f, "Tx version can not be lower than 1"), - TxDecodingError(e) => write!(f, "Impossible to decode tx: {:?}", e), + TxDecodingError(e) => write!(f, "Impossible to decode tx: {e:?}"), NotFoundChannelId => write!(f, "No downstream has been registered for this channel id"), NoValidJob => write!(f, "Impossible to create a standard job for channelA cause no valid job has been received from upstream yet"), NoValidTranslatorJob => write!(f, "Impossible to create a extended job for channel cause no valid job has been received from upstream yet"), NoTemplateForId => write!(f, "Impossible to retrieve a template for the required job id"), - NoValidTemplate(e) => write!(f, "Impossible to retrieve a template for the required template id: {}", e), - PoisonLock(e) => write!(f, "Poison lock: {}", e), - JobNotUpdated(ds_job_id, us_job_id) => write!(f, "Channel Factory did not update job: Downstream job id = {}, Upstream job id = {}", ds_job_id, us_job_id), - TargetError(e) => write!(f, "Impossible to get Target: {:?}", e), - HashrateError(e) => write!(f, "Impossible to get Hashrate: {:?}", e), - LogicErrorMessage(e) => write!(f, "Message is well formatted but can not be handled: {:?}", e), + NoValidTemplate(e) => write!(f, "Impossible to retrieve a template for the required template id: {e}"), + PoisonLock(e) => write!(f, "Poison lock: {e}"), + JobNotUpdated(ds_job_id, us_job_id) => write!(f, "Channel Factory did not update job: Downstream job id = {ds_job_id}, Upstream job id = {us_job_id}"), + TargetError(e) => write!(f, "Impossible to get Target: {e:?}"), + HashrateError(e) => write!(f, "Impossible to get Hashrate: {e:?}"), + LogicErrorMessage(e) => write!(f, "Message is well formatted but can not be handled: {e:?}"), JDSMissingTransactions => write!(f, "JD server cannot propagate the block: missing transactions"), - IoError(e) => write!(f, "IO error: {:?}", e), - ExtendedExtranonceCreationFailed(e) => write!(f, "Failed to create ExtendedExtranonce: {}", e), - FromSliceError(e) => write!(f, "Failed to hash from slice: {}", e), - InvalidUserIdentity(e) => write!(f, "Invalid user identity: {}", e), - ExtranoncePrefixFactoryError(e) => write!(f, "Failed to create ExtranoncePrefixFactory: {:?}", e), - Vardiff(e) => write!(f, "Failed to adjust diff in vardiff module: {:?}", e), - FailedToCreateStandardChannel(e) => write!(f, "Failed to create StandardChannel: {:?}", e), - FailedToCreateExtendedChannel(e) => write!(f, "Failed to create ExtendedChannel: {:?}", e), - FailedToProcessNewTemplateGroupChannel(e) => write!(f, "Failed to process NewTemplate: {:?}", e), - FailedToProcessSetNewPrevHashGroupChannel(e) => write!(f, "Failed to process SetNewPrevHash: {:?}", e), + IoError(e) => write!(f, "IO error: {e:?}"), + ExtendedExtranonceCreationFailed(e) => write!(f, "Failed to create ExtendedExtranonce: {e}"), + FromSliceError(e) => write!(f, "Failed to hash from slice: {e}"), + InvalidUserIdentity(e) => write!(f, "Invalid user identity: {e}"), + ExtranoncePrefixFactoryError(e) => write!(f, "Failed to create ExtranoncePrefixFactory: {e:?}"), + Vardiff(e) => write!(f, "Failed to adjust diff in vardiff module: {e:?}"), + FailedToCreateStandardChannel(e) => write!(f, "Failed to create StandardChannel: {e:?}"), + FailedToCreateExtendedChannel(e) => write!(f, "Failed to create ExtendedChannel: {e:?}"), + FailedToProcessNewTemplateGroupChannel(e) => write!(f, "Failed to process NewTemplate: {e:?}"), + FailedToProcessSetNewPrevHashGroupChannel(e) => write!(f, "Failed to process SetNewPrevHash: {e:?}"), NoActiveJob => write!(f, "No active job"), - FailedToUpdateStandardChannel(e) => write!(f, "Failed to update StandardChannel: {:?}", e), - FailedToUpdateExtendedChannel(e) => write!(f, "Failed to update ExtendedChannel: {:?}", e), + FailedToUpdateStandardChannel(e) => write!(f, "Failed to update StandardChannel: {e:?}"), + FailedToUpdateExtendedChannel(e) => write!(f, "Failed to update ExtendedChannel: {e:?}"), FailedToSendSolution => write!(f, "Failed to send solution"), - FailedToSetCustomMiningJob(e) => write!(f, "Failed to set custom mining job: {:?}", e), - FailedToProcessNewTemplateExtendedChannel(e) => write!(f, "Failed to process NewTemplate: {:?}", e), - FailedToProcessNewTemplateStandardChannel(e) => write!(f, "Failed to process NewTemplate: {:?}", e), - FailedToProcessSetNewPrevHashExtendedChannel(e) => write!(f, "Failed to process SetNewPrevHash: {:?}", e), - FailedToProcessSetNewPrevHashStandardChannel(e) => write!(f, "Failed to process SetNewPrevHash: {:?}", e), + FailedToSetCustomMiningJob(e) => write!(f, "Failed to set custom mining job: {e:?}"), + FailedToProcessNewTemplateExtendedChannel(e) => write!(f, "Failed to process NewTemplate: {e:?}"), + FailedToProcessNewTemplateStandardChannel(e) => write!(f, "Failed to process NewTemplate: {e:?}"), + FailedToProcessSetNewPrevHashExtendedChannel(e) => write!(f, "Failed to process SetNewPrevHash: {e:?}"), + FailedToProcessSetNewPrevHashStandardChannel(e) => write!(f, "Failed to process SetNewPrevHash: {e:?}"), } } } diff --git a/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs b/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs index d199580b15..597ed2e8bd 100644 --- a/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs +++ b/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs @@ -602,7 +602,7 @@ mod tests { pub fn _encode_hex(bytes: &[u8]) -> String { let mut s = String::with_capacity(bytes.len() * 2); for &b in bytes { - write!(&mut s, "{:02x}", b).unwrap(); + write!(&mut s, "{b:02x}").unwrap(); } s } diff --git a/protocols/v2/roles-logic-sv2/src/parsers.rs b/protocols/v2/roles-logic-sv2/src/parsers.rs index ee8c661279..1907688de4 100644 --- a/protocols/v2/roles-logic-sv2/src/parsers.rs +++ b/protocols/v2/roles-logic-sv2/src/parsers.rs @@ -1312,8 +1312,7 @@ mod test { 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, + "Header declared length [{message_length} bytes] differs from the actual payload length [{payload_length} bytes]", ); } } diff --git a/protocols/v2/roles-logic-sv2/src/utils.rs b/protocols/v2/roles-logic-sv2/src/utils.rs index c4d2dbbb8e..ecce16712c 100644 --- a/protocols/v2/roles-logic-sv2/src/utils.rs +++ b/protocols/v2/roles-logic-sv2/src/utils.rs @@ -210,7 +210,7 @@ pub fn merkle_root_from_path_>(coinbase_id: [u8; 32], path: &[T]) pub fn bytes_to_hex(bytes: &[u8]) -> String { let mut s = String::with_capacity(bytes.len() * 2); for &b in bytes { - write!(&mut s, "{:02x}", b) + write!(&mut s, "{b:02x}") .expect("Writing hex bytes to pre-allocated string should never fail"); } s diff --git a/protocols/v2/roles-logic-sv2/src/vardiff/test/mod.rs b/protocols/v2/roles-logic-sv2/src/vardiff/test/mod.rs index 8ea22f02bd..81df85ee1f 100644 --- a/protocols/v2/roles-logic-sv2/src/vardiff/test/mod.rs +++ b/protocols/v2/roles-logic-sv2/src/vardiff/test/mod.rs @@ -79,8 +79,7 @@ pub fn test_try_vardiff_stable_hashrate_minimal_change_or_no_change( if let Some(new_hashrate) = result { let diff_percentage = ((new_hashrate - initial_hashrate).abs() / initial_hashrate) * 100.0; println!( - "Stable hashrate test: new hashrate {}, initial {}, diff_pct {}", - new_hashrate, initial_hashrate, diff_percentage + "Stable hashrate test: new hashrate {new_hashrate}, initial {initial_hashrate}, diff_pct {diff_percentage}" ); assert!( diff_percentage < 20.0, @@ -119,7 +118,7 @@ pub fn test_try_vardiff_low_hashrate_decrease_target(vardiff: &mut V let target: Target = hash_rate_to_target(new_hashrate.into(), TEST_SHARES_PER_MINUTE.into()) .unwrap() .into(); - println!("target: {:?}", target); + println!("target: {target:?}"); assert!( target < initial_target, "Target should become harder (larger value)" diff --git a/protocols/v2/sv2-ffi/src/lib.rs b/protocols/v2/sv2-ffi/src/lib.rs index 271a587ba4..5e69242ae6 100644 --- a/protocols/v2/sv2-ffi/src/lib.rs +++ b/protocols/v2/sv2-ffi/src/lib.rs @@ -301,9 +301,9 @@ impl fmt::Display for Sv2Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Sv2Error::*; match self { - BinaryError(ref e) => write!(f, "{:?}", e), - CodecError(ref e) => write!(f, "{:?}", e), - PayloadTooBig(ref e) => write!(f, "Payload is too big: {:?}", e), + BinaryError(ref e) => write!(f, "{e:?}"), + CodecError(ref e) => write!(f, "{e:?}"), + PayloadTooBig(ref e) => write!(f, "Payload is too big: {e:?}"), InvalidSv2Frame => write!(f, "Invalid Sv2 frame"), MissingBytes => write!(f, "Missing expected bytes"), EncoderBusy => write!(f, "Encoder is busy"), @@ -381,7 +381,7 @@ fn encode_( let frame = StandardSv2Frame::>::from_message(message.clone(), m_type, 0, c_bit) .ok_or(Sv2Error::PayloadTooBig( - format!("{}", message).as_bytes().into(), + format!("{message}").as_bytes().into(), ))?; encoder .encoder diff --git a/roles/jd-client/src/lib/error.rs b/roles/jd-client/src/lib/error.rs index 01d68fa365..e10c6faa38 100644 --- a/roles/jd-client/src/lib/error.rs +++ b/roles/jd-client/src/lib/error.rs @@ -80,21 +80,21 @@ impl fmt::Display for Error<'_> { use Error::*; match self { BadCliArgs => write!(f, "Bad CLI arg input"), - BadConfigDeserialize(ref e) => write!(f, "Bad `config` TOML deserialize: `{:?}`", e), - BinarySv2(ref e) => write!(f, "Binary SV2 error: `{:?}`", e), - CodecNoise(ref e) => write!(f, "Noise error: `{:?}", e), - FramingSv2(ref e) => write!(f, "Framing SV2 error: `{:?}`", e), - Io(ref e) => write!(f, "I/O error: `{:?}", e), - ParseInt(ref e) => write!(f, "Bad convert from `String` to `int`: `{:?}`", e), - RolesSv2Logic(ref e) => write!(f, "Roles SV2 Logic Error: `{:?}`", e), - SubprotocolMining(ref e) => write!(f, "Subprotocol Mining Error: `{:?}`", e), - UpstreamIncoming(ref e) => write!(f, "Upstream parse incoming error: `{:?}`", e), + BadConfigDeserialize(ref e) => write!(f, "Bad `config` TOML deserialize: `{e:?}`"), + BinarySv2(ref e) => write!(f, "Binary SV2 error: `{e:?}`"), + CodecNoise(ref e) => write!(f, "Noise error: `{e:?}"), + FramingSv2(ref e) => write!(f, "Framing SV2 error: `{e:?}`"), + Io(ref e) => write!(f, "I/O error: `{e:?}"), + ParseInt(ref e) => write!(f, "Bad convert from `String` to `int`: `{e:?}`"), + RolesSv2Logic(ref e) => write!(f, "Roles SV2 Logic Error: `{e:?}`"), + SubprotocolMining(ref e) => write!(f, "Subprotocol Mining Error: `{e:?}`"), + UpstreamIncoming(ref e) => write!(f, "Upstream parse incoming error: `{e:?}`"), PoisonLock => write!(f, "Poison Lock error"), - ChannelErrorReceiver(ref e) => write!(f, "Channel receive error: `{:?}`", e), - TokioChannelErrorRecv(ref e) => write!(f, "Channel receive error: `{:?}`", e), - ChannelErrorSender(ref e) => write!(f, "Channel send error: `{:?}`", e), - VecToSlice32(ref e) => write!(f, "Standard Error: `{:?}`", e), - Infallible(ref e) => write!(f, "Infallible Error:`{:?}`", e), + ChannelErrorReceiver(ref e) => write!(f, "Channel receive error: `{e:?}`"), + TokioChannelErrorRecv(ref e) => write!(f, "Channel receive error: `{e:?}`"), + ChannelErrorSender(ref e) => write!(f, "Channel send error: `{e:?}`"), + VecToSlice32(ref e) => write!(f, "Standard Error: `{e:?}`"), + Infallible(ref e) => write!(f, "Infallible Error:`{e:?}`"), } } } diff --git a/roles/jd-client/src/lib/mod.rs b/roles/jd-client/src/lib/mod.rs index 81d84ea1d9..81a44e49fb 100644 --- a/roles/jd-client/src/lib/mod.rs +++ b/roles/jd-client/src/lib/mod.rs @@ -463,7 +463,7 @@ mod tests { jdc.shutdown(); let ip = config.listening_address().ip(); let port = config.listening_address().port(); - let jdc_addr = format!("{}:{}", ip, port); + let jdc_addr = format!("{ip}:{port}"); assert!(std::net::TcpListener::bind(jdc_addr).is_ok()); } } diff --git a/roles/jd-client/src/lib/template_receiver/mod.rs b/roles/jd-client/src/lib/template_receiver/mod.rs index 635779b690..e80468cc92 100644 --- a/roles/jd-client/src/lib/template_receiver/mod.rs +++ b/roles/jd-client/src/lib/template_receiver/mod.rs @@ -142,7 +142,7 @@ impl TemplateRx { let sender_to_tp = self_.safe_lock(|self_| self_.sender.clone()).unwrap(); match sender_to_tp.send(either_frame).await { Ok(_) => (), - Err(e) => panic!("{:?}", e), + Err(e) => panic!("{e:?}"), } } diff --git a/roles/jd-client/src/lib/upstream_sv2/upstream.rs b/roles/jd-client/src/lib/upstream_sv2/upstream.rs index 6391115197..956d1f4b42 100644 --- a/roles/jd-client/src/lib/upstream_sv2/upstream.rs +++ b/roles/jd-client/src/lib/upstream_sv2/upstream.rs @@ -691,7 +691,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { range_2, Some(self.jdc_signature.as_bytes().to_vec()), ) - .map_err(|err| RolesLogicError::ExtendedExtranonceCreationFailed(format!("{:?}", err)))?; + .map_err(|err| RolesLogicError::ExtendedExtranonceCreationFailed(format!("{err:?}")))?; // Job creator for the factory. let creator = roles_logic_sv2::job_creator::JobsCreators::new(total_len as u8); diff --git a/roles/jd-server/src/lib/error.rs b/roles/jd-server/src/lib/error.rs index 6065b443e7..73bcbece79 100644 --- a/roles/jd-server/src/lib/error.rs +++ b/roles/jd-server/src/lib/error.rs @@ -51,22 +51,22 @@ impl std::fmt::Display for JdsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use JdsError::*; match self { - Io(ref e) => write!(f, "I/O error: `{:?}", e), - ChannelSend(ref e) => write!(f, "Channel send failed: `{:?}`", e), - ChannelRecv(ref e) => write!(f, "Channel recv failed: `{:?}`", e), - BinarySv2(ref e) => write!(f, "Binary SV2 error: `{:?}`", e), - Codec(ref e) => write!(f, "Codec SV2 error: `{:?}", e), - Framing(ref e) => write!(f, "Framing SV2 error: `{:?}`", e), - Noise(ref e) => write!(f, "Noise SV2 error: `{:?}", e), - RolesLogic(ref e) => write!(f, "Roles Logic SV2 error: `{:?}`", e), - PoisonLock(ref e) => write!(f, "Poison lock: {:?}", e), - Custom(ref e) => write!(f, "Custom SV2 error: `{:?}`", e), + Io(ref e) => write!(f, "I/O error: `{e:?}"), + ChannelSend(ref e) => write!(f, "Channel send failed: `{e:?}`"), + ChannelRecv(ref e) => write!(f, "Channel recv failed: `{e:?}`"), + BinarySv2(ref e) => write!(f, "Binary SV2 error: `{e:?}`"), + Codec(ref e) => write!(f, "Codec SV2 error: `{e:?}"), + Framing(ref e) => write!(f, "Framing SV2 error: `{e:?}`"), + Noise(ref e) => write!(f, "Noise SV2 error: `{e:?}"), + RolesLogic(ref e) => write!(f, "Roles Logic SV2 error: `{e:?}`"), + PoisonLock(ref e) => write!(f, "Poison lock: {e:?}"), + Custom(ref e) => write!(f, "Custom SV2 error: `{e:?}`"), Sv2ProtocolError(ref e) => { - write!(f, "Received Sv2 Protocol Error from upstream: `{:?}`", e) + write!(f, "Received Sv2 Protocol Error from upstream: `{e:?}`") } - MempoolError(ref e) => write!(f, "Mempool error: `{:?}`", e), + MempoolError(ref e) => write!(f, "Mempool error: `{e:?}`"), ImpossibleToReconstructBlock(e) => { - write!(f, "Error in reconstructing the block: {:?}", e) + write!(f, "Error in reconstructing the block: {e:?}") } NoLastDeclaredJob => write!(f, "Last declared job not found"), InvalidRPCUrl => write!(f, "Invalid Template Provider RPC URL"), diff --git a/roles/mining-proxy/src/lib/error.rs b/roles/mining-proxy/src/lib/error.rs index 8029af2f21..e385fa4cdd 100644 --- a/roles/mining-proxy/src/lib/error.rs +++ b/roles/mining-proxy/src/lib/error.rs @@ -26,9 +26,9 @@ impl From> for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Error::SendError(e) => write!(f, "Send error: {}", e), - Error::UpstreamNotAvailabe(addr) => write!(f, "Upstream not available: {}", addr), - Error::SetupConnectionError(msg) => write!(f, "Setup connection error: {}", msg), + Error::SendError(e) => write!(f, "Send error: {e}"), + Error::UpstreamNotAvailabe(addr) => write!(f, "Upstream not available: {addr}"), + Error::SetupConnectionError(msg) => write!(f, "Setup connection error: {msg}"), Error::BadCliArgs => write!(f, "Bad CLI arguments provided"), } } diff --git a/roles/pool/src/lib/error.rs b/roles/pool/src/lib/error.rs index 73c7490189..cc4f165ab1 100644 --- a/roles/pool/src/lib/error.rs +++ b/roles/pool/src/lib/error.rs @@ -65,23 +65,23 @@ impl std::fmt::Display for PoolError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use PoolError::*; match self { - Io(ref e) => write!(f, "I/O error: `{:?}", e), - ChannelSend(ref e) => write!(f, "Channel send failed: `{:?}`", e), - ChannelRecv(ref e) => write!(f, "Channel recv failed: `{:?}`", e), - BinarySv2(ref e) => write!(f, "Binary SV2 error: `{:?}`", e), - Codec(ref e) => write!(f, "Codec SV2 error: `{:?}", e), - CoinbaseOutput(ref e) => write!(f, "Coinbase output error: `{:?}", e), - Framing(ref e) => write!(f, "Framing SV2 error: `{:?}`", e), - Noise(ref e) => write!(f, "Noise SV2 error: `{:?}", e), - RolesLogic(ref e) => write!(f, "Roles Logic SV2 error: `{:?}`", e), - PoisonLock(ref e) => write!(f, "Poison lock: {:?}", e), - ComponentShutdown(ref e) => write!(f, "Component shutdown: {:?}", e), - Custom(ref e) => write!(f, "Custom SV2 error: `{:?}`", e), + Io(ref e) => write!(f, "I/O error: `{e:?}"), + ChannelSend(ref e) => write!(f, "Channel send failed: `{e:?}`"), + ChannelRecv(ref e) => write!(f, "Channel recv failed: `{e:?}`"), + BinarySv2(ref e) => write!(f, "Binary SV2 error: `{e:?}`"), + Codec(ref e) => write!(f, "Codec SV2 error: `{e:?}"), + CoinbaseOutput(ref e) => write!(f, "Coinbase output error: `{e:?}"), + Framing(ref e) => write!(f, "Framing SV2 error: `{e:?}`"), + Noise(ref e) => write!(f, "Noise SV2 error: `{e:?}"), + RolesLogic(ref e) => write!(f, "Roles Logic SV2 error: `{e:?}`"), + PoisonLock(ref e) => write!(f, "Poison lock: {e:?}"), + ComponentShutdown(ref e) => write!(f, "Component shutdown: {e:?}"), + Custom(ref e) => write!(f, "Custom SV2 error: `{e:?}`"), Sv2ProtocolError(ref e) => { - write!(f, "Received Sv2 Protocol Error from upstream: `{:?}`", e) + write!(f, "Received Sv2 Protocol Error from upstream: `{e:?}`") } PoolError::Vardiff(ref e) => { - write!(f, "Received Vardiff Error : {:?}", e) + write!(f, "Received Vardiff Error : {e:?}") } } } diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 68438e4bad..03fa459efb 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -294,8 +294,7 @@ impl Downstream { if let Err(e) = status_tx .send(status::Status { state: status::State::Healthy(format!( - "Downstream connection dropped: {}", - e + "Downstream connection dropped: {e}" )), }) .await diff --git a/roles/roles-utils/network-helpers/src/sv1_connection.rs b/roles/roles-utils/network-helpers/src/sv1_connection.rs index d416a84041..8fed2a7594 100644 --- a/roles/roles-utils/network-helpers/src/sv1_connection.rs +++ b/roles/roles-utils/network-helpers/src/sv1_connection.rs @@ -74,7 +74,7 @@ impl ConnectionSV1 { res = receiver_outgoing.recv().fuse() => { let to_send = res.expect("Failed to receive message"); let to_send = match serde_json::to_string(&to_send) { - Ok(string) => format!("{}\n", string), + Ok(string) => format!("{string}\n"), Err(_e) => { break; } diff --git a/roles/roles-utils/rpc/src/mini_rpc_client.rs b/roles/roles-utils/rpc/src/mini_rpc_client.rs index 95bfb62651..61e064d7e1 100644 --- a/roles/roles-utils/rpc/src/mini_rpc_client.rs +++ b/roles/roles-utils/rpc/src/mini_rpc_client.rs @@ -129,7 +129,7 @@ impl MiniRpcClient { format!( "Basic {}", base64::engine::general_purpose::STANDARD - .encode(format!("{}:{}", username, password)) + .encode(format!("{username}:{password}")) ), ) .body(Full::::from(request_body)) diff --git a/roles/test-utils/mining-device/src/lib/mod.rs b/roles/test-utils/mining-device/src/lib/mod.rs index e78a97384f..0800093fd9 100644 --- a/roles/test-utils/mining-device/src/lib/mod.rs +++ b/roles/test-utils/mining-device/src/lib/mod.rs @@ -534,7 +534,7 @@ impl Miner { // target is sent in LE format, we'll keep it that way let hex_string = target .iter() - .fold("".to_string(), |acc, b| acc + format!("{:02x}", b).as_str()); + .fold("".to_string(), |acc, b| acc + format!("{b:02x}").as_str()); info!("Set target to {}", hex_string); // Store the target as U256 in little-endian format self.target = Some(U256::from_little_endian(target.as_slice())); diff --git a/roles/translator/src/lib/downstream_sv1/diff_management.rs b/roles/translator/src/lib/downstream_sv1/diff_management.rs index e6b6c9c178..e1e101a43c 100644 --- a/roles/translator/src/lib/downstream_sv1/diff_management.rs +++ b/roles/translator/src/lib/downstream_sv1/diff_management.rs @@ -291,7 +291,7 @@ mod test { let error = (calculated_share_per_min - expected_shares_per_minute as f32).abs(); assert!( error <= error_margin as f32, - "Calculated shares per minute are outside the 99.99...% confidence interval. Error: {:?}, Error margin: {:?}, {:?}", error, error_margin,calculated_share_per_min + "Calculated shares per minute are outside the 99.99...% confidence interval. Error: {error:?}, Error margin: {error_margin:?}, {calculated_share_per_min:?}" ); } @@ -403,7 +403,7 @@ mod test { .unwrap(); let mut share = generate_random_80_byte_array(); while elapsed <= total_run_time { - mock_mine(initial_target.clone().into(), &mut share); + mock_mine(initial_target.clone(), &mut share); Downstream::save_share(downstream.clone()).unwrap(); Downstream::try_update_difficulty_settings(downstream.clone()) .await diff --git a/roles/translator/src/lib/downstream_sv1/downstream.rs b/roles/translator/src/lib/downstream_sv1/downstream.rs index 7ccbcc9cd3..9563768bd9 100644 --- a/roles/translator/src/lib/downstream_sv1/downstream.rs +++ b/roles/translator/src/lib/downstream_sv1/downstream.rs @@ -286,7 +286,7 @@ impl Downstream { res = receiver_outgoing.recv().fuse() => { let to_send = handle_result!(tx_status_writer, res); let to_send = match serde_json::to_string(&to_send) { - Ok(string) => format!("{}\n", string), + Ok(string) => format!("{string}\n"), Err(_e) => { debug!("\nDownstream: Bad SV1 server message\n"); break; diff --git a/roles/translator/src/lib/error.rs b/roles/translator/src/lib/error.rs index 18f1cc3611..4485acc23f 100644 --- a/roles/translator/src/lib/error.rs +++ b/roles/translator/src/lib/error.rs @@ -107,32 +107,32 @@ impl fmt::Display for Error<'_> { use Error::*; match self { BadCliArgs => write!(f, "Bad CLI arg input"), - BadSerdeJson(ref e) => write!(f, "Bad serde json: `{:?}`", e), - BadConfigDeserialize(ref e) => write!(f, "Bad `config` TOML deserialize: `{:?}`", e), - BinarySv2(ref e) => write!(f, "Binary SV2 error: `{:?}`", e), - CodecNoise(ref e) => write!(f, "Noise error: `{:?}", e), - FramingSv2(ref e) => write!(f, "Framing SV2 error: `{:?}`", e), - InvalidExtranonce(ref e) => write!(f, "Invalid Extranonce error: `{:?}", e), - Io(ref e) => write!(f, "I/O error: `{:?}", e), - ParseInt(ref e) => write!(f, "Bad convert from `String` to `int`: `{:?}`", e), - RolesSv2Logic(ref e) => write!(f, "Roles SV2 Logic Error: `{:?}`", e), - V1Protocol(ref e) => write!(f, "V1 Protocol Error: `{:?}`", e), - SubprotocolMining(ref e) => write!(f, "Subprotocol Mining Error: `{:?}`", e), - UpstreamIncoming(ref e) => write!(f, "Upstream parse incoming error: `{:?}`", e), + BadSerdeJson(ref e) => write!(f, "Bad serde json: `{e:?}`"), + BadConfigDeserialize(ref e) => write!(f, "Bad `config` TOML deserialize: `{e:?}`"), + BinarySv2(ref e) => write!(f, "Binary SV2 error: `{e:?}`"), + CodecNoise(ref e) => write!(f, "Noise error: `{e:?}"), + FramingSv2(ref e) => write!(f, "Framing SV2 error: `{e:?}`"), + InvalidExtranonce(ref e) => write!(f, "Invalid Extranonce error: `{e:?}"), + Io(ref e) => write!(f, "I/O error: `{e:?}"), + ParseInt(ref e) => write!(f, "Bad convert from `String` to `int`: `{e:?}`"), + RolesSv2Logic(ref e) => write!(f, "Roles SV2 Logic Error: `{e:?}`"), + V1Protocol(ref e) => write!(f, "V1 Protocol Error: `{e:?}`"), + SubprotocolMining(ref e) => write!(f, "Subprotocol Mining Error: `{e:?}`"), + UpstreamIncoming(ref e) => write!(f, "Upstream parse incoming error: `{e:?}`"), PoisonLock => write!(f, "Poison Lock error"), - ChannelErrorReceiver(ref e) => write!(f, "Channel receive error: `{:?}`", e), - TokioChannelErrorRecv(ref e) => write!(f, "Channel receive error: `{:?}`", e), - ChannelErrorSender(ref e) => write!(f, "Channel send error: `{:?}`", e), + ChannelErrorReceiver(ref e) => write!(f, "Channel receive error: `{e:?}`"), + TokioChannelErrorRecv(ref e) => write!(f, "Channel receive error: `{e:?}`"), + ChannelErrorSender(ref e) => write!(f, "Channel send error: `{e:?}`"), SetDifficultyToMessage(ref e) => { - write!(f, "Error converting SetDifficulty to Message: `{:?}`", e) + write!(f, "Error converting SetDifficulty to Message: `{e:?}`") } - VecToSlice32(ref e) => write!(f, "Standard Error: `{:?}`", e), - Infallible(ref e) => write!(f, "Infallible Error:`{:?}`", e), + VecToSlice32(ref e) => write!(f, "Standard Error: `{e:?}`"), + Infallible(ref e) => write!(f, "Infallible Error:`{e:?}`"), Sv2ProtocolError(ref e) => { - write!(f, "Received Sv2 Protocol Error from upstream: `{:?}`", e) + write!(f, "Received Sv2 Protocol Error from upstream: `{e:?}`") } TargetError(ref e) => { - write!(f, "Impossible to get target from hashrate: `{:?}`", e) + write!(f, "Impossible to get target from hashrate: `{e:?}`") } Sv1MessageTooLong => { write!(f, "Received an sv1 message that is longer than max len") diff --git a/roles/translator/src/lib/mod.rs b/roles/translator/src/lib/mod.rs index dc5527fe7a..4f4f2bba88 100644 --- a/roles/translator/src/lib/mod.rs +++ b/roles/translator/src/lib/mod.rs @@ -381,7 +381,7 @@ mod tests { translator.shutdown(); let ip = config.downstream_address.clone(); let port = config.downstream_port; - let translator_addr = format!("{}:{}", ip, port); + let translator_addr = format!("{ip}:{port}"); assert!(std::net::TcpListener::bind(translator_addr).is_ok()); } } diff --git a/roles/translator/src/lib/upstream_sv2/upstream.rs b/roles/translator/src/lib/upstream_sv2/upstream.rs index d7d3c011aa..07faea55ce 100644 --- a/roles/translator/src/lib/upstream_sv2/upstream.rs +++ b/roles/translator/src/lib/upstream_sv2/upstream.rs @@ -414,8 +414,7 @@ impl Upstream { ..prefix_len + m.extranonce_size as usize; // extranonce2 let extended = handle_result!(tx_status, ExtendedExtranonce::from_upstream_extranonce( extranonce_prefix.clone(), range_0.clone(), range_1.clone(), range_2.clone(), - ).map_err(|err| InvalidExtranonce(format!("Impossible to create a valid extended extranonce from {:?} {:?} {:?} {:?}: {:?}", - extranonce_prefix, range_0, range_1, range_2, err)))); + ).map_err(|err| InvalidExtranonce(format!("Impossible to create a valid extended extranonce from {extranonce_prefix:?} {range_0:?} {range_1:?} {range_2:?}: {err:?}")))); handle_result!( tx_status, tx_sv2_extranonce.send((extended, m.channel_id)).await diff --git a/roles/translator/src/main.rs b/roles/translator/src/main.rs index 7fe418c09a..0e4ecb6a2b 100644 --- a/roles/translator/src/main.rs +++ b/roles/translator/src/main.rs @@ -17,7 +17,7 @@ async fn main() { let proxy_config = match process_cli_args() { Ok(p) => p, - Err(e) => panic!("failed to load config: {}", e), + Err(e) => panic!("failed to load config: {e}"), }; info!("Proxy Config: {:?}", &proxy_config); diff --git a/test/integration-tests/lib/template_provider.rs b/test/integration-tests/lib/template_provider.rs index 24115bfb42..57d58e2345 100644 --- a/test/integration-tests/lib/template_provider.rs +++ b/test/integration-tests/lib/template_provider.rs @@ -9,18 +9,15 @@ const VERSION_TP: &str = "0.1.15"; fn get_bitcoind_filename(os: &str, arch: &str) -> String { match (os, arch) { ("macos", "aarch64") => format!( - "bitcoin-sv2-tp-{}-arm64-apple-darwin-unsigned.tar.gz", - VERSION_TP + "bitcoin-sv2-tp-{VERSION_TP}-arm64-apple-darwin-unsigned.tar.gz" ), ("macos", "x86_64") => format!( - "bitcoin-sv2-tp-{}-x86_64-apple-darwin-unsigned.tar.gz", - VERSION_TP + "bitcoin-sv2-tp-{VERSION_TP}-x86_64-apple-darwin-unsigned.tar.gz" ), - ("linux", "x86_64") => format!("bitcoin-sv2-tp-{}-x86_64-linux-gnu.tar.gz", VERSION_TP), - ("linux", "aarch64") => format!("bitcoin-sv2-tp-{}-aarch64-linux-gnu.tar.gz", VERSION_TP), + ("linux", "x86_64") => format!("bitcoin-sv2-tp-{VERSION_TP}-x86_64-linux-gnu.tar.gz"), + ("linux", "aarch64") => format!("bitcoin-sv2-tp-{VERSION_TP}-aarch64-linux-gnu.tar.gz"), _ => format!( - "bitcoin-sv2-tp-{}-x86_64-apple-darwin-unsigned.zip", - VERSION_TP + "bitcoin-sv2-tp-{VERSION_TP}-x86_64-apple-darwin-unsigned.zip" ), } } @@ -40,10 +37,10 @@ impl TemplateProvider { let tp_dir = current_dir.join("template-provider"); let mut conf = Conf::default(); conf.wallet = Some(port.to_string()); - let staticdir = format!(".bitcoin-{}", port); + let staticdir = format!(".bitcoin-{port}"); conf.staticdir = Some(tp_dir.join(staticdir)); - let port_arg = format!("-sv2port={}", port); - let sv2_interval_arg = format!("-sv2interval={}", sv2_interval); + let port_arg = format!("-sv2port={port}"); + let sv2_interval_arg = format!("-sv2interval={sv2_interval}"); conf.args.extend(vec![ "-txindex=1", "-sv2", @@ -59,7 +56,7 @@ impl TemplateProvider { let arch = env::consts::ARCH; let download_filename = get_bitcoind_filename(os, arch); let bitcoin_exe_home = tp_dir - .join(format!("bitcoin-sv2-tp-{}", VERSION_TP)) + .join(format!("bitcoin-sv2-tp-{VERSION_TP}")) .join("bin"); if !bitcoin_exe_home.exists() { @@ -71,8 +68,7 @@ impl TemplateProvider { "https://github.com/Sjors/bitcoin/releases/download".to_owned() }); let url = format!( - "{}/sv2-tp-{}/{}", - download_endpoint, VERSION_TP, download_filename + "{download_endpoint}/sv2-tp-{VERSION_TP}/{download_filename}" ); http::make_get_request(&url, 5) } @@ -112,7 +108,7 @@ impl TemplateProvider { if current_time.elapsed() > timeout { panic!("Failed to start bitcoind: {}", e); } - println!("Failed to start bitcoind due to {}", e); + println!("Failed to start bitcoind due to {e}"); } } } diff --git a/test/integration-tests/lib/utils.rs b/test/integration-tests/lib/utils.rs index b2ee2d8e7e..a6bdbd73f6 100644 --- a/test/integration-tests/lib/utils.rs +++ b/test/integration-tests/lib/utils.rs @@ -349,8 +349,7 @@ pub mod http { return res.as_bytes().to_vec(); } else if (500..600).contains(&status_code) { eprintln!( - "Attempt {}: URL {} returned a server error code {}", - attempt, download_url, status_code + "Attempt {attempt}: URL {download_url} returned a server error code {status_code}" ); } else { panic!( @@ -371,7 +370,7 @@ pub mod http { if attempt < retries { let delay = 1u64 << (attempt - 1); - eprintln!("Retrying in {} seconds (exponential backoff)...", delay); + eprintln!("Retrying in {delay} seconds (exponential backoff)..."); std::thread::sleep(std::time::Duration::from_secs(delay)); } } diff --git a/utils/buffer/examples/variable_sized_messages.rs b/utils/buffer/examples/variable_sized_messages.rs index 922ec7fd39..df5875677e 100644 --- a/utils/buffer/examples/variable_sized_messages.rs +++ b/utils/buffer/examples/variable_sized_messages.rs @@ -23,7 +23,7 @@ fn main() { let data_slice = pool.get_data_owned(); slices.push_back(data_slice); println!("{:?}", &pool); - println!(""); + println!(); }; // Write a small message to the first slot diff --git a/utils/key-utils/src/main.rs b/utils/key-utils/src/main.rs index c15fa21836..35a12ccce9 100644 --- a/utils/key-utils/src/main.rs +++ b/utils/key-utils/src/main.rs @@ -23,8 +23,8 @@ fn main() { let (secret, public) = generate_key(); let secret: String = secret.into(); let public: String = public.into(); - println!("Secret Key: {}", secret); - println!("Public Key: {}", public); + println!("Secret Key: {secret}"); + println!("Public Key: {public}"); } #[cfg(not(feature = "std"))] From 4f4c81d2d01b0f5df884ffa55cfd5006815a7b88 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 26 Jun 2025 15:00:25 -0400 Subject: [PATCH 044/338] run cargo fmt across the codebase --- benches/benches/src/sv1/lib/client.rs | 2 -- .../lib/template_provider.rs | 21 ++++++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/benches/benches/src/sv1/lib/client.rs b/benches/benches/src/sv1/lib/client.rs index 806495b1e1..e59f4086f2 100644 --- a/benches/benches/src/sv1/lib/client.rs +++ b/benches/benches/src/sv1/lib/client.rs @@ -26,8 +26,6 @@ pub struct Client { impl Client { pub fn new(client_id: u32) -> Client { - - Client { client_id, extranonce1: extranonce_from_hex("00000000"), diff --git a/test/integration-tests/lib/template_provider.rs b/test/integration-tests/lib/template_provider.rs index 57d58e2345..78ffbce0a4 100644 --- a/test/integration-tests/lib/template_provider.rs +++ b/test/integration-tests/lib/template_provider.rs @@ -8,17 +8,15 @@ const VERSION_TP: &str = "0.1.15"; fn get_bitcoind_filename(os: &str, arch: &str) -> String { match (os, arch) { - ("macos", "aarch64") => format!( - "bitcoin-sv2-tp-{VERSION_TP}-arm64-apple-darwin-unsigned.tar.gz" - ), - ("macos", "x86_64") => format!( - "bitcoin-sv2-tp-{VERSION_TP}-x86_64-apple-darwin-unsigned.tar.gz" - ), + ("macos", "aarch64") => { + format!("bitcoin-sv2-tp-{VERSION_TP}-arm64-apple-darwin-unsigned.tar.gz") + } + ("macos", "x86_64") => { + format!("bitcoin-sv2-tp-{VERSION_TP}-x86_64-apple-darwin-unsigned.tar.gz") + } ("linux", "x86_64") => format!("bitcoin-sv2-tp-{VERSION_TP}-x86_64-linux-gnu.tar.gz"), ("linux", "aarch64") => format!("bitcoin-sv2-tp-{VERSION_TP}-aarch64-linux-gnu.tar.gz"), - _ => format!( - "bitcoin-sv2-tp-{VERSION_TP}-x86_64-apple-darwin-unsigned.zip" - ), + _ => format!("bitcoin-sv2-tp-{VERSION_TP}-x86_64-apple-darwin-unsigned.zip"), } } @@ -67,9 +65,8 @@ impl TemplateProvider { env::var("BITCOIND_DOWNLOAD_ENDPOINT").unwrap_or_else(|_| { "https://github.com/Sjors/bitcoin/releases/download".to_owned() }); - let url = format!( - "{download_endpoint}/sv2-tp-{VERSION_TP}/{download_filename}" - ); + let url = + format!("{download_endpoint}/sv2-tp-{VERSION_TP}/{download_filename}"); http::make_get_request(&url, 5) } }; From 3a23b0b476c9b1e2ee22005b939a546e83e27148 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 26 Jun 2025 14:00:07 -0400 Subject: [PATCH 045/338] change coverage workflows to run only when commits are pushed to main --- .github/workflows/coverage-protocols.yaml | 2 +- .github/workflows/coverage-roles.yaml | 2 +- .github/workflows/coverage-utils.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/coverage-protocols.yaml b/.github/workflows/coverage-protocols.yaml index a52bed95a6..6a870ff17a 100644 --- a/.github/workflows/coverage-protocols.yaml +++ b/.github/workflows/coverage-protocols.yaml @@ -1,7 +1,7 @@ name: Protocol test Coverage on: - pull_request: + push: branches: - main diff --git a/.github/workflows/coverage-roles.yaml b/.github/workflows/coverage-roles.yaml index 33e7497bb6..0b732df02e 100644 --- a/.github/workflows/coverage-roles.yaml +++ b/.github/workflows/coverage-roles.yaml @@ -1,7 +1,7 @@ name: Roles test Coverage on: - pull_request: + push: branches: - main diff --git a/.github/workflows/coverage-utils.yaml b/.github/workflows/coverage-utils.yaml index 1b6c0156d0..6ff5e4f3a5 100644 --- a/.github/workflows/coverage-utils.yaml +++ b/.github/workflows/coverage-utils.yaml @@ -1,7 +1,7 @@ name: Util Test Coverage on: - pull_request: + push: branches: - main From 3133ac29949211afb6725ac86a39a4ded8eb8e43 Mon Sep 17 00:00:00 2001 From: plebhash Date: Fri, 27 Jun 2025 16:21:34 -0300 Subject: [PATCH 046/338] fix bad deserialization of coinbase outputs --- .../src/channels/server/jobs/error.rs | 1 + .../src/channels/server/jobs/extended.rs | 33 ++++++--- .../src/channels/server/jobs/factory.rs | 53 ++++++++----- .../src/channels/server/jobs/standard.rs | 30 ++++++-- protocols/v2/roles-logic-sv2/src/utils.rs | 32 -------- roles/jd-client/src/lib/job_declarator/mod.rs | 74 ++++++++++++++++--- 6 files changed, 148 insertions(+), 75 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs index d975898f18..5fe956ab78 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs @@ -13,6 +13,7 @@ pub enum StandardJobError {} #[derive(Debug)] pub enum JobFactoryError { InvalidTemplate(String), + DeserializeCoinbaseOutputsError, CoinbaseTxPrefixError, CoinbaseTxSuffixError, CoinbaseOutputsSumOverflow, diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs index 0a053a05c9..f62d0de40c 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs @@ -4,10 +4,9 @@ use crate::{ server::jobs::{error::ExtendedJobError, JobOrigin}, }, template_distribution_sv2::NewTemplate, - utils::deserialize_outputs, }; use bitcoin::{ - consensus::{deserialize, serialize}, + consensus::{deserialize, serialize, Decodable}, transaction::{Transaction, TxOut}, }; use codec_sv2::binary_sv2::{Seq0255, Sv2Option, B0255, B064K, U256}; @@ -37,11 +36,30 @@ impl<'a> ExtendedJob<'a> { additional_coinbase_outputs: Vec, job_message: NewExtendedMiningJob<'a>, ) -> Self { + let mut template_coinbase_outputs = Vec::::consensus_decode( + &mut template + .coinbase_tx_outputs + .inner_as_ref() + .to_vec() + .as_slice(), + ) + .expect("Failed to deserialize template outputs"); + + // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 + if template_coinbase_outputs.is_empty() { + template_coinbase_outputs = vec![TxOut::consensus_decode( + &mut template + .coinbase_tx_outputs + .inner_as_ref() + .to_vec() + .as_slice(), + ) + .expect("Failed to deserialize template outputs")]; + } + let mut coinbase_outputs = vec![]; coinbase_outputs.extend(additional_coinbase_outputs); - coinbase_outputs.extend(deserialize_outputs( - template.coinbase_tx_outputs.inner_as_ref().to_vec(), - )); + coinbase_outputs.extend(template_coinbase_outputs); Self { origin: JobOrigin::NewTemplate(template), @@ -124,10 +142,7 @@ impl<'a> ExtendedJob<'a> { let coinbase_tx_locktime = deserialized_coinbase.lock_time.to_consensus_u32(); let coinbase_tx_input_n_sequence = deserialized_coinbase.input[0].sequence.0 as u32; - let mut serialized_outputs = Vec::new(); - for output in &deserialized_coinbase.output { - serialized_outputs.extend_from_slice(&serialize(output)); - } + let serialized_outputs = serialize(&deserialized_coinbase.output); let coinbase_tx_outputs: B064K<'a> = serialized_outputs .try_into() diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs index 4a45025d3a..9e35f76b59 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs @@ -5,7 +5,7 @@ use crate::{ server::jobs::{error::*, extended::ExtendedJob, standard::StandardJob}, }, template_distribution_sv2::NewTemplate, - utils::{deserialize_outputs, merkle_root_from_path, Id as JobIdFactory}, + utils::{merkle_root_from_path, Id as JobIdFactory}, }; use bitcoin::{ absolute::LockTime, @@ -189,7 +189,8 @@ impl JobFactory { .inner_as_ref() .to_vec(); - let coinbase_outputs = deserialize_outputs(serialized_outputs); + let coinbase_outputs = Vec::::consensus_decode(&mut serialized_outputs.as_slice()) + .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?; let job_id = self.job_id_factory.next(); @@ -228,8 +229,10 @@ impl JobFactory { // this is only used to extract coinbase_tx_prefix and coinbase_tx_suffix from the custom // coinbase fn custom_coinbase(&self, m: SetCustomMiningJob<'_>) -> Result { - let deserialized_outputs = - deserialize_outputs(m.coinbase_tx_outputs.inner_as_ref().to_vec()); + let deserialized_outputs = Vec::::consensus_decode( + &mut m.coinbase_tx_outputs.inner_as_ref().to_vec().as_slice(), + ) + .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?; let mut script_sig = vec![]; script_sig.extend_from_slice(m.coinbase_prefix.inner_as_ref()); @@ -322,19 +325,29 @@ impl JobFactory { outputs.push(output.clone()); } - let serialized_template_outputs = template.coinbase_tx_outputs.to_vec(); - let mut cursor = 0; - let mut txouts = &serialized_template_outputs[cursor..]; - while let Ok(out) = TxOut::consensus_decode(&mut txouts) { - let len = match out.script_pubkey.len() { - a @ 0..=252 => 8 + 1 + a, - a @ 253..=10000 => 8 + 3 + a, - _ => break, - }; - cursor += len; - outputs.push(out); + let mut template_outputs = Vec::::consensus_decode( + &mut template + .coinbase_tx_outputs + .inner_as_ref() + .to_vec() + .as_slice(), + ) + .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?; + + // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 + if template_outputs.is_empty() { + template_outputs = vec![TxOut::consensus_decode( + &mut template + .coinbase_tx_outputs + .inner_as_ref() + .to_vec() + .as_slice(), + ) + .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?]; } + outputs.append(&mut template_outputs); + let mut script_sig = vec![]; script_sig.extend_from_slice(&template.coinbase_prefix.to_vec()); script_sig.extend_from_slice(&[0; MAX_EXTRANONCE_LEN]); @@ -522,11 +535,11 @@ mod tests { coinbase_prefix: vec![82, 0].try_into().unwrap(), coinbase_tx_input_n_sequence: 4294967295, coinbase_tx_outputs: vec![ - 0, 242, 5, 42, 1, 0, 0, 0, 22, 0, 20, 235, 225, 183, 220, 194, 147, 204, 170, 14, - 231, 67, 168, 111, 137, 223, 130, 88, 194, 8, 252, 0, 0, 0, 0, 0, 0, 0, 0, 38, 106, - 36, 170, 33, 169, 237, 226, 246, 28, 63, 113, 209, 222, 253, 63, 169, 153, 223, - 163, 105, 83, 117, 92, 105, 6, 137, 121, 153, 98, 180, 139, 235, 216, 54, 151, 78, - 140, 249, + 2, 0, 242, 5, 42, 1, 0, 0, 0, 22, 0, 20, 235, 225, 183, 220, 194, 147, 204, 170, + 14, 231, 67, 168, 111, 137, 223, 130, 88, 194, 8, 252, 0, 0, 0, 0, 0, 0, 0, 0, 38, + 106, 36, 170, 33, 169, 237, 226, 246, 28, 63, 113, 209, 222, 253, 63, 169, 153, + 223, 163, 105, 83, 117, 92, 105, 6, 137, 121, 153, 98, 180, 139, 235, 216, 54, 151, + 78, 140, 249, ] .try_into() .unwrap(), diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs index 66c294e11f..00c4249df2 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs @@ -1,5 +1,4 @@ -use crate::utils::deserialize_outputs; -use bitcoin::transaction::TxOut; +use bitcoin::{consensus::Decodable, transaction::TxOut}; use codec_sv2::binary_sv2::{Sv2Option, U256}; use mining_sv2::NewMiningJob; use template_distribution_sv2::NewTemplate; @@ -26,9 +25,30 @@ impl<'a> StandardJob<'a> { ) -> Self { let mut coinbase_outputs = vec![]; coinbase_outputs.extend(additional_coinbase_outputs); - coinbase_outputs.extend(deserialize_outputs( - template.coinbase_tx_outputs.inner_as_ref().to_vec(), - )); + + let mut template_coinbase_outputs = Vec::::consensus_decode( + &mut template + .coinbase_tx_outputs + .inner_as_ref() + .to_vec() + .as_slice(), + ) + .expect("Failed to deserialize template outputs"); + + // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 + if template_coinbase_outputs.is_empty() { + template_coinbase_outputs = vec![TxOut::consensus_decode( + &mut template + .coinbase_tx_outputs + .inner_as_ref() + .to_vec() + .as_slice(), + ) + .expect("Failed to deserialize template outputs")]; + } + + coinbase_outputs.extend(template_coinbase_outputs); + Self { template, extranonce_prefix, diff --git a/protocols/v2/roles-logic-sv2/src/utils.rs b/protocols/v2/roles-logic-sv2/src/utils.rs index ecce16712c..2f191c67e9 100644 --- a/protocols/v2/roles-logic-sv2/src/utils.rs +++ b/protocols/v2/roles-logic-sv2/src/utils.rs @@ -8,10 +8,8 @@ use bitcoin::{ blockdata::block::{Header, Version}, consensus, - consensus::Decodable, hash_types::{BlockHash, TxMerkleNode}, hashes::{sha256d::Hash as DHash, Hash}, - transaction::TxOut, Block, CompactTarget, Transaction, }; use codec_sv2::binary_sv2::U256; @@ -230,36 +228,6 @@ fn reduce_path>(coinbase_id: [u8; 32], path: &[T]) -> [u8; 32] { root } -/// Deserializes a list of outputs from a serialized format. -pub fn deserialize_outputs(serialized_outputs: Vec) -> Vec { - let mut deserialized_outputs: Vec = vec![]; - - // The serialized outputs are in Bitcoin consensus format - // We need to parse them one by one, keeping track of cursor position - let mut cursor = 0; - let mut txouts = &serialized_outputs[cursor..]; - - // Iteratively decode each TxOut until we can't decode any more - while let Ok(out) = TxOut::consensus_decode(&mut txouts) { - // Calculate the size of this TxOut based on its script_pubkey length - // 8 bytes for value + variable bytes for script_pubkey length - // For small scripts (0-252 bytes): 1 byte length prefix - // For medium scripts (253-1000000 bytes): 3 byte length prefix (1 marker + 2 byte - // length) - let len = match out.script_pubkey.len() { - a @ 0..=252 => 8 + 1 + a, // 8 (value) + 1 (compact size) + script_len - a @ 253..=1000000 => 8 + 3 + a, // 8 (value) + 3 (compact size) + script_len - _ => break, // Unreasonably large script, likely an error - }; - - // Move the cursor forward by the size of this TxOut - cursor += len; - deserialized_outputs.push(out); - } - - deserialized_outputs -} - /// A list of potential errors during conversion between hashrate and target #[derive(Debug)] pub enum InputError { diff --git a/roles/jd-client/src/lib/job_declarator/mod.rs b/roles/jd-client/src/lib/job_declarator/mod.rs index 2f936ab835..781ebb1527 100644 --- a/roles/jd-client/src/lib/job_declarator/mod.rs +++ b/roles/jd-client/src/lib/job_declarator/mod.rs @@ -17,7 +17,12 @@ use std::{collections::HashMap, convert::TryInto}; use stratum_common::{ network_helpers_sv2::noise_connection::Connection, roles_logic_sv2::{ - bitcoin::{consensus, hashes::Hash, Transaction}, + bitcoin::{ + consensus, + consensus::{serialize, Decodable}, + hashes::Hash, + Transaction, TxOut, + }, codec_sv2::{ binary_sv2::{Seq0255, Seq064K, B016M, B064K, U256}, HandshakeRole, Initiator, StandardEitherFrame, StandardSv2Frame, @@ -418,9 +423,34 @@ impl JobDeclarator { // If it was a non-future job, it should have an associated // SetNewPrevHash. let set_new_prev_hash = last_declare.prev_hash; - let mut template_outs = template.coinbase_tx_outputs.to_vec(); - let mut pool_outs = last_declare.coinbase_pool_output; - pool_outs.append(&mut template_outs); + + let mut template_coinbase_outputs = Vec::::consensus_decode( + &mut template + .coinbase_tx_outputs + .inner_as_ref() + .to_vec() + .as_slice(), + ) + .expect("Failed to deserialize template outputs"); + + // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 + if template_coinbase_outputs.is_empty() { + template_coinbase_outputs = vec![TxOut::consensus_decode( + &mut template + .coinbase_tx_outputs + .inner_as_ref() + .to_vec() + .as_slice(), + ) + .expect("Failed to deserialize template outputs")]; + } + + let mut pool_coinbase_outputs = Vec::::consensus_decode( + &mut last_declare.coinbase_pool_output.as_slice(), + ) + .expect("Failed to deserialize pool outputs"); + pool_coinbase_outputs.append(&mut template_coinbase_outputs); + let serialized_pool_outs = serialize(&pool_coinbase_outputs); match set_new_prev_hash { // Send the SetCustomJobs message to the upstream pool. Some(p) => Upstream::set_custom_jobs( @@ -432,7 +462,7 @@ impl JobDeclarator { template.coinbase_tx_version, template.coinbase_prefix, template.coinbase_tx_input_sequence, - pool_outs, + serialized_pool_outs, template.coinbase_tx_locktime, template.template_id ).await.unwrap(), @@ -503,7 +533,7 @@ impl JobDeclarator { } }); // Loop to find and promote the corresponding future job. - let (job, up, merkle_path, template, mut pool_outs) = loop { + let (job, up, merkle_path, template, pool_outs) = loop { match self_mutex .safe_lock(|s| { // Check if the received SetNewPrevHash is outdated based on the counter @@ -540,8 +570,34 @@ impl JobDeclarator { // The token received from JDS for this job. let signed_token = job.mining_job_token.clone(); // Prepare the pool's coinbase output by appending the template's outputs. - let mut template_outs = template.coinbase_tx_outputs.to_vec(); - pool_outs.append(&mut template_outs); + let mut template_coinbase_outputs = Vec::::consensus_decode( + &mut template + .coinbase_tx_outputs + .inner_as_ref() + .to_vec() + .as_slice(), + ) + .expect("Failed to deserialize template outputs"); + + // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 + if template_coinbase_outputs.is_empty() { + template_coinbase_outputs = vec![TxOut::consensus_decode( + &mut template + .coinbase_tx_outputs + .inner_as_ref() + .to_vec() + .as_slice(), + ) + .expect("Failed to deserialize template outputs")]; + } + + let mut pool_coinbase_outputs = + Vec::::consensus_decode(&mut pool_outs.as_slice()) + .expect("Failed to deserialize pool outputs"); + pool_coinbase_outputs.append(&mut template_coinbase_outputs); + + let serialized_pool_outs = serialize(&pool_coinbase_outputs); + // Send the SetCustomJobs message to the upstream pool to activate this job. Upstream::set_custom_jobs( &up, @@ -552,7 +608,7 @@ impl JobDeclarator { template.coinbase_tx_version, template.coinbase_prefix, template.coinbase_tx_input_sequence, - pool_outs, + serialized_pool_outs, template.coinbase_tx_locktime, template.template_id, ) From a17565d3bcc516d457c0dc70977b39d33c418d9e Mon Sep 17 00:00:00 2001 From: plebhash Date: Fri, 27 Jun 2025 16:27:55 -0300 Subject: [PATCH 047/338] avoid panic on StandardJob::from_template and ExtendedJob::from_template --- .../src/channels/server/jobs/error.rs | 5 ++++- .../src/channels/server/jobs/extended.rs | 10 +++++----- .../src/channels/server/jobs/factory.rs | 6 ++++-- .../src/channels/server/jobs/standard.rs | 16 ++++++++-------- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs index 5fe956ab78..8ff43cf5b0 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs @@ -1,6 +1,7 @@ #[derive(Debug)] pub enum ExtendedJobError { FailedToDeserializeCoinbase, + FailedToDeserializeCoinbaseOutputs, CoinbaseInputCountMismatch, FailedToSerializeCoinbaseOutputs, FailedToSerializeCoinbasePrefix, @@ -8,7 +9,9 @@ pub enum ExtendedJobError { InvalidMinNTime, } -pub enum StandardJobError {} +pub enum StandardJobError { + FailedToDeserializeCoinbaseOutputs, +} #[derive(Debug)] pub enum JobFactoryError { diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs index f62d0de40c..f0ed4f964a 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs @@ -35,7 +35,7 @@ impl<'a> ExtendedJob<'a> { extranonce_prefix: Vec, additional_coinbase_outputs: Vec, job_message: NewExtendedMiningJob<'a>, - ) -> Self { + ) -> Result { let mut template_coinbase_outputs = Vec::::consensus_decode( &mut template .coinbase_tx_outputs @@ -43,7 +43,7 @@ impl<'a> ExtendedJob<'a> { .to_vec() .as_slice(), ) - .expect("Failed to deserialize template outputs"); + .map_err(|_| ExtendedJobError::FailedToDeserializeCoinbaseOutputs)?; // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 if template_coinbase_outputs.is_empty() { @@ -54,19 +54,19 @@ impl<'a> ExtendedJob<'a> { .to_vec() .as_slice(), ) - .expect("Failed to deserialize template outputs")]; + .map_err(|_| ExtendedJobError::FailedToDeserializeCoinbaseOutputs)?]; } let mut coinbase_outputs = vec![]; coinbase_outputs.extend(additional_coinbase_outputs); coinbase_outputs.extend(template_coinbase_outputs); - Self { + Ok(Self { origin: JobOrigin::NewTemplate(template), extranonce_prefix, coinbase_outputs, job_message, - } + }) } pub fn from_custom_job( diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs index 9e35f76b59..062dc699b3 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs @@ -106,7 +106,8 @@ impl JobFactory { extranonce_prefix, additional_coinbase_outputs, job_message, - ); + ) + .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?; Ok(job) } @@ -171,7 +172,8 @@ impl JobFactory { extranonce_prefix, additional_coinbase_outputs, job_message, - ); + ) + .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?; Ok(job) } diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs index 00c4249df2..e55bd2373c 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs @@ -1,3 +1,4 @@ +use crate::channels::server::jobs::error::StandardJobError; use bitcoin::{consensus::Decodable, transaction::TxOut}; use codec_sv2::binary_sv2::{Sv2Option, U256}; use mining_sv2::NewMiningJob; @@ -22,10 +23,7 @@ impl<'a> StandardJob<'a> { extranonce_prefix: Vec, additional_coinbase_outputs: Vec, job_message: NewMiningJob<'a>, - ) -> Self { - let mut coinbase_outputs = vec![]; - coinbase_outputs.extend(additional_coinbase_outputs); - + ) -> Result { let mut template_coinbase_outputs = Vec::::consensus_decode( &mut template .coinbase_tx_outputs @@ -33,7 +31,7 @@ impl<'a> StandardJob<'a> { .to_vec() .as_slice(), ) - .expect("Failed to deserialize template outputs"); + .map_err(|_| StandardJobError::FailedToDeserializeCoinbaseOutputs)?; // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 if template_coinbase_outputs.is_empty() { @@ -44,17 +42,19 @@ impl<'a> StandardJob<'a> { .to_vec() .as_slice(), ) - .expect("Failed to deserialize template outputs")]; + .map_err(|_| StandardJobError::FailedToDeserializeCoinbaseOutputs)?]; } + let mut coinbase_outputs = vec![]; + coinbase_outputs.extend(additional_coinbase_outputs); coinbase_outputs.extend(template_coinbase_outputs); - Self { + Ok(Self { template, extranonce_prefix, coinbase_outputs, job_message, - } + }) } pub fn get_job_id(&self) -> u32 { From f5bd352f75134ca1f86a23e34b5c2f017f4b33d2 Mon Sep 17 00:00:00 2001 From: plebhash Date: Fri, 27 Jun 2025 16:54:57 -0300 Subject: [PATCH 048/338] add integration test jdc_receives_submit_shares_success to avoid issues like #1771 again --- .../jdc_receives_submit_shares_success.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/integration-tests/tests/jdc_receives_submit_shares_success.rs diff --git a/test/integration-tests/tests/jdc_receives_submit_shares_success.rs b/test/integration-tests/tests/jdc_receives_submit_shares_success.rs new file mode 100644 index 0000000000..172939bf19 --- /dev/null +++ b/test/integration-tests/tests/jdc_receives_submit_shares_success.rs @@ -0,0 +1,22 @@ +use integration_tests_sv2::{interceptor::MessageDirection, *}; +use stratum_common::roles_logic_sv2::mining_sv2::*; + +#[tokio::test] +async fn jdc_submit_shares_success() { + start_tracing(); + let (tp, tp_addr) = start_template_provider(None); + let (_pool, pool_addr) = start_pool(Some(tp_addr)).await; + let (sniffer, sniffer_addr) = start_sniffer("0", pool_addr, false, vec![]); + let (_jds, jds_addr) = start_jds(tp.rpc_info()); + let (_jdc, jdc_addr) = start_jdc(&[(sniffer_addr, jds_addr)], tp_addr); + let (_translator, tproxy_addr) = start_sv2_translator(jdc_addr); + start_mining_device_sv1(tproxy_addr, false, None); + + // make sure sure JDC gets a share acknowledgement + sniffer + .wait_for_message_type( + MessageDirection::ToDownstream, + MESSAGE_TYPE_SUBMIT_SHARES_SUCCESS, + ) + .await; +} From fe526527f807f7f855dd7d4120b83766ccdc40ff Mon Sep 17 00:00:00 2001 From: Cole Roberts Date: Wed, 2 Jul 2025 10:35:36 -0400 Subject: [PATCH 049/338] decouple job state from channels with JobStore --- .../src/channels/server/extended.rs | 137 ++++++++---------- .../src/channels/server/group.rs | 71 ++++----- .../src/channels/server/jobs/extended.rs | 11 ++ .../src/channels/server/jobs/job_store.rs | 113 +++++++++++++++ .../src/channels/server/jobs/mod.rs | 6 + .../src/channels/server/jobs/standard.rs | 12 +- .../src/channels/server/standard.rs | 128 +++++++--------- .../src/lib/mining_pool/message_handler.rs | 11 +- roles/pool/src/lib/mining_pool/mod.rs | 9 +- 9 files changed, 293 insertions(+), 205 deletions(-) create mode 100644 protocols/v2/roles-logic-sv2/src/channels/server/jobs/job_store.rs diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs index 38689f6c87..4883425aaa 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs @@ -5,7 +5,12 @@ use crate::{ chain_tip::ChainTip, server::{ error::ExtendedChannelError, - jobs::{extended::ExtendedJob, factory::JobFactory, JobOrigin}, + jobs::{ + extended::ExtendedJob, + factory::JobFactory, + job_store::{DefaultJobStore, JobStore}, + JobOrigin, + }, share_accounting::{ShareAccounting, ShareValidationError, ShareValidationResult}, }, }, @@ -22,7 +27,7 @@ use bitcoin::{ }; use codec_sv2::binary_sv2; use mining_sv2::{SetCustomMiningJob, SubmitSharesExtended, Target, MAX_EXTRANONCE_LEN}; -use std::{collections::HashMap, convert::TryInto}; +use std::{collections::HashMap, convert::TryInto, fmt::Display}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash as SetNewPrevHashTdp}; use tracing::debug; @@ -47,7 +52,7 @@ use tracing::debug; /// - the channel's share validation state /// - the channel's job factory /// - the channel's chain tip -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct ExtendedChannel<'a> { channel_id: u32, user_identity: String, @@ -56,15 +61,7 @@ pub struct ExtendedChannel<'a> { requested_max_target: Target, target: Target, // todo: try to use Target from rust-bitcoin nominal_hashrate: f32, - // maps template_id to job_id on future jobs - future_template_to_job_id: HashMap, - // future jobs are indexed with job_id (u32) - future_jobs: HashMap>, - active_job: Option>, - // past jobs are indexed with job_id (u32) - past_jobs: HashMap>, - // stale jobs are indexed with job_id (u32) - stale_jobs: HashMap>, + job_store: Box>>, job_factory: JobFactory, share_accounting: ShareAccounting, expected_share_per_minute: f32, @@ -83,6 +80,7 @@ impl<'a> ExtendedChannel<'a> { requested_min_rollable_extranonce_size: u16, share_batch_size: usize, expected_share_per_minute: f32, + job_store: Box>>, ) -> Result { let target_u256 = match hash_rate_to_target(nominal_hashrate.into(), expected_share_per_minute.into()) { @@ -112,11 +110,7 @@ impl<'a> ExtendedChannel<'a> { requested_max_target: max_target, target, nominal_hashrate, - future_template_to_job_id: HashMap::new(), - future_jobs: HashMap::new(), - active_job: None, - past_jobs: HashMap::new(), - stale_jobs: HashMap::new(), + job_store, job_factory: JobFactory::new(version_rolling_allowed), share_accounting: ShareAccounting::new(share_batch_size), expected_share_per_minute, @@ -192,7 +186,7 @@ impl<'a> ExtendedChannel<'a> { } pub fn get_future_template_to_job_id(&self) -> &HashMap { - &self.future_template_to_job_id + &self.job_store.get_future_template_to_job_id() } pub fn get_nominal_hashrate(&self) -> f32 { @@ -262,15 +256,15 @@ impl<'a> ExtendedChannel<'a> { } pub fn get_active_job(&self) -> Option<&ExtendedJob<'a>> { - self.active_job.as_ref() + self.job_store.get_active_job() } pub fn get_future_jobs(&self) -> &HashMap> { - &self.future_jobs + self.job_store.get_future_jobs() } pub fn get_past_jobs(&self) -> &HashMap> { - &self.past_jobs + self.job_store.get_past_jobs() } pub fn get_share_accounting(&self) -> &ShareAccounting { @@ -302,9 +296,7 @@ impl<'a> ExtendedChannel<'a> { ) .map_err(ExtendedChannelError::JobFactoryError)?; let new_job_id = new_job.get_job_id(); - self.future_jobs.insert(new_job_id, new_job); - self.future_template_to_job_id - .insert(template.template_id, new_job_id); + self.job_store.add_future_job(template.template_id, new_job); } false => { match self.chain_tip.clone() { @@ -321,15 +313,7 @@ impl<'a> ExtendedChannel<'a> { coinbase_reward_outputs, ) .map_err(ExtendedChannelError::JobFactoryError)?; - // if there's already some active job, move it to the past jobs - // and set the new job as the active job - if let Some(active_job) = self.active_job.take() { - self.past_jobs.insert(active_job.get_job_id(), active_job); - self.active_job = Some(new_job); - } else { - // if there's no active job, simply set the new job as the active job - self.active_job = Some(new_job); - } + self.job_store.add_active_job(new_job); } } } @@ -352,45 +336,19 @@ impl<'a> ExtendedChannel<'a> { &mut self, set_new_prev_hash: SetNewPrevHashTdp<'a>, ) -> Result<(), ExtendedChannelError> { - match self.future_jobs.is_empty() { + match self.job_store.get_future_jobs().is_empty() { true => { return Err(ExtendedChannelError::TemplateIdNotFound); } false => { // the SetNewPrevHash message was addressed to a specific future template - let future_job_id = self - .future_template_to_job_id - .remove(&set_new_prev_hash.template_id) - .ok_or(ExtendedChannelError::TemplateIdNotFound)?; - - // move currently active job to past jobs (so it can be marked as stale) - let currently_active_job = self.active_job.take(); - if let Some(active_job) = currently_active_job { - self.past_jobs.insert(active_job.get_job_id(), active_job); - } - - // activate the future job - let mut activated_job = self - .future_jobs - .remove(&future_job_id) - .expect("future job must exist"); - - activated_job.activate(set_new_prev_hash.header_timestamp); - - self.active_job = Some(activated_job); - - self.future_jobs.clear(); - self.future_template_to_job_id.clear(); + self.job_store.activate_future_job( + set_new_prev_hash.template_id, + set_new_prev_hash.header_timestamp, + ); } } - // mark all past jobs as stale, so that shares can be rejected with the appropriate error - // code - self.stale_jobs = self.past_jobs.clone(); - - // clear past jobs, as we're no longer going to validate shares for them - self.past_jobs.clear(); - // clear seen shares, as shares for past chain tip will be rejected as stale self.share_accounting.flush_seen_shares(); @@ -425,11 +383,7 @@ impl<'a> ExtendedChannel<'a> { let job_id = new_job.get_job_id(); - if let Some(active_job) = self.active_job.take() { - self.past_jobs.insert(active_job.get_job_id(), active_job); - } - - self.active_job = Some(new_job); + self.job_store.add_active_job(new_job); Ok(job_id) } @@ -445,15 +399,15 @@ impl<'a> ExtendedChannel<'a> { // check if job_id is active job let is_active_job = self - .active_job - .as_ref() + .job_store + .get_active_job() .is_some_and(|job| job.get_job_id() == job_id); // check if job_id is past job - let is_past_job = self.past_jobs.contains_key(&job_id); + let is_past_job = self.job_store.get_past_jobs().contains_key(&job_id); // check if job_id is stale job - let is_stale_job = self.stale_jobs.contains_key(&job_id); + let is_stale_job = self.job_store.get_stale_jobs().contains_key(&job_id); if is_stale_job { return Err(ShareValidationError::Stale); @@ -465,11 +419,19 @@ impl<'a> ExtendedChannel<'a> { } let job = if is_active_job { - self.active_job.as_ref().expect("active job must exist") + self.job_store + .get_active_job() + .expect("active job must exist") } else if is_past_job { - self.past_jobs.get(&job_id).expect("past job must exist") + self.job_store + .get_past_jobs() + .get(&job_id) + .expect("past job must exist") } else { - self.stale_jobs.get(&job_id).expect("stale job must exist") + self.job_store + .get_stale_jobs() + .get(&job_id) + .expect("stale job must exist") }; let extranonce_prefix = job.get_extranonce_prefix(); @@ -610,10 +572,11 @@ impl<'a> ExtendedChannel<'a> { mod tests { use crate::channels::{ chain_tip::ChainTip, + client::extended::ExtendedJob, server::{ error::ExtendedChannelError, extended::ExtendedChannel, - jobs::JobOrigin, + jobs::{job_store::DefaultJobStore, JobOrigin}, share_accounting::{ShareValidationError, ShareValidationResult}, }, }; @@ -643,6 +606,7 @@ mod tests { let version_rolling_allowed = true; let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; let share_batch_size = 100; + let job_store = Box::new(DefaultJobStore::new()); let mut channel = ExtendedChannel::new( channel_id, @@ -654,6 +618,7 @@ mod tests { rollable_extranonce_size, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -791,6 +756,7 @@ mod tests { let version_rolling_allowed = true; let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; let share_batch_size = 100; + let job_store = Box::new(DefaultJobStore::new()); let mut channel = ExtendedChannel::new( channel_id, @@ -802,6 +768,7 @@ mod tests { rollable_extranonce_size, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -905,6 +872,7 @@ mod tests { let version_rolling_allowed = true; let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; let share_batch_size = 100; + let job_store = Box::new(DefaultJobStore::new()); // this extended channel lives on JDC let mut jdc_extended_channel = ExtendedChannel::new( @@ -917,6 +885,7 @@ mod tests { rollable_extranonce_size, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -1009,6 +978,7 @@ mod tests { let version_rolling_allowed = true; let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; let share_batch_size = 100; + let job_store = Box::new(DefaultJobStore::new()); // this extended channel lives on Pool Mining Server let mut pool_extended_channel = ExtendedChannel::new( @@ -1021,6 +991,7 @@ mod tests { rollable_extranonce_size, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -1066,6 +1037,7 @@ mod tests { let version_rolling_allowed = true; let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; let share_batch_size = 100; + let job_store = Box::new(DefaultJobStore::new()); let mut channel = ExtendedChannel::new( channel_id, @@ -1077,6 +1049,7 @@ mod tests { rollable_extranonce_size, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -1141,6 +1114,7 @@ mod tests { let version_rolling_allowed = true; let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; let share_batch_size = 100; + let job_store = Box::new(DefaultJobStore::new()); let mut channel = ExtendedChannel::new( channel_id, @@ -1152,6 +1126,7 @@ mod tests { rollable_extranonce_size, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -1247,6 +1222,7 @@ mod tests { let version_rolling_allowed = true; let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; let share_batch_size = 100; + let job_store = Box::new(DefaultJobStore::new()); let mut channel = ExtendedChannel::new( channel_id, @@ -1258,6 +1234,7 @@ mod tests { rollable_extranonce_size, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -1356,6 +1333,7 @@ mod tests { let version_rolling_allowed = true; let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; let share_batch_size = 100; + let job_store = Box::new(DefaultJobStore::new()); let mut channel = ExtendedChannel::new( channel_id, @@ -1367,6 +1345,7 @@ mod tests { rollable_extranonce_size, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -1475,6 +1454,7 @@ mod tests { let version_rolling_allowed = true; let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; let share_batch_size = 100; + let job_store = Box::new(DefaultJobStore::new()); // this is the most permissive possible max_target let max_target: Target = [0xff; 32].into(); @@ -1490,6 +1470,7 @@ mod tests { rollable_extranonce_size, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -1569,6 +1550,7 @@ mod tests { let version_rolling_allowed = true; let rollable_extranonce_size = (MAX_EXTRANONCE_LEN - extranonce_prefix.len()) as u16; let share_batch_size = 100; + let job_store = Box::new(DefaultJobStore::new()); let mut channel = ExtendedChannel::new( channel_id, @@ -1580,6 +1562,7 @@ mod tests { rollable_extranonce_size, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/group.rs b/protocols/v2/roles-logic-sv2/src/channels/server/group.rs index fbb960831f..ec63a51c10 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/group.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/group.rs @@ -3,7 +3,11 @@ use crate::channels::{ chain_tip::ChainTip, server::{ error::GroupChannelError, - jobs::{extended::ExtendedJob, factory::JobFactory}, + jobs::{ + extended::ExtendedJob, + factory::JobFactory, + job_store::{self, JobStore}, + }, }, }; use bitcoin::transaction::TxOut; @@ -26,28 +30,22 @@ use std::collections::{HashMap, HashSet}; /// - the group channel's past jobs /// - the group channel's stale jobs /// - the group channel's share validation state -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct GroupChannel<'a> { group_channel_id: u32, standard_channel_ids: HashSet, job_factory: JobFactory, - // maps template_id to job_id on future jobs - future_template_to_job_id: HashMap, - // future jobs are indexed with job_id (u32) - future_jobs: HashMap>, - active_job: Option>, + job_store: Box>>, chain_tip: Option, } impl<'a> GroupChannel<'a> { - pub fn new(group_channel_id: u32) -> Self { + pub fn new(group_channel_id: u32, job_store: Box>>) -> Self { Self { group_channel_id, standard_channel_ids: HashSet::new(), job_factory: JobFactory::new(true), - future_template_to_job_id: HashMap::new(), - future_jobs: HashMap::new(), - active_job: None, + job_store, chain_tip: None, } } @@ -79,15 +77,15 @@ impl<'a> GroupChannel<'a> { } pub fn get_active_job(&self) -> Option<&ExtendedJob<'a>> { - self.active_job.as_ref() + self.job_store.get_active_job() } pub fn get_future_template_to_job_id(&self) -> &HashMap { - &self.future_template_to_job_id + self.job_store.get_future_template_to_job_id() } pub fn get_future_jobs(&self) -> &HashMap> { - &self.future_jobs + self.job_store.get_future_jobs() } /// Updates the group channel state with a new template. @@ -114,9 +112,7 @@ impl<'a> GroupChannel<'a> { ) .map_err(GroupChannelError::JobFactoryError)?; let new_job_id = new_job.get_job_id(); - self.future_jobs.insert(new_job_id, new_job); - self.future_template_to_job_id - .insert(template.template_id, new_job_id); + self.job_store.add_future_job(template.template_id, new_job); } false => { match self.chain_tip.clone() { @@ -135,7 +131,7 @@ impl<'a> GroupChannel<'a> { coinbase_reward_outputs, ) .map_err(GroupChannelError::JobFactoryError)?; - self.active_job = Some(new_job); + self.job_store.set_active_job(new_job); } } } @@ -154,29 +150,15 @@ impl<'a> GroupChannel<'a> { &mut self, set_new_prev_hash: SetNewPrevHashTdp<'a>, ) -> Result<(), GroupChannelError> { - match self.future_jobs.is_empty() { + match self.job_store.get_future_jobs().is_empty() { true => { return Err(GroupChannelError::TemplateIdNotFound); } false => { - // the SetNewPrevHash message was addressed to a specific future template - let future_job_id = self - .future_template_to_job_id - .remove(&set_new_prev_hash.template_id) - .ok_or(GroupChannelError::TemplateIdNotFound)?; - - // activate the future job - let mut activated_job = self - .future_jobs - .remove(&future_job_id) - .expect("future job must exist"); - - activated_job.activate(set_new_prev_hash.header_timestamp); - - self.active_job = Some(activated_job); - - self.future_jobs.clear(); - self.future_template_to_job_id.clear(); + self.job_store.activate_future_job( + set_new_prev_hash.template_id, + set_new_prev_hash.header_timestamp, + ); } } @@ -195,7 +177,10 @@ impl<'a> GroupChannel<'a> { #[cfg(test)] mod tests { - use crate::channels::{chain_tip::ChainTip, server::group::GroupChannel}; + use crate::channels::{ + chain_tip::ChainTip, + server::{group::GroupChannel, jobs::job_store::DefaultJobStore}, + }; use bitcoin::{transaction::TxOut, Amount, ScriptBuf}; use codec_sv2::binary_sv2::Sv2Option; use mining_sv2::NewExtendedMiningJob; @@ -210,8 +195,8 @@ mod tests { // the messages on this test were collected from a sane message flow // we use them as test vectors to assert correct behavior of job creation let group_channel_id = 1; - - let mut group_channel = GroupChannel::new(group_channel_id); + let job_store = Box::new(DefaultJobStore::new()); + let mut group_channel = GroupChannel::new(group_channel_id, job_store); let template = NewTemplate { template_id: 1, @@ -338,7 +323,8 @@ mod tests { // we use them as test vectors to assert correct behavior of job creation let group_channel_id = 1; - let mut group_channel = GroupChannel::new(group_channel_id); + let job_store = Box::new(DefaultJobStore::new()); + let mut group_channel = GroupChannel::new(group_channel_id, job_store); let ntime = 1746839905; let prev_hash = [ @@ -428,7 +414,8 @@ mod tests { // we use them as test vectors to assert correct behavior of job creation let group_channel_id = 1; - let mut group_channel = GroupChannel::new(group_channel_id); + let job_store = Box::new(DefaultJobStore::new()); + let mut group_channel = GroupChannel::new(group_channel_id, job_store); let template = NewTemplate { template_id: 1, diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs index f0ed4f964a..99460fc760 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs @@ -1,3 +1,4 @@ +use super::Job; use crate::{ channels::{ chain_tip::ChainTip, @@ -26,6 +27,16 @@ pub struct ExtendedJob<'a> { job_message: NewExtendedMiningJob<'a>, } +impl<'a> Job for ExtendedJob<'a> { + fn get_job_id(&self) -> u32 { + self.job_message.job_id + } + + fn activate(&mut self, min_ntime: u32) { + self.activate(min_ntime); + } +} + impl<'a> ExtendedJob<'a> { /// Creates a new job from a template. /// diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/job_store.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/job_store.rs new file mode 100644 index 0000000000..f07a90db5a --- /dev/null +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/job_store.rs @@ -0,0 +1,113 @@ +use std::{collections::HashMap, convert::TryInto, fmt::Debug}; + +use super::Job; + +pub trait JobStore: Send + Sync + Debug { + fn add_future_job(&mut self, template_id: u64, job: T) -> u32; + fn add_active_job(&mut self, job: T); + fn activate_future_job(&mut self, template_id: u64, prev_hash_header_timestamp: u32) -> bool; + fn set_active_job(&mut self, job: T); + fn get_future_template_to_job_id(&self) -> &HashMap; + fn get_active_job(&self) -> Option<&T>; + fn get_future_jobs(&self) -> &HashMap; + fn get_past_jobs(&self) -> &HashMap; + fn get_stale_jobs(&self) -> &HashMap; +} + +#[derive(Debug)] +pub struct DefaultJobStore { + future_template_to_job_id: HashMap, + // future jobs are indexed with job_id (u32) + future_jobs: HashMap, + active_job: Option, + // past jobs are indexed with job_id (u32) + past_jobs: HashMap, + // stale jobs are indexed with job_id (u32) + stale_jobs: HashMap, +} + +impl DefaultJobStore { + pub fn new() -> Self { + Self { + future_template_to_job_id: HashMap::new(), + future_jobs: HashMap::new(), + active_job: None, + past_jobs: HashMap::new(), + stale_jobs: HashMap::new(), + } + } +} + +impl JobStore for DefaultJobStore { + fn add_future_job(&mut self, template_id: u64, new_job: T) -> u32 { + let new_job_id = new_job.get_job_id(); + self.future_jobs.insert(new_job_id, new_job); + self.future_template_to_job_id + .insert(template_id, new_job_id); + new_job_id + } + + fn add_active_job(&mut self, job: T) { + // move currently active job to past jobs (so it can be marked as stale) + if let Some(active_job) = self.active_job.take() { + self.past_jobs.insert(active_job.get_job_id(), active_job); + } + // set the new active job + self.active_job = Some(job); + } + + fn set_active_job(&mut self, job: T) { + self.active_job = Some(job); + } + + fn activate_future_job(&mut self, template_id: u64, prev_hash_header_timestamp: u32) -> bool { + let mut future_job = + if let Some(job_id) = self.future_template_to_job_id.remove(&template_id) { + if let Some(job) = self.future_jobs.remove(&job_id) { + job + } else { + return false; + } + } else { + return false; + }; + + // move currently active job to past jobs (so it can be marked as stale) + if let Some(active_job) = self.active_job.take() { + self.past_jobs.insert(active_job.get_job_id(), active_job); + } + + // activate the future job + future_job.activate(prev_hash_header_timestamp); + self.active_job = Some(future_job); + self.future_jobs.clear(); + self.future_template_to_job_id.clear(); + // mark all past jobs as stale, so that shares can be rejected with the appropriate error + // code + self.stale_jobs = self.past_jobs.clone(); + + // clear past jobs, as we're no longer going to validate shares for them + self.past_jobs.clear(); + true + } + + fn get_future_template_to_job_id(&self) -> &HashMap { + &self.future_template_to_job_id + } + + fn get_active_job(&self) -> Option<&T> { + self.active_job.as_ref() + } + + fn get_future_jobs(&self) -> &HashMap { + &self.future_jobs + } + + fn get_past_jobs(&self) -> &HashMap { + &self.past_jobs + } + + fn get_stale_jobs(&self) -> &HashMap { + &self.stale_jobs + } +} diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/mod.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/mod.rs index dd00f9a6df..bcb062ea5d 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/mod.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/mod.rs @@ -1,6 +1,7 @@ pub mod error; pub mod extended; pub mod factory; +pub mod job_store; pub mod standard; use mining_sv2::SetCustomMiningJob; @@ -11,3 +12,8 @@ pub enum JobOrigin<'a> { NewTemplate(NewTemplate<'a>), SetCustomMiningJob(SetCustomMiningJob<'a>), } + +pub trait Job: Send + Sync { + fn get_job_id(&self) -> u32; + fn activate(&mut self, prev_hash_header_timestamp: u32); +} diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs index e55bd2373c..745dca00b4 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs @@ -1,4 +1,4 @@ -use crate::channels::server::jobs::error::StandardJobError; +use crate::channels::server::jobs::{error::StandardJobError, Job}; use bitcoin::{consensus::Decodable, transaction::TxOut}; use codec_sv2::binary_sv2::{Sv2Option, U256}; use mining_sv2::NewMiningJob; @@ -17,6 +17,16 @@ pub struct StandardJob<'a> { job_message: NewMiningJob<'a>, } +impl<'a> Job for StandardJob<'a> { + fn get_job_id(&self) -> u32 { + self.job_message.job_id + } + + fn activate(&mut self, min_ntime: u32) { + self.activate(min_ntime); + } +} + impl<'a> StandardJob<'a> { pub fn from_template( template: NewTemplate<'a>, diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs index d488375243..0c5c2a082d 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs @@ -4,7 +4,7 @@ use crate::{ chain_tip::ChainTip, server::{ error::StandardChannelError, - jobs::{factory::JobFactory, standard::StandardJob}, + jobs::{factory::JobFactory, job_store::JobStore, standard::StandardJob}, share_accounting::{ShareAccounting, ShareValidationError, ShareValidationResult}, }, }, @@ -45,7 +45,7 @@ use tracing::debug; /// indexed by `job_id`) /// - the channel's job factory /// - the channel's chain tip -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct StandardChannel<'a> { pub channel_id: u32, user_identity: String, @@ -53,17 +53,9 @@ pub struct StandardChannel<'a> { requested_max_target: Target, target: Target, nominal_hashrate: f32, - // maps template_id to job_id on future jobs - future_template_to_job_id: HashMap, - // future jobs are indexed with job_id (u32) - future_jobs: HashMap>, - active_job: Option>, - // past jobs are indexed with job_id (u32) - past_jobs: HashMap>, - // stale jobs are indexed with job_id (u32) - stale_jobs: HashMap>, share_accounting: ShareAccounting, expected_share_per_minute: f32, + job_store: Box>>, job_factory: JobFactory, chain_tip: Option, } @@ -77,6 +69,7 @@ impl<'a> StandardChannel<'a> { nominal_hashrate: f32, share_batch_size: usize, expected_share_per_minute: f32, + job_store: Box>>, ) -> Result { let calculated_target = match hash_rate_to_target(nominal_hashrate.into(), expected_share_per_minute.into()) { @@ -99,15 +92,11 @@ impl<'a> StandardChannel<'a> { requested_max_target, target, nominal_hashrate, - future_template_to_job_id: HashMap::new(), - future_jobs: HashMap::new(), - active_job: None, - past_jobs: HashMap::new(), - stale_jobs: HashMap::new(), share_accounting: ShareAccounting::new(share_batch_size), expected_share_per_minute, job_factory: JobFactory::new(true), chain_tip: None, + job_store, }) } @@ -214,23 +203,23 @@ impl<'a> StandardChannel<'a> { } pub fn get_active_job(&self) -> Option<&StandardJob<'a>> { - self.active_job.as_ref() + self.job_store.get_active_job() } pub fn get_future_template_to_job_id(&self) -> &HashMap { - &self.future_template_to_job_id + self.job_store.get_future_template_to_job_id() } pub fn get_future_jobs(&self) -> &HashMap> { - &self.future_jobs + self.job_store.get_future_jobs() } pub fn get_past_jobs(&self) -> &HashMap> { - &self.past_jobs + self.job_store.get_past_jobs() } pub fn get_stale_jobs(&self) -> &HashMap> { - &self.stale_jobs + &self.job_store.get_stale_jobs() } pub fn get_shares_per_minute(&self) -> f32 { @@ -276,9 +265,7 @@ impl<'a> StandardChannel<'a> { ) .map_err(StandardChannelError::JobFactoryError)?; let new_job_id = new_job.get_job_id(); - self.future_jobs.insert(new_job_id, new_job); - self.future_template_to_job_id - .insert(template.template_id, new_job_id); + self.job_store.add_future_job(template.template_id, new_job); } false => { match self.chain_tip.clone() { @@ -295,15 +282,7 @@ impl<'a> StandardChannel<'a> { coinbase_reward_outputs, ) .map_err(StandardChannelError::JobFactoryError)?; - - // if there's already some active job, move it to the past jobs - // and set the new job as the active job - if let Some(active_job) = self.active_job.take() { - self.past_jobs.insert(active_job.get_job_id(), active_job); - self.active_job = Some(new_job); - } else { - self.active_job = Some(new_job); - } + self.job_store.add_active_job(new_job); } } } @@ -324,49 +303,18 @@ impl<'a> StandardChannel<'a> { &mut self, set_new_prev_hash: SetNewPrevHash<'a>, ) -> Result<(), StandardChannelError> { - match self.future_jobs.is_empty() { + match self.job_store.get_future_jobs().is_empty() { true => { return Err(StandardChannelError::TemplateIdNotFound); } false => { - // the SetNewPrevHash message was addressed to a specific future template - if !self - .future_template_to_job_id - .contains_key(&set_new_prev_hash.template_id) - { - return Err(StandardChannelError::TemplateIdNotFound); - } - - // move currently active job to past jobs (so it can be marked as stale) - let currently_active_job = self.active_job.take(); - if let Some(active_job) = currently_active_job { - self.past_jobs.insert(active_job.get_job_id(), active_job); - } - - let future_job_id = self - .future_template_to_job_id - .remove(&set_new_prev_hash.template_id) - .expect("future job must exist"); - - // activate the future job - let mut activated_job = self - .future_jobs - .remove(&future_job_id) - .expect("future job must exist"); - - activated_job.activate(set_new_prev_hash.header_timestamp); - - self.active_job = Some(activated_job); + self.job_store.activate_future_job( + set_new_prev_hash.template_id, + set_new_prev_hash.header_timestamp, + ); } } - // mark all past jobs as stale, so that shares can be rejected with the appropriate error - // code - self.stale_jobs = self.past_jobs.clone(); - - // clear past jobs, as we're no longer going to validate shares for them - self.past_jobs.clear(); - // update the chain tip let set_new_prev_hash_static = set_new_prev_hash.into_static(); let new_chain_tip = ChainTip::new( @@ -390,15 +338,15 @@ impl<'a> StandardChannel<'a> { // check if job_id is active job let is_active_job = self - .active_job - .as_ref() + .job_store + .get_active_job() .is_some_and(|job| job.get_job_id() == job_id); // check if job_id is past job - let is_past_job = self.past_jobs.contains_key(&job_id); + let is_past_job = self.job_store.get_past_jobs().contains_key(&job_id); // check if job_id is stale job - let is_stale_job = self.stale_jobs.contains_key(&job_id); + let is_stale_job = self.job_store.get_stale_jobs().contains_key(&job_id); if is_stale_job { return Err(ShareValidationError::Stale); @@ -410,11 +358,19 @@ impl<'a> StandardChannel<'a> { } let job = if is_active_job { - self.active_job.as_ref().expect("active job must exist") + self.job_store + .get_active_job() + .expect("active job must exist") } else if is_past_job { - self.past_jobs.get(&job_id).expect("past job must exist") + self.job_store + .get_past_jobs() + .get(&job_id) + .expect("past job must exist") } else { - self.stale_jobs.get(&job_id).expect("stale job must exist") + self.job_store + .get_stale_jobs() + .get(&job_id) + .expect("stale job must exist") }; let merkle_root: [u8; 32] = job @@ -540,6 +496,7 @@ mod tests { chain_tip::ChainTip, server::{ error::StandardChannelError, + jobs::{job_store::DefaultJobStore, standard::StandardJob}, share_accounting::{ShareValidationError, ShareValidationResult}, standard::StandardChannel, }, @@ -570,6 +527,7 @@ mod tests { let nominal_hashrate = 10.0; let share_batch_size = 100; let expected_share_per_minute = 1.0; + let job_store = Box::new(DefaultJobStore::::new()); let mut standard_channel = StandardChannel::new( standard_channel_id, @@ -579,6 +537,7 @@ mod tests { nominal_hashrate, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -694,6 +653,8 @@ mod tests { let share_batch_size = 100; let expected_share_per_minute = 1.0; + let job_store = Box::new(DefaultJobStore::::new()); + let mut standard_channel = StandardChannel::new( standard_channel_id, user_identity, @@ -702,6 +663,7 @@ mod tests { nominal_hashrate, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -793,6 +755,8 @@ mod tests { let share_batch_size = 100; let expected_share_per_minute = 1.0; + let job_store = Box::new(DefaultJobStore::::new()); + let mut standard_channel = StandardChannel::new( standard_channel_id, user_identity, @@ -801,6 +765,7 @@ mod tests { nominal_hashrate, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -894,6 +859,8 @@ mod tests { let share_batch_size = 100; let expected_share_per_minute = 1.0; + let job_store = Box::new(DefaultJobStore::::new()); + let mut standard_channel = StandardChannel::new( standard_channel_id, user_identity, @@ -902,6 +869,7 @@ mod tests { nominal_hashrate, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -998,6 +966,8 @@ mod tests { let share_batch_size = 100; let expected_share_per_minute = 1.0; + let job_store = Box::new(DefaultJobStore::::new()); + let mut standard_channel = StandardChannel::new( standard_channel_id, user_identity, @@ -1006,6 +976,7 @@ mod tests { nominal_hashrate, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -1093,7 +1064,7 @@ mod tests { let expected_share_per_minute = 1.0; let initial_hashrate = 10.0; let share_batch_size = 100; - + let job_store = Box::new(DefaultJobStore::::new()); // this is the most permissive possible max_target let max_target: Target = [0xff; 32].into(); @@ -1106,6 +1077,7 @@ mod tests { initial_hashrate, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); @@ -1183,6 +1155,7 @@ mod tests { let expected_share_per_minute = 1.0; let nominal_hashrate = 1_000.0; let share_batch_size = 100; + let job_store = Box::new(DefaultJobStore::::new()); let mut channel = StandardChannel::new( channel_id, @@ -1192,6 +1165,7 @@ mod tests { nominal_hashrate, share_batch_size, expected_share_per_minute, + job_store, ) .unwrap(); diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index a29eb4ddaf..30876f6256 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -15,6 +15,7 @@ use stratum_common::roles_logic_sv2::{ channels::server::{ error::{ExtendedChannelError, StandardChannelError}, extended::ExtendedChannel, + jobs::job_store::DefaultJobStore, share_accounting::{ShareValidationError, ShareValidationResult}, standard::StandardChannel, }, @@ -83,7 +84,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { .to_vec(); let channel_id = self.channel_id_factory.next(); - + let job_store = Box::new(DefaultJobStore::new()); let mut standard_channel = match StandardChannel::new( channel_id, user_identity, @@ -92,6 +93,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { nominal_hash_rate, self.share_batch_size, self.shares_per_minute, + job_store, ) { Ok(channel) => channel, Err(e) => match e { @@ -204,7 +206,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { let vardiff = VardiffState::new()?; self.standard_channels - .insert(channel_id, Arc::new(RwLock::new(standard_channel.clone()))); + .insert(channel_id, Arc::new(RwLock::new(standard_channel))); self.vardiff .insert(channel_id, Arc::new(RwLock::new(Box::new(vardiff)))); @@ -269,7 +271,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { }; let channel_id = self.channel_id_factory.next(); - + let job_store = Box::new(DefaultJobStore::new()); let mut extended_channel = match ExtendedChannel::new( channel_id, user_identity, @@ -280,6 +282,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { requested_min_rollable_extranonce_size, self.share_batch_size, self.shares_per_minute, + job_store, ) { Ok(channel) => channel, Err(e) => match e { @@ -398,7 +401,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { let vardiff = VardiffState::new()?; self.extended_channels - .insert(channel_id, Arc::new(RwLock::new(extended_channel.clone()))); + .insert(channel_id, Arc::new(RwLock::new(extended_channel))); self.vardiff .insert(channel_id, Arc::new(RwLock::new(Box::new(vardiff)))); diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 03fa459efb..1bacacca00 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -44,7 +44,8 @@ use stratum_common::{ self, bitcoin::{Amount, ScriptBuf, TxOut}, channels::server::{ - extended::ExtendedChannel, group::GroupChannel, standard::StandardChannel, + extended::ExtendedChannel, group::GroupChannel, jobs::job_store::DefaultJobStore, + standard::StandardChannel, }, codec_sv2::{ self, binary_sv2::U256, HandshakeRole, Responder, StandardEitherFrame, StandardSv2Frame, @@ -241,8 +242,8 @@ impl Downstream { // we know this will result in group_channel_id == 1 // so we use that for every standard channel let group_channel_id = channel_id_factory.next(); - - let mut group_channel = GroupChannel::new(group_channel_id); + let job_store = Box::new(DefaultJobStore::new()); + let mut group_channel = GroupChannel::new(group_channel_id, job_store); group_channel .on_new_template(last_future_template.clone(), pool_coinbase_outputs) @@ -504,7 +505,7 @@ impl Pool { match responder { Ok(resp) => { - if let Ok((receiver, sender)) = Connection::new(stream, HandshakeRole::Responder(resp)).await { + if let Ok((receiver, sender)) = Connection::new::(stream, HandshakeRole::Responder(resp)).await { handle_result!( status_tx, Self::accept_incoming_connection_( From b4c44136bc6195a7b766ad18727d5f573ff1db5c Mon Sep 17 00:00:00 2001 From: coleFD Date: Wed, 2 Jul 2025 13:48:58 -0400 Subject: [PATCH 050/338] fix clippy --- protocols/v2/binary-sv2/codec/src/lib.rs | 2 +- .../roles-logic-sv2/src/channels/server/extended.rs | 12 +++--------- .../v2/roles-logic-sv2/src/channels/server/group.rs | 7 +------ .../src/channels/server/jobs/extended.rs | 2 +- .../src/channels/server/jobs/job_store.rs | 8 +++++++- .../src/channels/server/jobs/standard.rs | 2 +- .../roles-logic-sv2/src/channels/server/standard.rs | 4 ++-- roles/translator/src/lib/error.rs | 2 +- 8 files changed, 17 insertions(+), 22 deletions(-) diff --git a/protocols/v2/binary-sv2/codec/src/lib.rs b/protocols/v2/binary-sv2/codec/src/lib.rs index 36ed68fe15..4911c6b576 100644 --- a/protocols/v2/binary-sv2/codec/src/lib.rs +++ b/protocols/v2/binary-sv2/codec/src/lib.rs @@ -480,7 +480,7 @@ impl From> for EncodableField<'_> { } #[cfg(feature = "with_buffer_pool")] -impl<'a> From for EncodableField<'a> { +impl From for EncodableField<'_> { fn from(_v: buffer_sv2::Slice) -> Self { unreachable!() } diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs index 4883425aaa..e63678ef3c 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs @@ -5,12 +5,7 @@ use crate::{ chain_tip::ChainTip, server::{ error::ExtendedChannelError, - jobs::{ - extended::ExtendedJob, - factory::JobFactory, - job_store::{DefaultJobStore, JobStore}, - JobOrigin, - }, + jobs::{extended::ExtendedJob, factory::JobFactory, job_store::JobStore, JobOrigin}, share_accounting::{ShareAccounting, ShareValidationError, ShareValidationResult}, }, }, @@ -27,7 +22,7 @@ use bitcoin::{ }; use codec_sv2::binary_sv2; use mining_sv2::{SetCustomMiningJob, SubmitSharesExtended, Target, MAX_EXTRANONCE_LEN}; -use std::{collections::HashMap, convert::TryInto, fmt::Display}; +use std::{collections::HashMap, convert::TryInto}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash as SetNewPrevHashTdp}; use tracing::debug; @@ -186,7 +181,7 @@ impl<'a> ExtendedChannel<'a> { } pub fn get_future_template_to_job_id(&self) -> &HashMap { - &self.job_store.get_future_template_to_job_id() + self.job_store.get_future_template_to_job_id() } pub fn get_nominal_hashrate(&self) -> f32 { @@ -295,7 +290,6 @@ impl<'a> ExtendedChannel<'a> { coinbase_reward_outputs, ) .map_err(ExtendedChannelError::JobFactoryError)?; - let new_job_id = new_job.get_job_id(); self.job_store.add_future_job(template.template_id, new_job); } false => { diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/group.rs b/protocols/v2/roles-logic-sv2/src/channels/server/group.rs index ec63a51c10..b120c8bae1 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/group.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/group.rs @@ -3,11 +3,7 @@ use crate::channels::{ chain_tip::ChainTip, server::{ error::GroupChannelError, - jobs::{ - extended::ExtendedJob, - factory::JobFactory, - job_store::{self, JobStore}, - }, + jobs::{extended::ExtendedJob, factory::JobFactory, job_store::JobStore}, }, }; use bitcoin::transaction::TxOut; @@ -111,7 +107,6 @@ impl<'a> GroupChannel<'a> { coinbase_reward_outputs, ) .map_err(GroupChannelError::JobFactoryError)?; - let new_job_id = new_job.get_job_id(); self.job_store.add_future_job(template.template_id, new_job); } false => { diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs index 99460fc760..2b548d5020 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs @@ -27,7 +27,7 @@ pub struct ExtendedJob<'a> { job_message: NewExtendedMiningJob<'a>, } -impl<'a> Job for ExtendedJob<'a> { +impl Job for ExtendedJob<'_> { fn get_job_id(&self) -> u32 { self.job_message.job_id } diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/job_store.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/job_store.rs index f07a90db5a..aae747a1c3 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/job_store.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/job_store.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, convert::TryInto, fmt::Debug}; +use std::{collections::HashMap, fmt::Debug}; use super::Job; @@ -38,6 +38,12 @@ impl DefaultJobStore { } } +impl Default for DefaultJobStore { + fn default() -> Self { + Self::new() + } +} + impl JobStore for DefaultJobStore { fn add_future_job(&mut self, template_id: u64, new_job: T) -> u32 { let new_job_id = new_job.get_job_id(); diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs index 745dca00b4..0982c445c1 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs @@ -17,7 +17,7 @@ pub struct StandardJob<'a> { job_message: NewMiningJob<'a>, } -impl<'a> Job for StandardJob<'a> { +impl Job for StandardJob<'_> { fn get_job_id(&self) -> u32 { self.job_message.job_id } diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs index 0c5c2a082d..dfc7fa0b05 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs @@ -61,6 +61,7 @@ pub struct StandardChannel<'a> { } impl<'a> StandardChannel<'a> { + #[allow(clippy::too_many_arguments)] pub fn new( channel_id: u32, user_identity: String, @@ -219,7 +220,7 @@ impl<'a> StandardChannel<'a> { } pub fn get_stale_jobs(&self) -> &HashMap> { - &self.job_store.get_stale_jobs() + self.job_store.get_stale_jobs() } pub fn get_shares_per_minute(&self) -> f32 { @@ -264,7 +265,6 @@ impl<'a> StandardChannel<'a> { coinbase_reward_outputs, ) .map_err(StandardChannelError::JobFactoryError)?; - let new_job_id = new_job.get_job_id(); self.job_store.add_future_job(template.template_id, new_job); } false => { diff --git a/roles/translator/src/lib/error.rs b/roles/translator/src/lib/error.rs index 4485acc23f..2e99cac40a 100644 --- a/roles/translator/src/lib/error.rs +++ b/roles/translator/src/lib/error.rs @@ -315,7 +315,7 @@ impl From, codec_sv2::buffer_sv2:: } } -impl<'a> From for Error<'a> { +impl From for Error<'_> { fn from(value: VardiffError) -> Self { Self::RolesSv2Logic(value.into()) } From b4f4a86e783a90f78e85c499e36e773f45454e0d Mon Sep 17 00:00:00 2001 From: Pavlenex <36959754+pavlenex@users.noreply.github.com> Date: Thu, 3 Jul 2025 09:35:04 +0500 Subject: [PATCH 051/338] Update readme logos --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ce735b11ac..facefddee0 100644 --- a/README.md +++ b/README.md @@ -120,10 +120,10 @@ Email us at: stratumv2@gmail.com SRI contributors are independently, financially supported by following entities:

- - - - + + + +

## 📖 License From 9b00659be932a8c045ae453d0b83f380f460052f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 2 Jul 2025 21:49:59 +0530 Subject: [PATCH 052/338] add close api --- roles/roles-utils/network-helpers/src/sv1_connection.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/roles/roles-utils/network-helpers/src/sv1_connection.rs b/roles/roles-utils/network-helpers/src/sv1_connection.rs index 8fed2a7594..019bff32c8 100644 --- a/roles/roles-utils/network-helpers/src/sv1_connection.rs +++ b/roles/roles-utils/network-helpers/src/sv1_connection.rs @@ -95,6 +95,11 @@ impl ConnectionSV1 { } } + pub fn close(&self) { + self.receiver.close(); + self.sender.close(); + } + /// Send a message to the other side of the connection. pub async fn send(&self, msg: json_rpc::Message) -> bool { self.sender.send(msg).await.is_ok() From c804c555a06afd53325d63388f34af4024c1557f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 2 Jul 2025 22:11:26 +0530 Subject: [PATCH 053/338] improve modularity of sv1 connection handler and improve error handling --- .../network-helpers/src/sv1_connection.rs | 133 +++++++++--------- 1 file changed, 70 insertions(+), 63 deletions(-) diff --git a/roles/roles-utils/network-helpers/src/sv1_connection.rs b/roles/roles-utils/network-helpers/src/sv1_connection.rs index 019bff32c8..57db1f1b51 100644 --- a/roles/roles-utils/network-helpers/src/sv1_connection.rs +++ b/roles/roles-utils/network-helpers/src/sv1_connection.rs @@ -2,7 +2,7 @@ use async_channel::{unbounded, Receiver, Sender}; use futures::{FutureExt, StreamExt}; use sv1_api::json_rpc; use tokio::{ - io::{AsyncWriteExt, BufReader}, + io::{AsyncWriteExt, BufReader, BufWriter}, net::TcpStream, }; use tokio_util::codec::{FramedRead, LinesCodec}; @@ -19,79 +19,86 @@ pub struct ConnectionSV1 { sender: Sender, } -const MAX_LINE_LENGTH: usize = 2_usize.pow(16); +const MAX_LINE_LENGTH: usize = 1 << 16; impl ConnectionSV1 { - /// Create a new connection set up to communicate with the other side of the given stream. - /// - /// Two tasks are spawned to handle reading and writing messages. The reading task will read - /// messages from the stream and send them to the receiver channel. The writing task will read - /// messages from the sender channel and write them to the stream. pub async fn new(stream: TcpStream) -> Self { - let (reader_stream, mut writer_stream) = stream.into_split(); + let (read_half, write_half) = stream.into_split(); let (sender_incoming, receiver_incoming) = unbounded(); - let (sender_outgoing, receiver_outgoing) = unbounded::(); - - // Read Job - tokio::task::spawn(async move { - let reader = BufReader::new(reader_stream); - let mut messages = - FramedRead::new(reader, LinesCodec::new_with_max_length(MAX_LINE_LENGTH)); - loop { - tokio::select! { - res = messages.next().fuse() => { - match res { - Some(Ok(incoming)) => { - let incoming: json_rpc::Message = serde_json::from_str(&incoming).expect("Failed to parse incoming message"); - if sender_incoming - .send(incoming) - .await - .is_err() - { - break; - } - - } - Some(Err(e)) => { - break tracing::error!("Error reading from stream: {:?}", e); - } - None => { - tracing::error!("No message received"); - } + let (sender_outgoing, receiver_outgoing) = unbounded(); + + let sender_outgoing_clone = sender_outgoing.clone(); + + let buffer_read_half = BufReader::new(read_half); + let buffer_write_half = BufWriter::new(write_half); + + tokio::spawn(async move { + tokio::select! { + _ = Self::run_reader(buffer_read_half, sender_incoming.clone()) => { + tracing::info!("Reader task exited. Closing writer sender."); + sender_outgoing_clone.close(); + } + _ = Self::run_writer(buffer_write_half, receiver_outgoing.clone()) => { + tracing::info!("Writer task exited.Closing reader sender."); + sender_incoming.close(); + } + } + }); + + Self { + receiver: receiver_incoming, + sender: sender_outgoing, + } + } + + async fn run_reader( + reader: BufReader, + sender: Sender, + ) { + let mut lines = FramedRead::new(reader, LinesCodec::new_with_max_length(MAX_LINE_LENGTH)); + while let Some(result) = lines.next().await { + match result { + Ok(line) => match serde_json::from_str::(&line) { + Ok(msg) => { + if sender.send(msg).await.is_err() { + tracing::warn!("Receiver dropped, stopping reader"); + break; } - }, - _ = tokio::signal::ctrl_c().fuse() => { - break; } - }; + Err(e) => { + tracing::error!("Failed to deserialize message: {e:?}"); + } + }, + Err(e) => { + tracing::error!("Error reading from stream: {e:?}"); + break; + } } - }); + } + } - // Write Job - tokio::task::spawn(async move { - loop { - tokio::select! { - res = receiver_outgoing.recv().fuse() => { - let to_send = res.expect("Failed to receive message"); - let to_send = match serde_json::to_string(&to_send) { - Ok(string) => format!("{string}\n"), - Err(_e) => { - break; - } - }; - let _ = writer_stream - .write_all(to_send.as_bytes()) - .await; - }, - _ = tokio::signal::ctrl_c().fuse() => { + async fn run_writer( + mut writer: BufWriter, + receiver: Receiver, + ) { + while let Ok(msg) = receiver.recv().await { + match serde_json::to_string(&msg) { + Ok(line) => { + let data = format!("{line}\n"); + if writer.write_all(data.as_bytes()).await.is_err() { + tracing::error!("Failed to write to stream"); break; } - }; + if writer.flush().await.is_err() { + tracing::error!("Failed to flush writer."); + break; + } + } + Err(e) => { + tracing::error!("Failed to serialize message: {e:?}"); + break; + } } - }); - Self { - receiver: receiver_incoming, - sender: sender_outgoing, } } From fa3902bef0f620d99ecb059234075c5d2756f743 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 2 Jul 2025 22:16:09 +0530 Subject: [PATCH 054/338] add connection state --- .../network-helpers/src/sv1_connection.rs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/roles/roles-utils/network-helpers/src/sv1_connection.rs b/roles/roles-utils/network-helpers/src/sv1_connection.rs index 57db1f1b51..53383c7663 100644 --- a/roles/roles-utils/network-helpers/src/sv1_connection.rs +++ b/roles/roles-utils/network-helpers/src/sv1_connection.rs @@ -1,5 +1,5 @@ use async_channel::{unbounded, Receiver, Sender}; -use futures::{FutureExt, StreamExt}; +use futures::StreamExt; use sv1_api::json_rpc; use tokio::{ io::{AsyncWriteExt, BufReader, BufWriter}, @@ -19,6 +19,31 @@ pub struct ConnectionSV1 { sender: Sender, } +struct ConnectionState { + receiver_outgoing: Receiver, + sender_outgoing: Sender, + receiver_incoming: Receiver, + sender_incoming: Sender, +} + +impl ConnectionState { + fn new(receiver_outgoing: Receiver, sender_outgoing: Sender, receiver_incoming: Receiver,sender_incoming: Sender )-> Self { + Self { + receiver_incoming, + receiver_outgoing, + sender_incoming, + sender_outgoing + } + } + + fn close(&self) { + self.receiver_incoming.close(); + self.receiver_outgoing.close(); + self.sender_incoming.close(); + self.sender_outgoing.close(); + } +} + const MAX_LINE_LENGTH: usize = 1 << 16; impl ConnectionSV1 { From b65401fdee98622004c077220c3aa4b8ecc187ab Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 2 Jul 2025 22:18:40 +0530 Subject: [PATCH 055/338] add connection state closure to reader and writer task --- .../network-helpers/src/sv1_connection.rs | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/roles/roles-utils/network-helpers/src/sv1_connection.rs b/roles/roles-utils/network-helpers/src/sv1_connection.rs index 53383c7663..cad4d49ccf 100644 --- a/roles/roles-utils/network-helpers/src/sv1_connection.rs +++ b/roles/roles-utils/network-helpers/src/sv1_connection.rs @@ -27,12 +27,17 @@ struct ConnectionState { } impl ConnectionState { - fn new(receiver_outgoing: Receiver, sender_outgoing: Sender, receiver_incoming: Receiver,sender_incoming: Sender )-> Self { + fn new( + receiver_outgoing: Receiver, + sender_outgoing: Sender, + receiver_incoming: Receiver, + sender_incoming: Sender, + ) -> Self { Self { receiver_incoming, receiver_outgoing, sender_incoming, - sender_outgoing + sender_outgoing, } } @@ -52,20 +57,25 @@ impl ConnectionSV1 { let (sender_incoming, receiver_incoming) = unbounded(); let (sender_outgoing, receiver_outgoing) = unbounded(); - let sender_outgoing_clone = sender_outgoing.clone(); - let buffer_read_half = BufReader::new(read_half); let buffer_write_half = BufWriter::new(write_half); + let connection_state = ConnectionState::new( + receiver_outgoing.clone(), + sender_outgoing.clone(), + receiver_incoming.clone(), + sender_incoming.clone(), + ); + tokio::spawn(async move { tokio::select! { _ = Self::run_reader(buffer_read_half, sender_incoming.clone()) => { tracing::info!("Reader task exited. Closing writer sender."); - sender_outgoing_clone.close(); + connection_state.close(); } _ = Self::run_writer(buffer_write_half, receiver_outgoing.clone()) => { tracing::info!("Writer task exited.Closing reader sender."); - sender_incoming.close(); + connection_state.close(); } } }); @@ -127,11 +137,6 @@ impl ConnectionSV1 { } } - pub fn close(&self) { - self.receiver.close(); - self.sender.close(); - } - /// Send a message to the other side of the connection. pub async fn send(&self, msg: json_rpc::Message) -> bool { self.sender.send(msg).await.is_ok() From d428dbc76f5a8c753a5dce6e3e7894863095803e Mon Sep 17 00:00:00 2001 From: plebhash Date: Mon, 30 Jun 2025 11:52:23 -0300 Subject: [PATCH 056/338] add check on coinbase output script pubkey for custom job on Pool --- protocols/v2/roles-logic-sv2/src/errors.rs | 2 ++ .../src/lib/mining_pool/message_handler.rs | 33 ++++++++++++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/errors.rs b/protocols/v2/roles-logic-sv2/src/errors.rs index 194f38eca3..93f3a4bb2a 100644 --- a/protocols/v2/roles-logic-sv2/src/errors.rs +++ b/protocols/v2/roles-logic-sv2/src/errors.rs @@ -134,6 +134,7 @@ pub enum Error { NoActiveJob, FailedToSendSolution, FailedToSetCustomMiningJob(ExtendedChannelError), + FailedToDeserializeCoinbaseOutputs, } impl From for Error { @@ -256,6 +257,7 @@ impl Display for Error { FailedToProcessNewTemplateStandardChannel(e) => write!(f, "Failed to process NewTemplate: {e:?}"), FailedToProcessSetNewPrevHashExtendedChannel(e) => write!(f, "Failed to process SetNewPrevHash: {e:?}"), FailedToProcessSetNewPrevHashStandardChannel(e) => write!(f, "Failed to process SetNewPrevHash: {e:?}"), + FailedToDeserializeCoinbaseOutputs => write!(f, "Failed to deserialize coinbase outputs"), } } } diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index 30876f6256..0ee937c39f 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -11,7 +11,7 @@ use std::{ sync::{Arc, RwLock}, }; use stratum_common::roles_logic_sv2::{ - bitcoin::Amount, + bitcoin::{consensus::Decodable, transaction::TxOut, Amount}, channels::server::{ error::{ExtendedChannelError, StandardChannelError}, extended::ExtendedChannel, @@ -884,10 +884,33 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { // this is a naive implementation, but ideally we should check the SetCustomMiningJob // message parameters, especially: // - the mining_job_token - // - the coinbase reward outputs - - // some of these checks are actually pending on spec discussion of - // https://github.com/stratum-mining/sv2-spec/issues/133 + // - the amount of the pool payout output + + let custom_job_coinbase_outputs = Vec::::consensus_decode( + &mut m.coinbase_tx_outputs.inner_as_ref().to_vec().as_slice(), + ) + .map_err(|_| Error::FailedToDeserializeCoinbaseOutputs)?; + + // check that all script_pubkeys from self.empty_pool_coinbase_outputs are present in the + // custom job coinbase outputs + for pool_output in self.empty_pool_coinbase_outputs.iter() { + let script_found = custom_job_coinbase_outputs + .iter() + .any(|custom_output| custom_output.script_pubkey == pool_output.script_pubkey); + + if !script_found { + error!("SetCustomMiningJobError: pool-payout-script-missing"); + let error = SetCustomMiningJobError { + request_id: m.request_id, + channel_id: m.channel_id, + error_code: "pool-payout-script-missing" + .to_string() + .try_into() + .expect("error code must be valid string"), + }; + return Ok(SendTo::Respond(Mining::SetCustomMiningJobError(error))); + } + } let channel_id = m.channel_id; if !self.extended_channels.contains_key(&channel_id) { From 1c889478bb18d74c929bec7565d7de056a0e3a05 Mon Sep 17 00:00:00 2001 From: plebhash <147345153+plebhash@users.noreply.github.com> Date: Fri, 4 Jul 2025 10:31:34 -0300 Subject: [PATCH 057/338] improve logic for checking missing script Co-authored-by: bit-aloo <84662239+Shourya742@users.noreply.github.com> --- .../src/lib/mining_pool/message_handler.rs | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index 0ee937c39f..c3bc787f16 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -893,23 +893,25 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { // check that all script_pubkeys from self.empty_pool_coinbase_outputs are present in the // custom job coinbase outputs - for pool_output in self.empty_pool_coinbase_outputs.iter() { - let script_found = custom_job_coinbase_outputs + let missing_script = self.empty_pool_coinbase_outputs.iter().find(|pool_output| { + !custom_job_coinbase_outputs .iter() - .any(|custom_output| custom_output.script_pubkey == pool_output.script_pubkey); + .any(|custom_output| custom_output.script_pubkey == pool_output.script_pubkey) + }); - if !script_found { - error!("SetCustomMiningJobError: pool-payout-script-missing"); - let error = SetCustomMiningJobError { - request_id: m.request_id, - channel_id: m.channel_id, - error_code: "pool-payout-script-missing" - .to_string() - .try_into() - .expect("error code must be valid string"), - }; - return Ok(SendTo::Respond(Mining::SetCustomMiningJobError(error))); - } + if missing_script.is_some() { + error!("SetCustomMiningJobError: pool-payout-script-missing"); + + let error = SetCustomMiningJobError { + request_id: m.request_id, + channel_id: m.channel_id, + error_code: "pool-payout-script-missing" + .to_string() + .try_into() + .expect("error code must be valid string"), + }; + + return Ok(SendTo::Respond(Mining::SetCustomMiningJobError(error))); } let channel_id = m.channel_id; From ffd060573ed156a5a1f403d31ba3bb9311a007e5 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Wed, 18 Jun 2025 23:42:29 -0400 Subject: [PATCH 058/338] implements Display trait for several low-level types --- .../src/datatypes/non_copy_data_types/mod.rs | 226 +++++++++++++++++- 1 file changed, 225 insertions(+), 1 deletion(-) diff --git a/protocols/v2/binary-sv2/codec/src/datatypes/non_copy_data_types/mod.rs b/protocols/v2/binary-sv2/codec/src/datatypes/non_copy_data_types/mod.rs index f9f2d55997..057fd298f8 100644 --- a/protocols/v2/binary-sv2/codec/src/datatypes/non_copy_data_types/mod.rs +++ b/protocols/v2/binary-sv2/codec/src/datatypes/non_copy_data_types/mod.rs @@ -31,9 +31,9 @@ #[cfg(feature = "prop_test")] use quickcheck::{Arbitrary, Gen}; -use alloc::string::String; #[cfg(feature = "prop_test")] use alloc::vec::Vec; +use alloc::{borrow::ToOwned, fmt, string::String}; mod inner; mod seq_inner; @@ -74,6 +74,230 @@ pub type B064K<'a> = Inner<'a, false, 1, 2, { u16::MAX as usize }>; /// represented using the `Inner` type with a 3-byte header. pub type B016M<'a> = Inner<'a, false, 1, 3, { 2_usize.pow(24) - 1 }>; +impl fmt::Display for U32AsRef<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let inner = self.inner_as_ref(); + write!( + f, + "U32AsRef({})", + u32::from_le_bytes([inner[0], inner[1], inner[2], inner[3]]) + ) + } +} + +impl fmt::Display for B0255<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let inner = self + .inner_as_ref() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect::(); + write!(f, "B0255({inner})") + } +} + +impl fmt::Display for Sv2Option<'_, u32> { + // internally Sv2Option is pub struct Sv2Option<'a, T>(pub Vec, PhantomData<&'a T>); + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let inner = self.to_owned().into_inner(); + match inner { + Some(value) => write!(f, "Sv2Option({value})"), + None => write!(f, "Sv2Option(None)"), + } + } +} + +impl Str0255<'_> { + /// Returns the value as a UTF-8 string if possible, otherwise as a hex string prefixed with 0x. + pub fn as_utf8_or_hex(&self) -> String { + match core::str::from_utf8(self.inner_as_ref()) { + Ok(s) => alloc::string::String::from(s), + Err(_) => format!( + "0x{}", + self.inner_as_ref() + .iter() + .map(|b| format!("{b:02x}")) + .collect::() + ), + } + } +} + +impl fmt::Display for B064K<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let inner = self + .inner_as_ref() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect::(); + write!(f, "B064K({inner})") + } +} + +impl fmt::Display for U256<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let inner = self + .inner_as_ref() + .iter() + .rev() + .map(|byte| format!("{byte:02x}")) + .collect::(); + write!(f, "U256({inner})") + } +} + +impl fmt::Display for Seq0255<'_, U256<'_>> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let len = self.0.len(); + let as_hex = |item: &U256<'_>| { + item.inner_as_ref() + .iter() + .rev() + .map(|byte| format!("{byte:02x}")) + .collect::() + }; + write!(f, "Seq0255 write!(f, "[]"), + 1 => write!(f, "{}]", as_hex(&self.0[0])), + 2 => write!(f, "{}, {}]", as_hex(&self.0[0]), as_hex(&self.0[1])), + 3 => write!( + f, + "[{}, {}, {}]", + as_hex(&self.0[0]), + as_hex(&self.0[1]), + as_hex(&self.0[2]) + ), + _ => write!( + f, + "[{}, {}, ... , {}, {}]", + as_hex(&self.0[0]), + as_hex(&self.0[1]), + as_hex(&self.0[len - 2]), + as_hex(&self.0[len - 1]) + ), + } + } +} + +impl fmt::Display for Seq064K<'_, B016M<'_>> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let len = self.0.len(); + let as_hex = |item: &B016M<'_>| { + item.inner_as_ref() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect::() + }; + write!(f, "Seq064K write!(f, "[]"), + 1 => write!(f, "[{}]", as_hex(&self.0[0])), + 2 => write!(f, "[{}, {}]", as_hex(&self.0[0]), as_hex(&self.0[1])), + 3 => write!( + f, + "[{}, {}, {}]", + as_hex(&self.0[0]), + as_hex(&self.0[1]), + as_hex(&self.0[2]) + ), + _ => write!( + f, + "[{}, {}, ... , {}, {}]", + as_hex(&self.0[0]), + as_hex(&self.0[1]), + as_hex(&self.0[len - 2]), + as_hex(&self.0[len - 1]) + ), + } + } +} + +impl fmt::Display for Seq064K<'_, U256<'_>> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let len = self.0.len(); + let as_hex = |item: &U256<'_>| { + item.inner_as_ref() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect::() + }; + write!(f, "Seq064K write!(f, "[]"), + 1 => write!(f, "[{}]", as_hex(&self.0[0])), + 2 => write!(f, "[{}, {}]", as_hex(&self.0[0]), as_hex(&self.0[1])), + 3 => write!( + f, + "[{}, {}, {}]", + as_hex(&self.0[0]), + as_hex(&self.0[1]), + as_hex(&self.0[2]) + ), + _ => write!( + f, + "[{}, {}, ... , {}, {}]", + as_hex(&self.0[0]), + as_hex(&self.0[1]), + as_hex(&self.0[len - 2]), + as_hex(&self.0[len - 1]) + ), + } + } +} + +impl fmt::Display for Seq064K<'_, u16> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let len = self.0.len(); + write!(f, "Seq064K write!(f, "[]"), + 1 => write!(f, "[{}]", self.0[0]), + 2 => write!(f, "[{}, {}]", self.0[0], self.0[1]), + 3 => write!(f, "[{}, {}, {}]", self.0[0], self.0[1], self.0[2]), + _ => write!( + f, + "[{}, {}, ... , {}, {}]", + self.0[0], + self.0[1], + self.0[len - 2], + self.0[len - 1] + ), + } + } +} +impl fmt::Display for Seq064K<'_, u32> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let len = self.0.len(); + write!(f, "Seq064K write!(f, "[]"), + 1 => write!(f, "[{}]", self.0[0]), + 2 => write!(f, "[{}, {}]", self.0[0], self.0[1]), + 3 => write!(f, "[{}, {}, {}]", self.0[0], self.0[1], self.0[2]), + _ => write!( + f, + "[{}, {}, ... , {}, {}]", + self.0[0], + self.0[1], + self.0[len - 2], + self.0[len - 1] + ), + } + } +} + +impl fmt::Display for B032<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let item = self + .inner_as_ref() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect::(); + write!(f, "B032({item})") + } +} + impl From<[u8; 32]> for U256<'_> { fn from(v: [u8; 32]) -> Self { Inner::Owned(v.into()) From 6815a14114683a22fa2b011c3c0664ed2992d35d Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Wed, 18 Jun 2025 23:43:01 -0400 Subject: [PATCH 059/338] Add Display implementations for all message structs in template distribution subprotocol --- .../src/coinbase_output_constraints.rs | 13 +++++++- .../template-distribution/src/new_template.rs | 25 +++++++++++++- .../src/request_transaction_data.rs | 33 ++++++++++++++++++- .../src/set_new_prev_hash.rs | 16 ++++++++- .../src/submit_solution.rs | 16 ++++++++- 5 files changed, 98 insertions(+), 5 deletions(-) diff --git a/protocols/v2/subprotocols/template-distribution/src/coinbase_output_constraints.rs b/protocols/v2/subprotocols/template-distribution/src/coinbase_output_constraints.rs index 3824bcb0b0..318afa3ea7 100644 --- a/protocols/v2/subprotocols/template-distribution/src/coinbase_output_constraints.rs +++ b/protocols/v2/subprotocols/template-distribution/src/coinbase_output_constraints.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize}; use core::convert::TryInto; @@ -30,3 +30,14 @@ pub struct CoinbaseOutputConstraints { /// Additional sigops needed in coinbase transaction outputs. pub coinbase_output_max_additional_sigops: u16, } + +impl fmt::Display for CoinbaseOutputConstraints { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "CoinbaseOutputConstraints(coinbase_output_max_additional_size: {}, coinbase_output_max_additional_sigops: {})", + self.coinbase_output_max_additional_size, + self.coinbase_output_max_additional_sigops + ) + } +} diff --git a/protocols/v2/subprotocols/template-distribution/src/new_template.rs b/protocols/v2/subprotocols/template-distribution/src/new_template.rs index 650d5e9569..362bbc920b 100644 --- a/protocols/v2/subprotocols/template-distribution/src/new_template.rs +++ b/protocols/v2/subprotocols/template-distribution/src/new_template.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{ binary_codec_sv2::{self, free_vec, free_vec_2, CVec, CVec2}, Deserialize, Error, Seq0255, Serialize, B0255, B064K, U256, @@ -52,6 +52,29 @@ pub struct NewTemplate<'decoder> { pub merkle_path: Seq0255<'decoder, U256<'decoder>>, } +impl fmt::Display for NewTemplate<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "NewTemplate(template_id: {}, future_template: {}, version: {}, coinbase_tx_version: {}, \ + coinbase_prefix: {}, coinbase_tx_input_sequence: {}, coinbase_tx_value_remaining: {}, \ + coinbase_tx_outputs_count: {}, coinbase_tx_outputs: {}, coinbase_tx_locktime: {}, \ + merkle_path: {})", + self.template_id, + self.future_template, + self.version, + self.coinbase_tx_version, + self.coinbase_prefix, + self.coinbase_tx_input_sequence, + self.coinbase_tx_value_remaining, + self.coinbase_tx_outputs_count, + self.coinbase_tx_outputs, + self.coinbase_tx_locktime, + self.merkle_path + ) + } +} + /// C representation of [`NewTemplate`]. #[repr(C)] pub struct CNewTemplate { diff --git a/protocols/v2/subprotocols/template-distribution/src/request_transaction_data.rs b/protocols/v2/subprotocols/template-distribution/src/request_transaction_data.rs index ffcaf9b325..8844d392bd 100644 --- a/protocols/v2/subprotocols/template-distribution/src/request_transaction_data.rs +++ b/protocols/v2/subprotocols/template-distribution/src/request_transaction_data.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{ binary_codec_sv2::{self, free_vec, free_vec_2, CVec, CVec2}, Deserialize, Error, Seq064K, Serialize, Str0255, B016M, B064K, @@ -19,6 +19,16 @@ pub struct RequestTransactionData { pub template_id: u64, } +impl fmt::Display for RequestTransactionData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "RequestTransactionData(template_id: {})", + self.template_id + ) + } +} + /// Message used by an upstream(Template Provider) to respond successfully to a /// [`RequestTransactionData`] message. /// @@ -62,6 +72,16 @@ pub struct RequestTransactionDataSuccess<'decoder> { pub transaction_list: Seq064K<'decoder, B016M<'decoder>>, } +impl fmt::Display for RequestTransactionDataSuccess<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "RequestTransactionDataSuccess(template_id: {}, excess_data: {}, transaction_list: {})", + self.template_id, self.excess_data, self.transaction_list + ) + } +} + /// C representation of [`RequestTransactionDataSuccess`]. #[repr(C)] pub struct CRequestTransactionDataSuccess { @@ -125,6 +145,17 @@ pub struct RequestTransactionDataError<'decoder> { pub error_code: Str0255<'decoder>, } +impl fmt::Display for RequestTransactionDataError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "RequestTransactionDataError(template_id: {}, error_code: {})", + self.template_id, + self.error_code.as_utf8_or_hex() + ) + } +} + /// C representation of [`RequestTransactionDataError`]. #[repr(C)] pub struct CRequestTransactionDataError { diff --git a/protocols/v2/subprotocols/template-distribution/src/set_new_prev_hash.rs b/protocols/v2/subprotocols/template-distribution/src/set_new_prev_hash.rs index b3ee50f433..901ddb8b35 100644 --- a/protocols/v2/subprotocols/template-distribution/src/set_new_prev_hash.rs +++ b/protocols/v2/subprotocols/template-distribution/src/set_new_prev_hash.rs @@ -3,7 +3,7 @@ use binary_sv2::{ binary_codec_sv2::{self, free_vec, CVec}, Deserialize, Error, Serialize, U256, }; -use core::convert::TryInto; +use core::{convert::TryInto, fmt}; /// Message used by an upstream(Template Provider) to indicate the latest block header hash /// to mine on. @@ -33,6 +33,20 @@ pub struct SetNewPrevHash<'decoder> { pub target: U256<'decoder>, } +impl fmt::Display for SetNewPrevHash<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SetNewPrevHash {{ template_id: {}, prev_hash: {}, header_timestamp: {}, n_bits: {}, target: {} }}", + self.template_id, + self.prev_hash, + self.header_timestamp, + self.n_bits, + self.target + ) + } +} + /// C representation of [`SetNewPrevHash`]. #[repr(C)] pub struct CSetNewPrevHash { diff --git a/protocols/v2/subprotocols/template-distribution/src/submit_solution.rs b/protocols/v2/subprotocols/template-distribution/src/submit_solution.rs index fe6ad63884..6ca1e44c1f 100644 --- a/protocols/v2/subprotocols/template-distribution/src/submit_solution.rs +++ b/protocols/v2/subprotocols/template-distribution/src/submit_solution.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{ binary_codec_sv2::{self, free_vec, CVec}, Deserialize, Error, Serialize, B064K, @@ -39,6 +39,20 @@ pub struct SubmitSolution<'decoder> { pub coinbase_tx: B064K<'decoder>, } +impl fmt::Display for SubmitSolution<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SubmitSolution {{ template_id: {}, version: {}, header_timestamp: {}, header_nonce: {}, coinbase_tx: {} }}", + self.template_id, + self.version, + self.header_timestamp, + self.header_nonce, + self.coinbase_tx + ) + } +} + /// C representation of [`SubmitSolution`]. #[repr(C)] pub struct CSubmitSolution { From 983c3a547b58f3a086ab9b6b674529b54f2ac4d8 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 19 Jun 2025 00:06:14 -0400 Subject: [PATCH 060/338] Implement Display trait for all common messages --- .../src/channel_endpoint_changed.rs | 8 +++- .../common-messages/src/reconnect.rs | 13 +++++- .../common-messages/src/setup_connection.rs | 42 ++++++++++++++++++- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/protocols/v2/subprotocols/common-messages/src/channel_endpoint_changed.rs b/protocols/v2/subprotocols/common-messages/src/channel_endpoint_changed.rs index f3988a7400..8cd8b1c67b 100644 --- a/protocols/v2/subprotocols/common-messages/src/channel_endpoint_changed.rs +++ b/protocols/v2/subprotocols/common-messages/src/channel_endpoint_changed.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize}; use core::convert::TryInto; @@ -16,3 +16,9 @@ pub struct ChannelEndpointChanged { /// Unique identifier of the channel that has changed its endpoint. pub channel_id: u32, } + +impl fmt::Display for ChannelEndpointChanged { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ChannelEndpointChanged(channel_id: {})", self.channel_id) + } +} diff --git a/protocols/v2/subprotocols/common-messages/src/reconnect.rs b/protocols/v2/subprotocols/common-messages/src/reconnect.rs index 33ab80ac81..9e33a769a3 100644 --- a/protocols/v2/subprotocols/common-messages/src/reconnect.rs +++ b/protocols/v2/subprotocols/common-messages/src/reconnect.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize, Str0255}; use core::convert::TryInto; @@ -20,6 +20,17 @@ pub struct Reconnect<'decoder> { pub new_port: u16, } +impl fmt::Display for Reconnect<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Reconnect(new_host: {}, new_port: {})", + self.new_host.as_utf8_or_hex(), + self.new_port + ) + } +} + impl PartialEq for Reconnect<'_> { fn eq(&self, other: &Self) -> bool { self.new_host.as_ref() == other.new_host.as_ref() && self.new_port == other.new_port diff --git a/protocols/v2/subprotocols/common-messages/src/setup_connection.rs b/protocols/v2/subprotocols/common-messages/src/setup_connection.rs index e0ff207d65..c2ccabca34 100644 --- a/protocols/v2/subprotocols/common-messages/src/setup_connection.rs +++ b/protocols/v2/subprotocols/common-messages/src/setup_connection.rs @@ -2,7 +2,7 @@ use crate::{ SV2_JOB_DECLARATION_PROTOCOL_DISCRIMINANT, SV2_MINING_PROTOCOL_DISCRIMINANT, SV2_TEMPLATE_DISTRIBUTION_PROTOCOL_DISCRIMINANT, }; -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{ binary_codec_sv2, binary_codec_sv2::CVec, @@ -54,6 +54,25 @@ pub struct SetupConnection<'decoder> { pub device_id: Str0255<'decoder>, } +impl fmt::Display for SetupConnection<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SetupConnection(protocol: {}, min_version: {}, max_version: {}, flags: {}, endpoint_host: {}, endpoint_port: {}, vendor: {}, hardware_version: {}, firmware: {}, device_id: {})", + self.protocol as u8, + self.min_version, + self.max_version, + self.flags, + self.endpoint_host.as_utf8_or_hex(), + self.endpoint_port, + self.vendor.as_utf8_or_hex(), + self.hardware_version.as_utf8_or_hex(), + self.firmware.as_utf8_or_hex(), + self.device_id.as_utf8_or_hex() + ) + } +} + impl SetupConnection<'_> { /// Set the flag to indicate that the downstream requires a standard job pub fn set_requires_standard_job(&mut self) { @@ -287,6 +306,16 @@ pub struct SetupConnectionSuccess { pub flags: u32, } +impl fmt::Display for SetupConnectionSuccess { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SetupConnectionSuccess(used_version: {}, flags: {})", + self.used_version, self.flags + ) + } +} + /// Message used by an upstream role to reject a connection setup request from a downstream role. /// /// This message is sent in response to a [`SetupConnection`] message. @@ -316,6 +345,17 @@ pub struct SetupConnectionError<'decoder> { pub error_code: Str0255<'decoder>, } +impl fmt::Display for SetupConnectionError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SetupConnectionError(flags: {}, error_code: {})", + self.flags, + self.error_code.as_utf8_or_hex() + ) + } +} + #[repr(C)] #[derive(Debug, Clone)] /// C representation of [`SetupConnectionError`] From 8ce21729b22e8b7913da2f9c6b9656c05c904dba Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 19 Jun 2025 00:48:15 -0400 Subject: [PATCH 061/338] Implement Display trait for job declaration messages --- .../src/allocate_mining_job_token.rs | 25 +++++++++++- .../job-declaration/src/declare_mining_job.rs | 40 ++++++++++++++++++- .../src/provide_missing_transactions.rs | 21 +++++++++- .../job-declaration/src/push_solution.rs | 17 +++++++- 4 files changed, 99 insertions(+), 4 deletions(-) diff --git a/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs b/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs index 4329fb52af..6ed2cd188d 100644 --- a/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs +++ b/protocols/v2/subprotocols/job-declaration/src/allocate_mining_job_token.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize, Str0255, B0255, B064K}; use core::convert::TryInto; @@ -14,6 +14,17 @@ pub struct AllocateMiningJobToken<'decoder> { pub request_id: u32, } +impl fmt::Display for AllocateMiningJobToken<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "AllocateMiningJobToken(user_identifier: {}, request_id: {})", + self.user_identifier.as_utf8_or_hex(), + self.request_id + ) + } +} + /// Message used by JDS to accept [`AllocateMiningJobToken`] message. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[repr(C)] @@ -28,3 +39,15 @@ pub struct AllocateMiningJobTokenSuccess<'decoder> { /// Bitcoin transaction outputs added by JDS. pub coinbase_outputs: B064K<'decoder>, } + +impl fmt::Display for AllocateMiningJobTokenSuccess<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "AllocateMiningJobTokenSuccess(request_id: {}, mining_job_token: {}, coinbase_outputs: {})", + self.request_id, + self.mining_job_token, + self.coinbase_outputs + ) + } +} diff --git a/protocols/v2/subprotocols/job-declaration/src/declare_mining_job.rs b/protocols/v2/subprotocols/job-declaration/src/declare_mining_job.rs index 8cc9e3ba7a..d217cbcab6 100644 --- a/protocols/v2/subprotocols/job-declaration/src/declare_mining_job.rs +++ b/protocols/v2/subprotocols/job-declaration/src/declare_mining_job.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Seq064K, Serialize, Str0255, B0255, B064K, U256}; use core::convert::TryInto; @@ -34,6 +34,22 @@ pub struct DeclareMiningJob<'decoder> { pub excess_data: B064K<'decoder>, } +impl fmt::Display for DeclareMiningJob<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "DeclareMiningJob(request_id: {}, mining_job_token: {}, version: {}, coinbase_prefix: {}, coinbase_suffix: {}, tx_ids_list: {}, excess_data: {})", + self.request_id, + self.mining_job_token, + self.version, + self.coinbase_prefix, + self.coinbase_suffix, + self.tx_ids_list, + self.excess_data + ) + } +} + /// Messaged used by JDS to accept [`DeclareMiningJob`] message. /// /// If [`Full Template`] mode is used, JDS MAY request txdata via `ProvideMissingTransactions` @@ -54,6 +70,16 @@ pub struct DeclareMiningJobSuccess<'decoder> { pub new_mining_job_token: B0255<'decoder>, } +impl fmt::Display for DeclareMiningJobSuccess<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "DeclareMiningJobSuccess(request_id: {}, new_mining_job_token: {})", + self.request_id, self.new_mining_job_token + ) + } +} + /// Messaged used by JDS to reject [`DeclareMiningJob`] message. /// /// Downstream should consider this as a trigger to fallback into some other Pool/JDS or solo @@ -73,3 +99,15 @@ pub struct DeclareMiningJobError<'decoder> { /// Optional details about the error. pub error_details: B064K<'decoder>, } + +impl fmt::Display for DeclareMiningJobError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "DeclareMiningJobError(request_id: {}, error_code: {}, error_details: {})", + self.request_id, + self.error_code.as_utf8_or_hex(), + self.error_details + ) + } +} diff --git a/protocols/v2/subprotocols/job-declaration/src/provide_missing_transactions.rs b/protocols/v2/subprotocols/job-declaration/src/provide_missing_transactions.rs index e6e4782866..6618745fc6 100644 --- a/protocols/v2/subprotocols/job-declaration/src/provide_missing_transactions.rs +++ b/protocols/v2/subprotocols/job-declaration/src/provide_missing_transactions.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Seq064K, Serialize, B016M}; use core::convert::TryInto; @@ -28,6 +28,16 @@ pub struct ProvideMissingTransactions<'decoder> { pub unknown_tx_position_list: Seq064K<'decoder, u16>, } +impl fmt::Display for ProvideMissingTransactions<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ProvideMissingTransactions(request_id: {}, unknown_tx_position_list: {})", + self.request_id, self.unknown_tx_position_list + ) + } +} + /// Message used by JDC to accept [`ProvideMissingTransactions`] message and provide the full /// list of transactions in the order they were requested by [`ProvideMissingTransactions`]. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] @@ -40,3 +50,12 @@ pub struct ProvideMissingTransactionsSuccess<'decoder> { /// List of full transactions as requested by [`ProvideMissingTransactions`]. pub transaction_list: Seq064K<'decoder, B016M<'decoder>>, } +impl fmt::Display for ProvideMissingTransactionsSuccess<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ProvideMissingTransactionsSuccess(request_id: {}, transaction_list: {})", + self.request_id, self.transaction_list + ) + } +} diff --git a/protocols/v2/subprotocols/job-declaration/src/push_solution.rs b/protocols/v2/subprotocols/job-declaration/src/push_solution.rs index d4aa2a207b..cc5d155b2b 100644 --- a/protocols/v2/subprotocols/job-declaration/src/push_solution.rs +++ b/protocols/v2/subprotocols/job-declaration/src/push_solution.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize, B032, U256}; -use core::convert::TryInto; +use core::{convert::TryInto, fmt}; /// Message used by JDC to push a solution to JDS as soon as it finds a new valid block. /// @@ -35,3 +35,18 @@ pub struct PushSolution<'decoder> { /// [`BIP320`]: https://en.bitcoin.it/wiki/BIP_0320 pub version: u32, } + +impl fmt::Display for PushSolution<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "PushSolution(extranonce: {}, prev_hash: {}, ntime: {}, nonce: {}, nbits: {}, version: {})", + self.extranonce, + self.prev_hash, + self.ntime, + self.nonce, + self.nbits, + self.version + ) + } +} From 9c62e49d3dc1196d0db7d238b449b6ffc4306b27 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 19 Jun 2025 12:07:31 -0400 Subject: [PATCH 062/338] implements Display trait for mining messages --- .../subprotocols/mining/src/close_channel.rs | 13 +++- .../subprotocols/mining/src/new_mining_job.rs | 29 +++++++- .../subprotocols/mining/src/open_channel.rs | 68 ++++++++++++++++++- .../mining/src/set_custom_mining_job.rs | 44 +++++++++++- .../mining/src/set_extranonce_prefix.rs | 12 +++- .../mining/src/set_group_channel.rs | 12 +++- .../mining/src/set_new_prev_hash.rs | 12 +++- .../v2/subprotocols/mining/src/set_target.rs | 12 +++- .../subprotocols/mining/src/submit_shares.rs | 42 +++++++++++- .../subprotocols/mining/src/update_channel.rs | 22 +++++- 10 files changed, 256 insertions(+), 10 deletions(-) diff --git a/protocols/v2/subprotocols/mining/src/close_channel.rs b/protocols/v2/subprotocols/mining/src/close_channel.rs index 83e7be3f96..a9f32c8280 100644 --- a/protocols/v2/subprotocols/mining/src/close_channel.rs +++ b/protocols/v2/subprotocols/mining/src/close_channel.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize, Str0255}; use core::convert::TryInto; @@ -15,3 +15,14 @@ pub struct CloseChannel<'decoder> { /// Reason for closing the channel. pub reason_code: Str0255<'decoder>, } + +impl fmt::Display for CloseChannel<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "CloseChannel(channel_id: {}, reason_code: {})", + self.channel_id, + self.reason_code.as_utf8_or_hex() + ) + } +} diff --git a/protocols/v2/subprotocols/mining/src/new_mining_job.rs b/protocols/v2/subprotocols/mining/src/new_mining_job.rs index cfb39f5612..8a14b6950a 100644 --- a/protocols/v2/subprotocols/mining/src/new_mining_job.rs +++ b/protocols/v2/subprotocols/mining/src/new_mining_job.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; use binary_sv2::{binary_codec_sv2, Deserialize, Seq0255, Serialize, Sv2Option, B064K, U256}; -use core::convert::TryInto; +use core::{convert::TryInto, fmt}; /// Message used by an upstream to provide an updated mining job to downstream. /// @@ -46,6 +46,16 @@ pub struct NewMiningJob<'decoder> { 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: {}, 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() @@ -111,6 +121,23 @@ pub struct NewExtendedMiningJob<'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: {}, 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() diff --git a/protocols/v2/subprotocols/mining/src/open_channel.rs b/protocols/v2/subprotocols/mining/src/open_channel.rs index e5a34cd3c7..0af64e43f8 100644 --- a/protocols/v2/subprotocols/mining/src/open_channel.rs +++ b/protocols/v2/subprotocols/mining/src/open_channel.rs @@ -1,6 +1,6 @@ use alloc::{string::ToString, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize, Str0255, U32AsRef, B032, U256}; -use core::convert::TryInto; +use core::{convert::TryInto, fmt}; /// Message used by a downstream to request opening a Standard Channel. /// /// Upon receiving `SetupConnectionSuccess` message, the downstream should open channel(s) on the @@ -36,6 +36,19 @@ pub struct OpenStandardMiningChannel<'decoder> { 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() @@ -73,6 +86,20 @@ pub struct OpenStandardMiningChannelSuccess<'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() @@ -128,6 +155,20 @@ pub struct OpenExtendedMiningChannel<'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 @@ -154,6 +195,20 @@ pub struct OpenExtendedMiningChannelSuccess<'decoder> { pub extranonce_prefix: B032<'decoder>, } +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: {})", + self.request_id, + self.channel_id, + self.target, + self.extranonce_size, + self.extranonce_prefix + ) + } +} + /// Message used by upstream to reject [`OpenExtendedMiningChannel`] or /// [`OpenStandardMiningchannel`] request from downstream. #[derive(Serialize, Deserialize, Debug, Clone)] @@ -172,6 +227,17 @@ pub struct OpenMiningChannelError<'decoder> { 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 { diff --git a/protocols/v2/subprotocols/mining/src/set_custom_mining_job.rs b/protocols/v2/subprotocols/mining/src/set_custom_mining_job.rs index 7a9c74629f..431671910e 100644 --- a/protocols/v2/subprotocols/mining/src/set_custom_mining_job.rs +++ b/protocols/v2/subprotocols/mining/src/set_custom_mining_job.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Seq0255, Serialize, Str0255, B0255, B064K, U256}; use core::convert::TryInto; @@ -52,6 +52,26 @@ pub struct SetCustomMiningJob<'decoder> { pub merkle_path: Seq0255<'decoder, U256<'decoder>>, } +impl fmt::Display for SetCustomMiningJob<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SetCustomMiningJob(channel_id={}, request_id={}, token={}, version={}, prev_hash={}, min_ntime={}, nbits={}, coinbase_tx_version={}, coinbase_prefix={}, coinbase_tx_input_n_sequence={}, coinbase_tx_outputs={}, coinbase_tx_locktime={}, merkle_path={})", + self.channel_id, + self.request_id, + self.token, + self.version, + self.prev_hash, + self.min_ntime, + self.nbits, + self.coinbase_tx_version, + self.coinbase_prefix, + self.coinbase_tx_input_n_sequence, + self.coinbase_tx_outputs, + self.coinbase_tx_locktime, + self.merkle_path + ) + } +} + /// Message used by upstream to accept [`SetCustomMiningJob`] request. /// /// Upon receiving this message, downstream can start submitting shares for this job immediately (by @@ -66,6 +86,16 @@ pub struct SetCustomMiningJobSuccess { pub job_id: u32, } +impl fmt::Display for SetCustomMiningJobSuccess { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SetCustomMiningJobSuccess(channel_id={}, request_id={}, job_id={})", + self.channel_id, self.request_id, self.job_id + ) + } +} + /// Message used by upstream to reject [`SetCustomMiningJob`] request. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct SetCustomMiningJobError<'decoder> { @@ -81,3 +111,15 @@ pub struct SetCustomMiningJobError<'decoder> { /// - invalid-job-param-value-{field_name} pub error_code: Str0255<'decoder>, } + +impl fmt::Display for SetCustomMiningJobError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SetCustomMiningJobError(channel_id={}, request_id={}, error_code={})", + self.channel_id, + self.request_id, + self.error_code.as_utf8_or_hex() + ) + } +} diff --git a/protocols/v2/subprotocols/mining/src/set_extranonce_prefix.rs b/protocols/v2/subprotocols/mining/src/set_extranonce_prefix.rs index 8a78793056..e10f617468 100644 --- a/protocols/v2/subprotocols/mining/src/set_extranonce_prefix.rs +++ b/protocols/v2/subprotocols/mining/src/set_extranonce_prefix.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize, B032}; @@ -18,3 +18,13 @@ pub struct SetExtranoncePrefix<'decoder> { /// New extranonce prefix. pub extranonce_prefix: B032<'decoder>, } + +impl fmt::Display for SetExtranoncePrefix<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SetExtranoncePrefix(channel_id={}, extranonce_prefix={})", + self.channel_id, self.extranonce_prefix + ) + } +} diff --git a/protocols/v2/subprotocols/mining/src/set_group_channel.rs b/protocols/v2/subprotocols/mining/src/set_group_channel.rs index 6b5340a6d2..2662150cd5 100644 --- a/protocols/v2/subprotocols/mining/src/set_group_channel.rs +++ b/protocols/v2/subprotocols/mining/src/set_group_channel.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Seq064K, Serialize}; use core::convert::TryInto; @@ -24,3 +24,13 @@ pub struct SetGroupChannel<'decoder> { /// A sequence of opened standard channel IDs, for which the group channel is being redefined. pub channel_ids: Seq064K<'decoder, u32>, } + +impl fmt::Display for SetGroupChannel<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SetGroupChannel(group_channel_id={}, channel_ids={})", + self.group_channel_id, self.channel_ids + ) + } +} diff --git a/protocols/v2/subprotocols/mining/src/set_new_prev_hash.rs b/protocols/v2/subprotocols/mining/src/set_new_prev_hash.rs index e09f20b331..5b5a4348a3 100644 --- a/protocols/v2/subprotocols/mining/src/set_new_prev_hash.rs +++ b/protocols/v2/subprotocols/mining/src/set_new_prev_hash.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize, U256}; -use core::convert::TryInto; +use core::{convert::TryInto, fmt}; /// Message used by upstream to share or distribute the latest block hash. /// @@ -27,3 +27,13 @@ pub struct SetNewPrevHash<'decoder> { /// Block header field. pub nbits: u32, } + +impl fmt::Display for SetNewPrevHash<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SetNewPrevHash(channel_id={}, job_id={}, prev_hash={}, min_ntime={}, nbits={})", + self.channel_id, self.job_id, self.prev_hash, self.min_ntime, self.nbits + ) + } +} diff --git a/protocols/v2/subprotocols/mining/src/set_target.rs b/protocols/v2/subprotocols/mining/src/set_target.rs index 87e5db952b..62e5b07ed7 100644 --- a/protocols/v2/subprotocols/mining/src/set_target.rs +++ b/protocols/v2/subprotocols/mining/src/set_target.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize, U256}; use core::convert::TryInto; @@ -21,3 +21,13 @@ pub struct SetTarget<'decoder> { /// Maximum value of produced hash that will be accepted by a upstream to accept shares. pub maximum_target: U256<'decoder>, } + +impl fmt::Display for SetTarget<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SetTarget(channel_id={}, maximum_target={})", + self.channel_id, self.maximum_target + ) + } +} diff --git a/protocols/v2/subprotocols/mining/src/submit_shares.rs b/protocols/v2/subprotocols/mining/src/submit_shares.rs index 5ed2354f8e..359117bb3d 100644 --- a/protocols/v2/subprotocols/mining/src/submit_shares.rs +++ b/protocols/v2/subprotocols/mining/src/submit_shares.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize, Str0255, B032}; use core::convert::TryInto; @@ -26,6 +26,16 @@ pub struct SubmitSharesStandard { pub version: u32, } +impl fmt::Display for SubmitSharesStandard { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SubmitSharesStandard(channel_id={}, sequence_number={}, job_id={}, nonce={}, ntime={}, version={})", + self.channel_id, self.sequence_number, self.job_id, self.nonce, self.ntime, self.version + ) + } +} + /// Message used by downstream to send result of its hashing work to an upstream. /// /// The message is the same as [`SubmitShares`], but with an additional field, @@ -62,6 +72,16 @@ pub struct SubmitSharesExtended<'decoder> { pub extranonce: B032<'decoder>, } +impl fmt::Display for SubmitSharesExtended<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SubmitSharesExtended(channel_id={}, sequence_number={}, job_id={}, nonce={}, ntime={}, version={}, extranonce={})", + self.channel_id, self.sequence_number, self.job_id, self.nonce, self.ntime, self.version, self.extranonce + ) + } +} + /// Message used by upstream to accept [`SubmitSharesStandard`] or [`SubmitSharesExtended`]. /// /// Because it is a common case that shares submission is successful, this response can be provided @@ -82,6 +102,16 @@ pub struct SubmitSharesSuccess { pub new_shares_sum: u64, } +impl fmt::Display for SubmitSharesSuccess { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SubmitSharesSuccess(channel_id={}, last_sequence_number={}, new_submits_accepted_count={}, new_shares_sum={})", + self.channel_id, self.last_sequence_number, self.new_submits_accepted_count, self.new_shares_sum + ) + } +} + /// Message used by upstream to reject [`SubmitSharesStandard`] or [`SubmitSharesExtended`]. /// /// In case the upstream is not able to immediately validate the submission, the error is sent as @@ -104,6 +134,16 @@ pub struct SubmitSharesError<'decoder> { pub error_code: Str0255<'decoder>, } +impl fmt::Display for SubmitSharesError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SubmitSharesError(channel_id={}, sequence_number={}, error_code={})", + self.channel_id, self.sequence_number, self.error_code + ) + } +} + impl SubmitSharesError<'_> { pub fn invalid_channel_error_code() -> &'static str { "invalid-channel-id" diff --git a/protocols/v2/subprotocols/mining/src/update_channel.rs b/protocols/v2/subprotocols/mining/src/update_channel.rs index 4ca06831b6..1695b69238 100644 --- a/protocols/v2/subprotocols/mining/src/update_channel.rs +++ b/protocols/v2/subprotocols/mining/src/update_channel.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{fmt, vec::Vec}; use binary_sv2::{binary_codec_sv2, Deserialize, Serialize, Str0255, U256}; use core::convert::TryInto; @@ -34,6 +34,16 @@ pub struct UpdateChannel<'decoder> { pub maximum_target: U256<'decoder>, } +impl fmt::Display for UpdateChannel<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "⛏️ UpdateChannel(channel_id={}, nominal_hash_rate={}, maximum_target={})", + self.channel_id, self.nominal_hash_rate, self.maximum_target + ) + } +} + /// Message used by upstream to notify downstream about an error in the [`UpdateChannel`] message. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct UpdateChannelError<'decoder> { @@ -46,3 +56,13 @@ pub struct UpdateChannelError<'decoder> { /// - invalid-channel-id pub error_code: Str0255<'decoder>, } + +impl fmt::Display for UpdateChannelError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "UpdateChannelError(channel_id={}, error_code={})", + self.channel_id, self.error_code + ) + } +} From 58bb0bd5a67a300f9d3916737017ece2e94ac4b2 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 19 Jun 2025 16:21:00 -0400 Subject: [PATCH 063/338] implements Display trait for parsers --- protocols/v2/roles-logic-sv2/src/parsers.rs | 101 +++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/protocols/v2/roles-logic-sv2/src/parsers.rs b/protocols/v2/roles-logic-sv2/src/parsers.rs index 1907688de4..482137d069 100644 --- a/protocols/v2/roles-logic-sv2/src/parsers.rs +++ b/protocols/v2/roles-logic-sv2/src/parsers.rs @@ -31,7 +31,10 @@ use codec_sv2::{ framing_sv2::framing::Sv2Frame, }; use common_messages_sv2::*; -use core::convert::{TryFrom, TryInto}; +use core::{ + convert::{TryFrom, TryInto}, + fmt, +}; use job_declaration_sv2::*; use mining_sv2::*; use template_distribution_sv2::*; @@ -135,6 +138,18 @@ pub enum CommonMessages<'a> { SetupConnectionSuccess(SetupConnectionSuccess), } +impl fmt::Display for CommonMessages<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CommonMessages::ChannelEndpointChanged(m) => write!(f, "{m}"), + CommonMessages::Reconnect(m) => write!(f, "{m}"), + CommonMessages::SetupConnection(m) => write!(f, "{m}"), + CommonMessages::SetupConnectionError(m) => write!(f, "{m}"), + CommonMessages::SetupConnectionSuccess(m) => write!(f, "{m}"), + } + } +} + /// A parser of messages of Template Distribution subprotocol, to be used for parsing raw messages #[derive(Clone, Debug)] pub enum TemplateDistribution<'a> { @@ -147,6 +162,28 @@ pub enum TemplateDistribution<'a> { SubmitSolution(SubmitSolution<'a>), } +impl fmt::Display for TemplateDistribution<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TemplateDistribution::CoinbaseOutputConstraints(m) => { + write!(f, "CoinbaseOutputConstraints: {m}") + } + TemplateDistribution::NewTemplate(m) => write!(f, "{m}"), + TemplateDistribution::RequestTransactionData(m) => { + write!(f, "{m}") + } + TemplateDistribution::RequestTransactionDataError(m) => { + write!(f, "{m}") + } + TemplateDistribution::RequestTransactionDataSuccess(m) => { + write!(f, "{m}") + } + TemplateDistribution::SetNewPrevHash(m) => write!(f, "{m}"), + TemplateDistribution::SubmitSolution(m) => write!(f, "{m}"), + } + } +} + /// A parser of messages of Job Declaration subprotocol, to be used for parsing raw messages #[derive(Clone, Debug)] pub enum JobDeclaration<'a> { @@ -160,6 +197,29 @@ pub enum JobDeclaration<'a> { PushSolution(PushSolution<'a>), } +impl fmt::Display for JobDeclaration<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + JobDeclaration::AllocateMiningJobToken(m) => write!(f, "AllocateMiningJobToken: {m}"), + JobDeclaration::AllocateMiningJobTokenSuccess(m) => { + write!(f, "AllocateMiningJobTokenSuccess: {m}") + } + JobDeclaration::DeclareMiningJob(m) => write!(f, "DeclareMiningJob: {m}"), + JobDeclaration::DeclareMiningJobError(m) => write!(f, "DeclareMiningJobError: {m}"), + JobDeclaration::DeclareMiningJobSuccess(m) => { + write!(f, "DeclareMiningJobSuccess: {m}") + } + JobDeclaration::ProvideMissingTransactions(m) => { + write!(f, "ProvideMissingTransactions: {m}") + } + JobDeclaration::ProvideMissingTransactionsSuccess(m) => { + write!(f, "ProvideMissingTransactionsSuccess: {m}") + } + JobDeclaration::PushSolution(m) => write!(f, "PushSolution: {m}"), + } + } +} + /// Mining subprotocol messages: categorization, encapsulation, and parsing. /// /// Encapsulates mining-related Sv2 protocol messages, providing both a structured representation @@ -208,6 +268,34 @@ pub enum Mining<'a> { UpdateChannelError(UpdateChannelError<'a>), } +impl fmt::Display for Mining<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Mining::CloseChannel(m) => write!(f, "{m}"), + Mining::NewExtendedMiningJob(m) => write!(f, "{m}"), + Mining::NewMiningJob(m) => write!(f, "{m}"), + Mining::OpenExtendedMiningChannel(m) => write!(f, "{m}"), + Mining::OpenExtendedMiningChannelSuccess(m) => write!(f, "{m}"), + Mining::OpenMiningChannelError(m) => write!(f, "{m}"), + Mining::OpenStandardMiningChannel(m) => write!(f, "{m}"), + Mining::OpenStandardMiningChannelSuccess(m) => write!(f, "{m}"), + Mining::SetCustomMiningJob(m) => write!(f, "{m}"), + Mining::SetCustomMiningJobError(m) => write!(f, "{m}"), + Mining::SetCustomMiningJobSuccess(m) => write!(f, "{m}"), + Mining::SetExtranoncePrefix(m) => write!(f, "{m}"), + Mining::SetGroupChannel(m) => write!(f, "{m}"), + Mining::SetNewPrevHash(m) => write!(f, "{m}"), + Mining::SetTarget(m) => write!(f, "{m}"), + Mining::SubmitSharesError(m) => write!(f, "{m}"), + Mining::SubmitSharesExtended(m) => write!(f, "{m}"), + Mining::SubmitSharesStandard(m) => write!(f, "{m}"), + Mining::SubmitSharesSuccess(m) => write!(f, "{m}"), + Mining::UpdateChannel(m) => write!(f, "{m}"), + Mining::UpdateChannelError(m) => write!(f, "{m}"), + } + } +} + impl Mining<'_> { /// converter into static lifetime pub fn into_static(self) -> Mining<'static> { @@ -1018,6 +1106,17 @@ pub enum AnyMessage<'a> { TemplateDistribution(TemplateDistribution<'a>), } +impl fmt::Display for AnyMessage<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AnyMessage::Common(m) => write!(f, "CommonMessage: {m}"), + AnyMessage::Mining(m) => write!(f, "MiningMessage: {m}"), + AnyMessage::JobDeclaration(m) => write!(f, "JobDeclarationMessage: {m}"), + AnyMessage::TemplateDistribution(m) => write!(f, "TemplateDistributionMessage: {m}"), + } + } +} + impl<'a> TryFrom> for AnyMessage<'a> { type Error = Error; From d0b9584211cb4f4a0bbd72a8e4011edf75c36a8c Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Fri, 4 Jul 2025 16:44:16 -0400 Subject: [PATCH 064/338] replace `{:?}` with `{}` wherever possible on `roles` --- roles/jd-client/src/lib/downstream.rs | 4 ++-- .../src/lib/job_declarator/message_handler.rs | 6 +++--- roles/jd-client/src/lib/job_declarator/mod.rs | 2 +- .../src/lib/template_receiver/message_handler.rs | 6 +++--- roles/jd-client/src/lib/upstream_sv2/upstream.rs | 10 +++++----- .../src/lib/job_declarator/message_handler.rs | 8 ++++---- roles/jd-server/src/lib/job_declarator/mod.rs | 4 ++-- roles/mining-proxy/src/lib/downstream_mining.rs | 4 ++-- roles/mining-proxy/src/lib/upstream_mining.rs | 10 +++++----- .../pool/src/lib/mining_pool/message_handler.rs | 16 ++++++++-------- roles/pool/src/lib/mining_pool/mod.rs | 8 ++++---- .../pool/src/lib/mining_pool/setup_connection.rs | 2 +- .../src/lib/template_receiver/message_handler.rs | 6 +++--- roles/pool/src/lib/template_receiver/mod.rs | 2 +- roles/test-utils/mining-device/src/lib/mod.rs | 8 ++++---- .../translator/src/lib/upstream_sv2/upstream.rs | 12 ++++++------ 16 files changed, 54 insertions(+), 54 deletions(-) diff --git a/roles/jd-client/src/lib/downstream.rs b/roles/jd-client/src/lib/downstream.rs index 8ecf9ea6bd..ccfe27b95b 100644 --- a/roles/jd-client/src/lib/downstream.rs +++ b/roles/jd-client/src/lib/downstream.rs @@ -654,7 +654,7 @@ impl ParseMiningMessagesFromDownstream for DownstreamMiningN std::str::from_utf8(m.user_identity.as_ref()).unwrap_or("Unknown identity"), m.get_request_id_as_u32() ); - debug!("OpenExtendedMiningChannel: {:?}", m); + debug!("OpenExtendedMiningChannel: {}", m); // Check if the downstream is in solo mining mode. if !self.status.is_solo_miner() { @@ -801,7 +801,7 @@ impl ParseMiningMessagesFromDownstream for DownstreamMiningN m: SubmitSharesExtended, ) -> Result, Error> { info!("Received SubmitSharesExtended message"); - debug!("SubmitSharesExtended {:?}", m); + debug!("SubmitSharesExtended {}", m); // Process the submitted share using the channel factory. match self diff --git a/roles/jd-client/src/lib/job_declarator/message_handler.rs b/roles/jd-client/src/lib/job_declarator/message_handler.rs index ce1115e080..907570550d 100644 --- a/roles/jd-client/src/lib/job_declarator/message_handler.rs +++ b/roles/jd-client/src/lib/job_declarator/message_handler.rs @@ -51,7 +51,7 @@ impl ParseJobDeclarationMessagesFromUpstream for JobDeclarator { "Received `DeclareMiningJobSuccess` with id {}", message.request_id ); - debug!("`DeclareMiningJobSuccess`: {:?}", message); + debug!("`DeclareMiningJobSuccess`: {}", message); let message = JobDeclaration::DeclareMiningJobSuccess(message.into_static()); Ok(SendTo::None(Some(message))) } @@ -69,7 +69,7 @@ impl ParseJobDeclarationMessagesFromUpstream for JobDeclarator { "Received `DeclareMiningJobError`, error code: {}", std::str::from_utf8(message.error_code.as_ref()).unwrap_or("unknown error code") ); - debug!("`DeclareMiningJobError`: {:?}", message); + debug!("`DeclareMiningJobError`: {}", message); Ok(SendTo::None(None)) } @@ -96,7 +96,7 @@ impl ParseJobDeclarationMessagesFromUpstream for JobDeclarator { "Received `ProvideMissingTransactions` with id: {}", message.request_id ); - debug!("`ProvideMissingTransactions`: {:?}", message); + debug!("`ProvideMissingTransactions`: {}", message); // Find the corresponding declared job in the window using the request ID. // Extract the full transaction list from the found job's details. diff --git a/roles/jd-client/src/lib/job_declarator/mod.rs b/roles/jd-client/src/lib/job_declarator/mod.rs index 781ebb1527..729d4f420a 100644 --- a/roles/jd-client/src/lib/job_declarator/mod.rs +++ b/roles/jd-client/src/lib/job_declarator/mod.rs @@ -471,7 +471,7 @@ impl JobDeclarator { } } Ok(SendTo::None(Some(JobDeclaration::DeclareMiningJobError(m)))) => { - error!("Job is not verified: {:?}", m); + error!("Job is not verified: {}", m); } Ok(SendTo::None(None)) => (), Ok(SendTo::Respond(m)) => { diff --git a/roles/jd-client/src/lib/template_receiver/message_handler.rs b/roles/jd-client/src/lib/template_receiver/message_handler.rs index 1665069a34..50780b8e9b 100644 --- a/roles/jd-client/src/lib/template_receiver/message_handler.rs +++ b/roles/jd-client/src/lib/template_receiver/message_handler.rs @@ -24,7 +24,7 @@ impl ParseTemplateDistributionMessagesFromServer for TemplateRx { "Received NewTemplate with id: {}, is future: {}", m.template_id, m.future_template ); - debug!("NewTemplate: {:?}", m); + debug!("NewTemplate: {}", m); let new_template = m.into_static(); let new_template = TemplateDistribution::NewTemplate(new_template); Ok(SendTo::None(Some(new_template))) @@ -37,7 +37,7 @@ impl ParseTemplateDistributionMessagesFromServer for TemplateRx { // other components like the `JobDeclarator` and downstream. fn handle_set_new_prev_hash(&mut self, m: SetNewPrevHash) -> Result { info!("Received SetNewPrevHash for template: {}", m.template_id); - debug!("SetNewPrevHash: {:?}", m); + debug!("SetNewPrevHash: {}", m); let new_prev_hash = SetNewPrevHash { template_id: m.template_id, prev_hash: m.prev_hash.into_static(), @@ -63,7 +63,7 @@ impl ParseTemplateDistributionMessagesFromServer for TemplateRx { "Received RequestTransactionDataSuccess for template: {}", m.template_id ); - debug!("RequestTransactionDataSuccess: {:?}", m); + debug!("RequestTransactionDataSuccess: {}", m); let m = RequestTransactionDataSuccess { transaction_list: m.transaction_list.into_static(), excess_data: m.excess_data.into_static(), diff --git a/roles/jd-client/src/lib/upstream_sv2/upstream.rs b/roles/jd-client/src/lib/upstream_sv2/upstream.rs index 956d1f4b42..e8234e1743 100644 --- a/roles/jd-client/src/lib/upstream_sv2/upstream.rs +++ b/roles/jd-client/src/lib/upstream_sv2/upstream.rs @@ -673,7 +673,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { "Received OpenExtendedMiningChannelSuccess with request id: {} and channel id: {}", m.request_id, m.channel_id ); - debug!("OpenStandardMiningChannelSuccess: {:?}", m); + debug!("OpenStandardMiningChannelSuccess: {}", m); // --- Create the PoolChannelFactory --- let ids = Arc::new(Mutex::new(roles_logic_sv2::utils::GroupId::new())); let jdc_signature_len = self.jdc_signature.len(); @@ -798,7 +798,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { "Received SetExtranoncePrefix for channel id: {}", m.channel_id ); - debug!("SetExtranoncePrefix: {:?}", m); + debug!("SetExtranoncePrefix: {}", m); Ok(SendTo::RelaySameMessageToRemote( self.downstream.as_ref().unwrap().clone(), )) @@ -813,7 +813,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { m: roles_logic_sv2::mining_sv2::SubmitSharesSuccess, ) -> Result, RolesLogicError> { info!("Received SubmitSharesSuccess"); - debug!("SubmitSharesSuccess: {:?}", m); + debug!("SubmitSharesSuccess: {}", m); Ok(SendTo::RelaySameMessageToRemote( self.downstream.as_ref().unwrap().clone(), )) @@ -904,7 +904,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { "Received SetCustomMiningJobSuccess for channel id: {} for job id: {}", m.channel_id, m.job_id ); - debug!("SetCustomMiningJobSuccess: {:?}", m); + debug!("SetCustomMiningJobSuccess: {}", m); if let Some(template_id) = self.template_to_job_id.take_template_id(m.request_id) { self.template_to_job_id .register_job_id(template_id, m.job_id); @@ -937,7 +937,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { m: roles_logic_sv2::mining_sv2::SetTarget, ) -> Result, RolesLogicError> { info!("Received SetTarget for channel id: {}", m.channel_id); - debug!("SetTarget: {:?}", m); + debug!("SetTarget: {}", m); if let Some(factory) = self.channel_factory.as_mut() { factory.update_target_for_channel(m.channel_id, m.maximum_target.clone().into()); factory.set_target(&mut m.maximum_target.clone().into()); diff --git a/roles/jd-server/src/lib/job_declarator/message_handler.rs b/roles/jd-server/src/lib/job_declarator/message_handler.rs index 0ed61ba36f..5f930086ce 100644 --- a/roles/jd-server/src/lib/job_declarator/message_handler.rs +++ b/roles/jd-server/src/lib/job_declarator/message_handler.rs @@ -63,7 +63,7 @@ impl ParseJobDeclarationMessagesFromDownstream for JobDeclaratorDownstream { }; let message_enum = JobDeclaration::AllocateMiningJobTokenSuccess(message_success); info!( - "Sending AllocateMiningJobTokenSuccess to proxy {:?}", + "Sending AllocateMiningJobTokenSuccess to proxy {}", message_enum ); Ok(SendTo::Respond(message_enum)) @@ -78,7 +78,7 @@ impl ParseJobDeclarationMessagesFromDownstream for JobDeclaratorDownstream { "Received `DeclareMiningJob` with id: {}", message.request_id ); - debug!("`DeclareMiningJob`: {:?}", message); + debug!("`DeclareMiningJob`: {}", message); if let Some(old_mining_job) = self.declared_mining_job.0.take() { clear_declared_mining_job(old_mining_job, &message, self.mempool.clone())?; } @@ -156,7 +156,7 @@ impl ParseJobDeclarationMessagesFromDownstream for JobDeclaratorDownstream { "Received `ProvideMissingTransactionsSuccess` with id: {}", message.request_id ); - debug!("`ProvideMissingTransactionsSuccess`: {:?}", message); + debug!("`ProvideMissingTransactionsSuccess`: {}", message); let (declared_mining_job, ref mut transactions_with_state, missing_indexes) = &mut self.declared_mining_job; let mut unknown_transactions: Vec = vec![]; @@ -223,7 +223,7 @@ impl ParseJobDeclarationMessagesFromDownstream for JobDeclaratorDownstream { fn handle_push_solution(&mut self, message: PushSolution<'_>) -> Result { info!("Received PushSolution from JDC"); - debug!("`PushSolution`: {:?}", message); + debug!("`PushSolution`: {}", message); let m = JobDeclaration::PushSolution(message.clone().into_static()); Ok(SendTo::None(Some(m))) } diff --git a/roles/jd-server/src/lib/job_declarator/mod.rs b/roles/jd-server/src/lib/job_declarator/mod.rs index 64a23a572b..ec9d61cad9 100644 --- a/roles/jd-server/src/lib/job_declarator/mod.rs +++ b/roles/jd-server/src/lib/job_declarator/mod.rs @@ -320,11 +320,11 @@ impl JobDeclaratorDownstream { Self::send(self_mutex.clone(), m).await.unwrap(); } Ok(SendTo::RelayNewMessage(message)) => { - error!("JD Server: unexpected relay new message {:?}", message); + error!("JD Server: unexpected relay new message {}", message); } Ok(SendTo::RelayNewMessageToRemote(remote, message)) => { error!( - "JD Server: unexpected relay new message to remote. Remote: {:?}, Message: {:?}", + "JD Server: unexpected relay new message to remote. Remote: {:?}, Message: {}", remote, message ); diff --git a/roles/mining-proxy/src/lib/downstream_mining.rs b/roles/mining-proxy/src/lib/downstream_mining.rs index 77517792a4..9940aa878a 100644 --- a/roles/mining-proxy/src/lib/downstream_mining.rs +++ b/roles/mining-proxy/src/lib/downstream_mining.rs @@ -315,7 +315,7 @@ impl ParseMiningMessagesFromDownstream for DownstreamMiningN std::str::from_utf8(req.user_identity.as_ref()).unwrap_or("Unknown identity"), req.get_request_id_as_u32() ); - debug!("OpenStandardMiningChannel: {:?}", req); + debug!("OpenStandardMiningChannel: {}", req); let downstream_mining_data = self.get_downstream_mining_data(); let routing_logic = super::get_routing_logic(); @@ -392,7 +392,7 @@ impl ParseMiningMessagesFromDownstream for DownstreamMiningN m: SubmitSharesStandard, ) -> Result, Error> { info!("Received SubmitSharesStandard"); - debug!("SubmitSharesStandard {:?}", m); + debug!("SubmitSharesStandard {}", m); // TODO maybe we want to check if shares meet target before // sending them upstream If that is the case it should be // done by GroupChannel not here diff --git a/roles/mining-proxy/src/lib/upstream_mining.rs b/roles/mining-proxy/src/lib/upstream_mining.rs index 13ccb1249d..dd67f3e483 100644 --- a/roles/mining-proxy/src/lib/upstream_mining.rs +++ b/roles/mining-proxy/src/lib/upstream_mining.rs @@ -949,7 +949,7 @@ impl ParseMiningMessagesFromUpstream for UpstreamMiningNod "Received OpenExtendedMiningChannelSuccess with request id: {} and channel id: {}", m.request_id, m.channel_id ); - debug!("OpenStandardMiningChannelSuccess: {:?}", m); + debug!("OpenStandardMiningChannelSuccess: {}", m); let extranonce_prefix: Extranonce = m.extranonce_prefix.clone().into(); let range_0 = 0..m.extranonce_prefix.clone().to_vec().len(); let range_1 = range_0.end..(range_0.end + EXTRANONCE_RANGE_1_LENGTH); @@ -1005,7 +1005,7 @@ impl ParseMiningMessagesFromUpstream for UpstreamMiningNod m: SubmitSharesSuccess, ) -> Result, Error> { info!("Received SubmitSharesSuccess"); - debug!("SubmitSharesSuccess: {:?}", m); + debug!("SubmitSharesSuccess: {}", m); match &self .downstream_selector .downstream_from_channel_id(m.channel_id) @@ -1065,7 +1065,7 @@ impl ParseMiningMessagesFromUpstream for UpstreamMiningNod m.job_id, m.is_future() ); - debug!("NewExtendedMiningJob: {:?}", m); + debug!("NewExtendedMiningJob: {}", m); let mut res = vec![]; match &mut self.channel_kind { ChannelKind::Group(group) => { @@ -1155,7 +1155,7 @@ impl ParseMiningMessagesFromUpstream for UpstreamMiningNod "Received SetNewPrevHash channel id: {}, job id: {}", m.channel_id, m.job_id ); - debug!("SetNewPrevHash: {:?}", m); + debug!("SetNewPrevHash: {}", m); match &mut self.channel_kind { ChannelKind::Group(group) => { group.update_new_prev_hash(&m); @@ -1195,7 +1195,7 @@ impl ParseMiningMessagesFromUpstream for UpstreamMiningNod "Received SetCustomMiningJobSuccess for channel id: {} for job id: {}", m.channel_id, m.job_id ); - debug!("SetCustomMiningJobSuccess: {:?}", m); + debug!("SetCustomMiningJobSuccess: {}", m); Ok(SendTo::None(None)) } diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index c3bc787f16..cefa73d0f4 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -71,7 +71,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { .map(|s| s.to_string()) .map_err(|e| Error::InvalidUserIdentity(e.to_string()))?; - info!("Received OpenStandardMiningChannel: {:?}", incoming); + info!("Received OpenStandardMiningChannel: {}", incoming); let nominal_hash_rate = incoming.nominal_hash_rate; let requested_max_target = incoming.max_target.into_static(); @@ -240,7 +240,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { .map(|s| s.to_string()) .map_err(|e| Error::InvalidUserIdentity(e.to_string()))?; - info!("Received OpenExtendedMiningChannel: {:?}", m); + info!("Received OpenExtendedMiningChannel: {}", m); let nominal_hash_rate = m.nominal_hash_rate; let requested_max_target = m.max_target.into_static(); @@ -418,7 +418,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { // target difficulty. // - `Err(Error)` - If calculating the target fails or the channel factory interaction fails. fn handle_update_channel(&mut self, m: UpdateChannel) -> Result, Error> { - info!("Received UpdateChannel message: {:?}", m); + info!("Received UpdateChannel message: {}", m); let channel_id = m.channel_id; let new_nominal_hash_rate = m.nominal_hash_rate; @@ -560,7 +560,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { &mut self, m: SubmitSharesStandard, ) -> Result, Error> { - info!("Received: {:?}", m); + info!("Received: {}", m); let channel_id = m.channel_id; if !self.standard_channels.contains_key(&channel_id) { @@ -613,7 +613,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { new_submits_accepted_count, new_shares_sum, }; - info!("SubmitSharesStandard: {:?} ✅", success); + info!("SubmitSharesStandard: {} ✅", success); Ok(SendTo::Respond(Mining::SubmitSharesSuccess(success))) } Ok(ShareValidationResult::BlockFound(template_id, coinbase)) => { @@ -722,7 +722,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { &mut self, m: SubmitSharesExtended, ) -> Result, Error> { - info!("Received: {:?}", m); + info!("Received: {}", m); let channel_id = m.channel_id; if !self.extended_channels.contains_key(&channel_id) { @@ -773,7 +773,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { new_submits_accepted_count, new_shares_sum, }; - info!("SubmitSharesExtended: {:?} ✅", success); + info!("SubmitSharesExtended: {} ✅", success); Ok(SendTo::Respond(Mining::SubmitSharesSuccess(success))) } Ok(ShareValidationResult::BlockFound(template_id, coinbase)) => { @@ -879,7 +879,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { // custom job setup. // - `Err(Error)` - If the channel factory interaction fails. fn handle_set_custom_mining_job(&mut self, m: SetCustomMiningJob) -> Result, Error> { - info!("Received SetCustomMiningJob: {:?}", m); + info!("Received SetCustomMiningJob: {}", m); // this is a naive implementation, but ideally we should check the SetCustomMiningJob // message parameters, especially: diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 1bacacca00..1db0bd1e2c 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -380,7 +380,7 @@ impl Downstream { ) -> PoolResult<()> { match send_to { Ok(SendTo::Respond(message)) => { - debug!("Sending to downstream: {:?}", message); + debug!("Sending to downstream: {}", message); // returning an error will send the error to the main thread, // and the main thread will drop the downstream from the pool if let &Mining::OpenMiningChannelError(_) = &message { @@ -591,7 +591,7 @@ impl Pool { .safe_lock(|s| s.status_tx.clone()) .map_err(|e| PoolError::PoisonLock(e.to_string()))?; while let Ok(new_prev_hash) = rx.recv().await { - debug!("New prev hash received: {:?}", new_prev_hash); + debug!("New prev hash received: {}", new_prev_hash); let res = self_ .safe_lock(|s| { s.last_new_prev_hash = Some(new_prev_hash.clone()); @@ -724,7 +724,7 @@ impl Pool { let status_tx = self_.safe_lock(|s| s.status_tx.clone())?; while let Ok(new_template) = rx.recv().await { info!( - "New template received, creating a new mining job(s): {:?}", + "New template received, creating a new mining job(s): {}", new_template ); @@ -1151,7 +1151,7 @@ async fn send_set_target_downstream( let mining_msg = Mining::SetTarget(target_message); - info!("Sending SetTarget message to downstream: {:?}", mining_msg); + info!("Sending SetTarget message to downstream: {}", mining_msg); let sv2_frame: StdFrame = AnyMessage::Mining(mining_msg).try_into()?; diff --git a/roles/pool/src/lib/mining_pool/setup_connection.rs b/roles/pool/src/lib/mining_pool/setup_connection.rs index 51ba8fb4c7..1682bb299c 100644 --- a/roles/pool/src/lib/mining_pool/setup_connection.rs +++ b/roles/pool/src/lib/mining_pool/setup_connection.rs @@ -91,7 +91,7 @@ impl SetupConnectionHandler { match message { CommonMessages::SetupConnectionSuccess(m) => { - debug!("Sent back SetupConnectionSuccess: {:?}", m); + debug!("Sent back SetupConnectionSuccess: {}", m); Ok(CommonDownstreamData { header_only: has_requires_std_job(m.flags), work_selection: has_work_selection(m.flags), diff --git a/roles/pool/src/lib/template_receiver/message_handler.rs b/roles/pool/src/lib/template_receiver/message_handler.rs index ab722a248c..e3a326ae72 100644 --- a/roles/pool/src/lib/template_receiver/message_handler.rs +++ b/roles/pool/src/lib/template_receiver/message_handler.rs @@ -20,7 +20,7 @@ impl ParseTemplateDistributionMessagesFromServer for TemplateRx { "Received NewTemplate with id: {}, is future: {}", m.template_id, m.future_template ); - debug!("NewTemplate: {:?}", m); + debug!("NewTemplate: {}", m); let new_template = TemplateDistribution::NewTemplate(m.into_static()); Ok(SendTo::RelayNewMessageToRemote( Arc::new(Mutex::new(())), @@ -31,7 +31,7 @@ impl ParseTemplateDistributionMessagesFromServer for TemplateRx { // Handles a `SetNewPrevHash` and return `RelayNewMessageToRemote` fn handle_set_new_prev_hash(&mut self, m: SetNewPrevHash) -> Result { info!("Received SetNewPrevHash for template: {}", m.template_id); - debug!("SetNewPrevHash: {:?}", m); + debug!("SetNewPrevHash: {}", m); let new_prev_hash = TemplateDistribution::SetNewPrevHash(m.into_static()); Ok(SendTo::RelayNewMessageToRemote( Arc::new(Mutex::new(())), @@ -52,7 +52,7 @@ impl ParseTemplateDistributionMessagesFromServer for TemplateRx { "Received RequestTransactionDataSuccess for template: {}", m.template_id ); - debug!("RequestTransactionDataSuccess: {:?}", m); + debug!("RequestTransactionDataSuccess: {}", m); // Just ignore tx data messages this are meant for the declarators Ok(SendTo::None(None)) } diff --git a/roles/pool/src/lib/template_receiver/mod.rs b/roles/pool/src/lib/template_receiver/mod.rs index c8b6f9234d..34d2a20e02 100644 --- a/roles/pool/src/lib/template_receiver/mod.rs +++ b/roles/pool/src/lib/template_receiver/mod.rs @@ -221,7 +221,7 @@ impl TemplateRx { async fn on_new_solution(self_: Arc>, rx: Receiver>) { let status_tx = self_.safe_lock(|s| s.status_tx.clone()).unwrap(); while let Ok(solution) = rx.recv().await { - info!("Sending Solution to TP: {:?}", &solution); + info!("Sending Solution to TP: {}", &solution); let sv2_frame_res: Result = AnyMessage::TemplateDistribution(TemplateDistribution::SubmitSolution(solution)) .try_into(); diff --git a/roles/test-utils/mining-device/src/lib/mod.rs b/roles/test-utils/mining-device/src/lib/mod.rs index 0800093fd9..478dece20a 100644 --- a/roles/test-utils/mining-device/src/lib/mod.rs +++ b/roles/test-utils/mining-device/src/lib/mod.rs @@ -410,7 +410,7 @@ impl ParseMiningMessagesFromUpstream<()> for Device { m: SubmitSharesSuccess, ) -> Result, Error> { info!("Received SubmitSharesSuccess"); - debug!("SubmitSharesSuccess: {:?}", m); + debug!("SubmitSharesSuccess: {}", m); Ok(SendTo::None(None)) } @@ -429,7 +429,7 @@ impl ParseMiningMessagesFromUpstream<()> for Device { m.job_id, m.is_future() ); - debug!("NewMiningJob: {:?}", m); + debug!("NewMiningJob: {}", m); match (m.is_future(), self.prev_hash.as_ref()) { (false, Some(p_h)) => { self.miner @@ -458,7 +458,7 @@ impl ParseMiningMessagesFromUpstream<()> for Device { "Received SetNewPrevHash channel id: {}, job id: {}", m.channel_id, m.job_id ); - debug!("SetNewPrevHash: {:?}", m); + debug!("SetNewPrevHash: {}", m); let jobs: Vec<&NewMiningJob<'static>> = self .jobs .iter() @@ -497,7 +497,7 @@ impl ParseMiningMessagesFromUpstream<()> for Device { fn handle_set_target(&mut self, m: SetTarget) -> Result, Error> { info!("Received SetTarget for channel id: {}", m.channel_id); - debug!("SetTarget: {:?}", m); + debug!("SetTarget: {}", m); self.miner .safe_lock(|miner| miner.new_target(m.maximum_target.to_vec())) .unwrap(); diff --git a/roles/translator/src/lib/upstream_sv2/upstream.rs b/roles/translator/src/lib/upstream_sv2/upstream.rs index 07faea55ce..543578ef37 100644 --- a/roles/translator/src/lib/upstream_sv2/upstream.rs +++ b/roles/translator/src/lib/upstream_sv2/upstream.rs @@ -729,7 +729,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { "Received OpenExtendedMiningChannelSuccess with request id: {} and channel id: {}", m.request_id, m.channel_id ); - debug!("OpenStandardMiningChannelSuccess: {:?}", m); + debug!("OpenStandardMiningChannelSuccess: {}", m); let tproxy_e1_len = super::super::utils::proxy_extranonce1_len( m.extranonce_size as usize, self.min_extranonce_size.into(), @@ -800,7 +800,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { m: roles_logic_sv2::mining_sv2::SubmitSharesSuccess, ) -> Result, RolesLogicError> { info!("Received SubmitSharesSuccess"); - debug!("SubmitSharesSuccess: {:?}", m); + debug!("SubmitSharesSuccess: {}", m); Ok(SendTo::None(None)) } @@ -839,7 +839,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { m.job_id, m.is_future() ); - debug!("NewExtendedMiningJob: {:?}", m); + debug!("NewExtendedMiningJob: {}", m); if self.is_work_selection_enabled() { Ok(SendTo::None(None)) } else { @@ -866,7 +866,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { "Received SetNewPrevHash channel id: {}, job id: {}", m.channel_id, m.job_id ); - debug!("SetNewPrevHash: {:?}", m); + debug!("SetNewPrevHash: {}", m); if self.is_work_selection_enabled() { Ok(SendTo::None(None)) } else { @@ -884,7 +884,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { "Received SetCustomMiningJobSuccess for channel id: {} for job id: {}", m.channel_id, m.job_id ); - debug!("SetCustomMiningJobSuccess: {:?}", m); + debug!("SetCustomMiningJobSuccess: {}", m); self.last_job_id = Some(m.job_id); Ok(SendTo::None(None)) } @@ -904,7 +904,7 @@ impl ParseMiningMessagesFromUpstream for Upstream { m: roles_logic_sv2::mining_sv2::SetTarget, ) -> Result, RolesLogicError> { info!("Received SetTarget for channel id: {}", m.channel_id); - debug!("SetTarget: {:?}", m); + debug!("SetTarget: {}", m); let m = m.into_static(); self.target.safe_lock(|t| *t = m.maximum_target.to_vec())?; Ok(SendTo::None(None)) From dbd45d795b4a0403eca0f5b7e98890af33c1d4ff Mon Sep 17 00:00:00 2001 From: plebhash Date: Thu, 3 Jul 2025 19:52:09 -0300 Subject: [PATCH 065/338] add static lifetime converters to roles_logic_sv2 parsers module --- protocols/v2/roles-logic-sv2/src/parsers.rs | 88 +++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/protocols/v2/roles-logic-sv2/src/parsers.rs b/protocols/v2/roles-logic-sv2/src/parsers.rs index 482137d069..dc1dc84322 100644 --- a/protocols/v2/roles-logic-sv2/src/parsers.rs +++ b/protocols/v2/roles-logic-sv2/src/parsers.rs @@ -335,6 +335,94 @@ impl Mining<'_> { } } +impl CommonMessages<'_> { + /// converter into static lifetime + pub fn into_static(self) -> CommonMessages<'static> { + match self { + CommonMessages::ChannelEndpointChanged(m) => CommonMessages::ChannelEndpointChanged(m), + CommonMessages::Reconnect(m) => CommonMessages::Reconnect(m.into_static()), + CommonMessages::SetupConnection(m) => CommonMessages::SetupConnection(m.into_static()), + CommonMessages::SetupConnectionError(m) => { + CommonMessages::SetupConnectionError(m.into_static()) + } + CommonMessages::SetupConnectionSuccess(m) => CommonMessages::SetupConnectionSuccess(m), + } + } +} + +impl TemplateDistribution<'_> { + /// converter into static lifetime + pub fn into_static(self) -> TemplateDistribution<'static> { + match self { + TemplateDistribution::CoinbaseOutputConstraints(m) => { + TemplateDistribution::CoinbaseOutputConstraints(m) + } + TemplateDistribution::NewTemplate(m) => { + TemplateDistribution::NewTemplate(m.into_static()) + } + TemplateDistribution::RequestTransactionData(m) => { + TemplateDistribution::RequestTransactionData(m) + } + TemplateDistribution::RequestTransactionDataError(m) => { + TemplateDistribution::RequestTransactionDataError(m.into_static()) + } + TemplateDistribution::RequestTransactionDataSuccess(m) => { + TemplateDistribution::RequestTransactionDataSuccess(m.into_static()) + } + TemplateDistribution::SetNewPrevHash(m) => { + TemplateDistribution::SetNewPrevHash(m.into_static()) + } + TemplateDistribution::SubmitSolution(m) => { + TemplateDistribution::SubmitSolution(m.into_static()) + } + } + } +} + +impl JobDeclaration<'_> { + /// converter into static lifetime + pub fn into_static(self) -> JobDeclaration<'static> { + match self { + JobDeclaration::AllocateMiningJobToken(m) => { + JobDeclaration::AllocateMiningJobToken(m.into_static()) + } + JobDeclaration::AllocateMiningJobTokenSuccess(m) => { + JobDeclaration::AllocateMiningJobTokenSuccess(m.into_static()) + } + JobDeclaration::DeclareMiningJob(m) => { + JobDeclaration::DeclareMiningJob(m.into_static()) + } + JobDeclaration::DeclareMiningJobError(m) => { + JobDeclaration::DeclareMiningJobError(m.into_static()) + } + JobDeclaration::DeclareMiningJobSuccess(m) => { + JobDeclaration::DeclareMiningJobSuccess(m.into_static()) + } + JobDeclaration::ProvideMissingTransactions(m) => { + JobDeclaration::ProvideMissingTransactions(m.into_static()) + } + JobDeclaration::ProvideMissingTransactionsSuccess(m) => { + JobDeclaration::ProvideMissingTransactionsSuccess(m.into_static()) + } + JobDeclaration::PushSolution(m) => JobDeclaration::PushSolution(m.into_static()), + } + } +} + +impl AnyMessage<'_> { + /// converter into static lifetime + pub fn into_static(self) -> AnyMessage<'static> { + match self { + AnyMessage::Common(m) => AnyMessage::Common(m.into_static()), + AnyMessage::Mining(m) => AnyMessage::Mining(m.into_static()), + AnyMessage::JobDeclaration(m) => AnyMessage::JobDeclaration(m.into_static()), + AnyMessage::TemplateDistribution(m) => { + AnyMessage::TemplateDistribution(m.into_static()) + } + } + } +} + /// A trait that every Sv2 message parser must implement. /// It helps parsing from Rust types to raw messages. pub trait IsSv2Message { From 460fb1f8e6c78ca0314edf54b2fb0078c3f30263 Mon Sep 17 00:00:00 2001 From: plebhash Date: Fri, 4 Jul 2025 15:18:23 -0300 Subject: [PATCH 066/338] fix unnecessary creation of group channels on pool --- .../src/lib/mining_pool/message_handler.rs | 37 ++++++++++++++----- roles/pool/src/lib/mining_pool/mod.rs | 30 ++------------- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index cefa73d0f4..027ea0c24d 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -15,6 +15,7 @@ use stratum_common::roles_logic_sv2::{ channels::server::{ error::{ExtendedChannelError, StandardChannelError}, extended::ExtendedChannel, + group::GroupChannel, jobs::job_store::DefaultJobStore, share_accounting::{ShareValidationError, ShareValidationResult}, standard::StandardChannel, @@ -73,6 +74,33 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { info!("Received OpenStandardMiningChannel: {}", incoming); + let last_future_template = self.last_future_template.clone(); + let last_set_new_prev_hash_tdp = self.last_new_prev_hash.clone(); + + // note: the fact that we're parsing a Vec from the config file is a bit of a hack + // so while we don't clean that up, we only set the value of the first output + let mut pool_coinbase_outputs = self.empty_pool_coinbase_outputs.clone(); + pool_coinbase_outputs[0].value = + Amount::from_sat(last_future_template.coinbase_tx_value_remaining); + + if !self.downstream_data.header_only && self.group_channel.is_none() { + // we only create one group channel for all standard channels + + let group_channel_id = self.channel_id_factory.next(); + let job_store = Box::new(DefaultJobStore::new()); + + let mut group_channel = GroupChannel::new(group_channel_id, job_store); + group_channel + .on_new_template(last_future_template.clone(), pool_coinbase_outputs.clone()) + .map_err(Error::FailedToProcessNewTemplateGroupChannel)?; + + group_channel + .on_set_new_prev_hash(last_set_new_prev_hash_tdp.clone()) + .map_err(Error::FailedToProcessSetNewPrevHashGroupChannel)?; + + self.group_channel = Some(Arc::new(RwLock::new(group_channel))); + } + let nominal_hash_rate = incoming.nominal_hash_rate; let requested_max_target = incoming.max_target.into_static(); @@ -158,14 +186,6 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { open_standard_mining_channel_success, )); - let last_future_template = self.last_future_template.clone(); - - // note: the fact that we're parsing a Vec from the config file is a bit of a hack - // so while we don't clean that up, we only set the value of the first output - let mut pool_coinbase_outputs = self.empty_pool_coinbase_outputs.clone(); - pool_coinbase_outputs[0].value = - Amount::from_sat(last_future_template.coinbase_tx_value_remaining); - // create a future standard job based on the last future template standard_channel .on_new_template(last_future_template.clone(), pool_coinbase_outputs) @@ -185,7 +205,6 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { messages.push(Mining::NewMiningJob(future_standard_job_message)); // SetNewPrevHash message activates the future job - let last_set_new_prev_hash_tdp = self.last_new_prev_hash.clone(); let prev_hash = last_set_new_prev_hash_tdp.prev_hash.clone(); let header_timestamp = last_set_new_prev_hash_tdp.header_timestamp; let n_bits = last_set_new_prev_hash_tdp.n_bits; diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 1db0bd1e2c..0c78545ed2 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -44,8 +44,7 @@ use stratum_common::{ self, bitcoin::{Amount, ScriptBuf, TxOut}, channels::server::{ - extended::ExtendedChannel, group::GroupChannel, jobs::job_store::DefaultJobStore, - standard::StandardChannel, + extended::ExtendedChannel, group::GroupChannel, standard::StandardChannel, }, codec_sv2::{ self, binary_sv2::U256, HandshakeRole, Responder, StandardEitherFrame, StandardSv2Frame, @@ -194,7 +193,7 @@ impl Downstream { let id = pool.safe_lock(|p| p.downstream_id_factory.next())?; - let mut channel_id_factory = IdFactory::new(); + let channel_id_factory = IdFactory::new(); // extranonce prefix factories are shared across all downstreams // that avoids extranonce_prefix collision across different downstreams @@ -235,29 +234,6 @@ impl Downstream { pool_coinbase_outputs[0].value = Amount::from_sat(last_future_template.coinbase_tx_value_remaining); - let group_channel = if !downstream_data.header_only { - // naive approach: - // we create one group channel for the entire connection - // and add all standard channels to this same single group channel - // we know this will result in group_channel_id == 1 - // so we use that for every standard channel - let group_channel_id = channel_id_factory.next(); - let job_store = Box::new(DefaultJobStore::new()); - let mut group_channel = GroupChannel::new(group_channel_id, job_store); - - group_channel - .on_new_template(last_future_template.clone(), pool_coinbase_outputs) - .map_err(Error::FailedToProcessNewTemplateGroupChannel)?; - - group_channel - .on_set_new_prev_hash(last_new_prev_hash.clone()) - .map_err(Error::FailedToProcessSetNewPrevHashGroupChannel)?; - - Some(Arc::new(RwLock::new(group_channel))) - } else { - None - }; - // Create the Downstream instance, wrapped for shared access. let self_ = Arc::new(Mutex::new(Downstream { id, @@ -269,7 +245,7 @@ impl Downstream { extended_channels: HashMap::new(), standard_channels: HashMap::new(), vardiff: HashMap::new(), - group_channel, + group_channel: None, extranonce_prefix_factory_extended, extranonce_prefix_factory_standard, share_batch_size, From 9633259824ad0aa3f8c31e23815e5874d1760b27 Mon Sep 17 00:00:00 2001 From: plebhash Date: Mon, 30 Jun 2025 17:55:43 -0300 Subject: [PATCH 067/338] re-introduce function to deserialize template outputs as the outcome of https://github.com/Sjors/bitcoin/issues/92 --- .../src/channels/server/extended.rs | 1 - .../src/channels/server/jobs/extended.rs | 24 +++------- .../src/channels/server/jobs/factory.rs | 23 ++-------- .../src/channels/server/jobs/standard.rs | 28 ++++-------- protocols/v2/roles-logic-sv2/src/utils.rs | 44 +++++++++++++++++++ roles/jd-client/src/lib/job_declarator/mod.rs | 44 +++---------------- 6 files changed, 68 insertions(+), 96 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs index e63678ef3c..574a342600 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs @@ -566,7 +566,6 @@ impl<'a> ExtendedChannel<'a> { mod tests { use crate::channels::{ chain_tip::ChainTip, - client::extended::ExtendedJob, server::{ error::ExtendedChannelError, extended::ExtendedChannel, diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs index 2b548d5020..d3e43b2fd3 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs @@ -5,9 +5,10 @@ use crate::{ server::jobs::{error::ExtendedJobError, JobOrigin}, }, template_distribution_sv2::NewTemplate, + utils::deserialize_template_outputs, }; use bitcoin::{ - consensus::{deserialize, serialize, Decodable}, + consensus::{deserialize, serialize}, transaction::{Transaction, TxOut}, }; use codec_sv2::binary_sv2::{Seq0255, Sv2Option, B0255, B064K, U256}; @@ -47,27 +48,12 @@ impl<'a> ExtendedJob<'a> { additional_coinbase_outputs: Vec, job_message: NewExtendedMiningJob<'a>, ) -> Result { - let mut template_coinbase_outputs = Vec::::consensus_decode( - &mut template - .coinbase_tx_outputs - .inner_as_ref() - .to_vec() - .as_slice(), + let template_coinbase_outputs = deserialize_template_outputs( + template.coinbase_tx_outputs.to_vec(), + template.coinbase_tx_outputs_count, ) .map_err(|_| ExtendedJobError::FailedToDeserializeCoinbaseOutputs)?; - // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 - if template_coinbase_outputs.is_empty() { - template_coinbase_outputs = vec![TxOut::consensus_decode( - &mut template - .coinbase_tx_outputs - .inner_as_ref() - .to_vec() - .as_slice(), - ) - .map_err(|_| ExtendedJobError::FailedToDeserializeCoinbaseOutputs)?]; - } - let mut coinbase_outputs = vec![]; coinbase_outputs.extend(additional_coinbase_outputs); coinbase_outputs.extend(template_coinbase_outputs); diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs index 062dc699b3..59acb0d115 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs @@ -5,7 +5,7 @@ use crate::{ server::jobs::{error::*, extended::ExtendedJob, standard::StandardJob}, }, template_distribution_sv2::NewTemplate, - utils::{merkle_root_from_path, Id as JobIdFactory}, + utils::{deserialize_template_outputs, merkle_root_from_path, Id as JobIdFactory}, }; use bitcoin::{ absolute::LockTime, @@ -327,27 +327,12 @@ impl JobFactory { outputs.push(output.clone()); } - let mut template_outputs = Vec::::consensus_decode( - &mut template - .coinbase_tx_outputs - .inner_as_ref() - .to_vec() - .as_slice(), + let mut template_outputs = deserialize_template_outputs( + template.coinbase_tx_outputs.to_vec(), + template.coinbase_tx_outputs_count, ) .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?; - // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 - if template_outputs.is_empty() { - template_outputs = vec![TxOut::consensus_decode( - &mut template - .coinbase_tx_outputs - .inner_as_ref() - .to_vec() - .as_slice(), - ) - .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?]; - } - outputs.append(&mut template_outputs); let mut script_sig = vec![]; diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs index 0982c445c1..40930217e7 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs +++ b/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs @@ -1,5 +1,8 @@ -use crate::channels::server::jobs::{error::StandardJobError, Job}; -use bitcoin::{consensus::Decodable, transaction::TxOut}; +use crate::{ + channels::server::jobs::{error::StandardJobError, Job}, + utils::deserialize_template_outputs, +}; +use bitcoin::transaction::TxOut; use codec_sv2::binary_sv2::{Sv2Option, U256}; use mining_sv2::NewMiningJob; use template_distribution_sv2::NewTemplate; @@ -34,27 +37,12 @@ impl<'a> StandardJob<'a> { additional_coinbase_outputs: Vec, job_message: NewMiningJob<'a>, ) -> Result { - let mut template_coinbase_outputs = Vec::::consensus_decode( - &mut template - .coinbase_tx_outputs - .inner_as_ref() - .to_vec() - .as_slice(), + let template_coinbase_outputs = deserialize_template_outputs( + template.coinbase_tx_outputs.to_vec(), + template.coinbase_tx_outputs_count, ) .map_err(|_| StandardJobError::FailedToDeserializeCoinbaseOutputs)?; - // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 - if template_coinbase_outputs.is_empty() { - template_coinbase_outputs = vec![TxOut::consensus_decode( - &mut template - .coinbase_tx_outputs - .inner_as_ref() - .to_vec() - .as_slice(), - ) - .map_err(|_| StandardJobError::FailedToDeserializeCoinbaseOutputs)?]; - } - let mut coinbase_outputs = vec![]; coinbase_outputs.extend(additional_coinbase_outputs); coinbase_outputs.extend(template_coinbase_outputs); diff --git a/protocols/v2/roles-logic-sv2/src/utils.rs b/protocols/v2/roles-logic-sv2/src/utils.rs index 2f191c67e9..654e935dc4 100644 --- a/protocols/v2/roles-logic-sv2/src/utils.rs +++ b/protocols/v2/roles-logic-sv2/src/utils.rs @@ -8,8 +8,10 @@ use bitcoin::{ blockdata::block::{Header, Version}, consensus, + consensus::Decodable, hash_types::{BlockHash, TxMerkleNode}, hashes::{sha256d::Hash as DHash, Hash}, + transaction::TxOut, Block, CompactTarget, Transaction, }; use codec_sv2::binary_sv2::U256; @@ -55,6 +57,48 @@ impl Default for Id { } } +/// Deserializes a vector of serialized outputs into a vector of TxOuts. +/// +/// Only to be used for deserializing outputs from a NewTemplate message. +/// +/// Not suitable for deserializing outputs from a SetCustomMiningJob message or +/// AllocateMiningJobToken.Success. +pub fn deserialize_template_outputs( + serialized_outputs: Vec, + coinbase_tx_outputs_count: u32, +) -> Result, Error> { + let mut deserialized_outputs: Vec = vec![]; + + // The serialized outputs are in Bitcoin consensus format + // We need to parse them one by one, keeping track of cursor position + let mut cursor = 0; + let mut txouts = &serialized_outputs[cursor..]; + + // Iteratively decode each TxOut until we can't decode any more + while let Ok(out) = TxOut::consensus_decode(&mut txouts) { + // Calculate the size of this TxOut based on its script_pubkey length + // 8 bytes for value + variable bytes for script_pubkey length + // For small scripts (0-252 bytes): 1 byte length prefix + // For medium scripts (253-1000000 bytes): 3 byte length prefix (1 marker + 2 byte + // length) + let len = match out.script_pubkey.len() { + a @ 0..=252 => 8 + 1 + a, // 8 (value) + 1 (compact size) + script_len + a @ 253..=1000000 => 8 + 3 + a, // 8 (value) + 3 (compact size) + script_len + _ => break, // Unreasonably large script, likely an error + }; + + // Move the cursor forward by the size of this TxOut + cursor += len; + deserialized_outputs.push(out); + } + + if deserialized_outputs.len() != coinbase_tx_outputs_count as usize { + return Err(Error::FailedToDeserializeCoinbaseOutputs); + } + + Ok(deserialized_outputs) +} + /// Custom synchronization primitive for managing shared mutable state. /// /// This custom mutex implementation builds on [`std::sync::Mutex`] to enhance usability and safety diff --git a/roles/jd-client/src/lib/job_declarator/mod.rs b/roles/jd-client/src/lib/job_declarator/mod.rs index 729d4f420a..b86118f217 100644 --- a/roles/jd-client/src/lib/job_declarator/mod.rs +++ b/roles/jd-client/src/lib/job_declarator/mod.rs @@ -32,7 +32,7 @@ use stratum_common::{ mining_sv2::SubmitSharesExtended, parsers::{AnyMessage, JobDeclaration}, template_distribution_sv2::SetNewPrevHash, - utils::Mutex, + utils::{deserialize_template_outputs, Mutex}, }, }; use tokio::task::AbortHandle; @@ -424,27 +424,12 @@ impl JobDeclarator { // SetNewPrevHash. let set_new_prev_hash = last_declare.prev_hash; - let mut template_coinbase_outputs = Vec::::consensus_decode( - &mut template - .coinbase_tx_outputs - .inner_as_ref() - .to_vec() - .as_slice(), + let mut template_coinbase_outputs = deserialize_template_outputs( + template.coinbase_tx_outputs.to_vec(), + template.coinbase_tx_outputs_count, ) .expect("Failed to deserialize template outputs"); - // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 - if template_coinbase_outputs.is_empty() { - template_coinbase_outputs = vec![TxOut::consensus_decode( - &mut template - .coinbase_tx_outputs - .inner_as_ref() - .to_vec() - .as_slice(), - ) - .expect("Failed to deserialize template outputs")]; - } - let mut pool_coinbase_outputs = Vec::::consensus_decode( &mut last_declare.coinbase_pool_output.as_slice(), ) @@ -570,27 +555,12 @@ impl JobDeclarator { // The token received from JDS for this job. let signed_token = job.mining_job_token.clone(); // Prepare the pool's coinbase output by appending the template's outputs. - let mut template_coinbase_outputs = Vec::::consensus_decode( - &mut template - .coinbase_tx_outputs - .inner_as_ref() - .to_vec() - .as_slice(), + let mut template_coinbase_outputs = deserialize_template_outputs( + template.coinbase_tx_outputs.to_vec(), + template.coinbase_tx_outputs_count, ) .expect("Failed to deserialize template outputs"); - // temporary workaround for https://github.com/Sjors/bitcoin/issues/92 - if template_coinbase_outputs.is_empty() { - template_coinbase_outputs = vec![TxOut::consensus_decode( - &mut template - .coinbase_tx_outputs - .inner_as_ref() - .to_vec() - .as_slice(), - ) - .expect("Failed to deserialize template outputs")]; - } - let mut pool_coinbase_outputs = Vec::::consensus_decode(&mut pool_outs.as_slice()) .expect("Failed to deserialize pool outputs"); From f8756b9c652517424cc8f57c6e45bf310da89989 Mon Sep 17 00:00:00 2001 From: plebhash Date: Wed, 25 Jun 2025 17:34:44 -0300 Subject: [PATCH 068/338] deprecate mining-proxy crate --- .github/workflows/coverage-roles.yaml | 8 - roles/Cargo.lock | 21 - roles/Cargo.toml | 1 - roles/jd-client/src/main.rs | 3 +- roles/mining-proxy/Cargo.toml | 33 - roles/mining-proxy/README.md | 64 - .../config-examples/proxy-config-example.toml | 13 - roles/mining-proxy/flamegraph.svg | 419 ------ roles/mining-proxy/perf.data | Bin 1138556 -> 0 bytes roles/mining-proxy/perf.data.old | Bin 741540 -> 0 bytes roles/mining-proxy/src/args.rs | 52 - .../mining-proxy/src/lib/downstream_mining.rs | 529 ------- roles/mining-proxy/src/lib/error.rs | 35 - roles/mining-proxy/src/lib/mod.rs | 191 --- roles/mining-proxy/src/lib/routing_logic.rs | 422 ------ roles/mining-proxy/src/lib/selectors.rs | 346 ----- roles/mining-proxy/src/lib/upstream_mining.rs | 1340 ----------------- roles/mining-proxy/src/main.rs | 47 - scripts/coverage-roles.sh | 1 - test/integration-tests/Cargo.lock | 21 - test/integration-tests/Cargo.toml | 1 - test/integration-tests/lib/mod.rs | 32 - 22 files changed, 1 insertion(+), 3578 deletions(-) delete mode 100644 roles/mining-proxy/Cargo.toml delete mode 100644 roles/mining-proxy/README.md delete mode 100644 roles/mining-proxy/config-examples/proxy-config-example.toml delete mode 100644 roles/mining-proxy/flamegraph.svg delete mode 100644 roles/mining-proxy/perf.data delete mode 100644 roles/mining-proxy/perf.data.old delete mode 100644 roles/mining-proxy/src/args.rs delete mode 100644 roles/mining-proxy/src/lib/downstream_mining.rs delete mode 100644 roles/mining-proxy/src/lib/error.rs delete mode 100644 roles/mining-proxy/src/lib/mod.rs delete mode 100644 roles/mining-proxy/src/lib/routing_logic.rs delete mode 100644 roles/mining-proxy/src/lib/selectors.rs delete mode 100644 roles/mining-proxy/src/lib/upstream_mining.rs delete mode 100644 roles/mining-proxy/src/main.rs diff --git a/.github/workflows/coverage-roles.yaml b/.github/workflows/coverage-roles.yaml index 0b732df02e..f777de8de7 100644 --- a/.github/workflows/coverage-roles.yaml +++ b/.github/workflows/coverage-roles.yaml @@ -53,14 +53,6 @@ jobs: flags: mining_device-coverage token: ${{ secrets.CODECOV_TOKEN }} - - name: Upload mining_proxy_sv2-coverage to codecov.io - uses: codecov/codecov-action@v4 - with: - directory: ./roles/target/tarpaulin-reports/mining-proxy-coverage - file: ./roles/target/tarpaulin-reports/mining-proxy-coverage/cobertura.xml - flags: mining_proxy_sv2-coverage - token: ${{ secrets.CODECOV_TOKEN }} - - name: Upload pool_sv2-coverage to codecov.io uses: codecov/codecov-action@v4 with: diff --git a/roles/Cargo.lock b/roles/Cargo.lock index 52100e1b77..9c62a8b739 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -1383,7 +1383,6 @@ dependencies = [ "key-utils", "mining_device", "mining_device_sv1", - "mining_proxy_sv2", "minreq", "once_cell", "pool_sv2", @@ -1611,26 +1610,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "mining_proxy_sv2" -version = "0.1.3" -dependencies = [ - "async-channel 1.9.0", - "async-recursion 0.3.2", - "buffer_sv2", - "clap", - "config", - "futures", - "key-utils", - "nohash-hasher", - "once_cell", - "serde", - "stratum-common", - "tokio", - "tracing", - "tracing-subscriber", -] - [[package]] name = "mining_sv2" version = "4.0.0" diff --git a/roles/Cargo.toml b/roles/Cargo.toml index 857edeed6a..3705300e29 100644 --- a/roles/Cargo.toml +++ b/roles/Cargo.toml @@ -2,7 +2,6 @@ resolver="2" members = [ - "mining-proxy", "pool", "test-utils/mining-device", "test-utils/mining-device-sv1", diff --git a/roles/jd-client/src/main.rs b/roles/jd-client/src/main.rs index 75c6e58610..da2af17cac 100644 --- a/roles/jd-client/src/main.rs +++ b/roles/jd-client/src/main.rs @@ -13,8 +13,7 @@ use tracing::error; /// This will start: /// 1. An Upstream, this will connect with the mining Pool -/// 2. A listener that will wait for a mining downstream with ExtendedChannel capabilities (tproxy, -/// mining-proxy) +/// 2. A listener that will wait for a tproxy /// 3. A JobDeclarator, this will connect with the job-declarator-server /// 4. A TemplateRx, this will connect with bitcoind /// diff --git a/roles/mining-proxy/Cargo.toml b/roles/mining-proxy/Cargo.toml deleted file mode 100644 index dac10c81c6..0000000000 --- a/roles/mining-proxy/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "mining_proxy_sv2" -version = "0.1.3" -authors = ["The Stratum V2 Developers"] -edition = "2018" -description = "SV2 mining proxy role" -documentation = "https://docs.rs/mining_proxy_sv2" -readme = "README.md" -homepage = "https://stratumprotocol.org" -repository = "https://github.com/stratum-mining/stratum" -license = "MIT OR Apache-2.0" -keywords = ["stratum", "mining", "bitcoin", "protocol"] - - -[lib] -name = "mining_proxy_sv2" -path = "src/lib/mod.rs" - -[dependencies] -stratum-common = { path = "../../common" , features = ["with_network_helpers"] } -async-channel = "1.8.0" -async-recursion = "0.3.2" -buffer_sv2 = { path = "../../utils/buffer" } -futures = "0.3.19" -once_cell = "1.12.0" -serde = { version = "1.0.89", features = ["derive", "alloc"], default-features = false } -tokio = { version = "1.44.1", features = ["full"] } -ext-config = { version = "0.14.0", features = ["toml"], package = "config" } -tracing = {version = "0.1"} -tracing-subscriber = {version = "0.3"} -nohash-hasher = "0.2.0" -key-utils = { path = "../../utils/key-utils" } -clap = { version = "4.5.39", features = ["derive"] } diff --git a/roles/mining-proxy/README.md b/roles/mining-proxy/README.md deleted file mode 100644 index da17d89102..0000000000 --- a/roles/mining-proxy/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# mining-proxy - -## Run - -## proxy-config.toml file - -When spawned the proxy will look in the current working directory (linux) for a -`proxy-config.config` if the file is not available the proxy will panic. We can specify a different -path for the config file with the `-c` option. - -The config need to be a valid toml file with the below values: -1. upstreams: vector of upstreams (likely pools). An upstream is composed by: - 1. channel_kind: can be either `Group`, `Extended`, `ExtendedWithDeclarator`. - * __Group__: Proxy do not open an extended channel with upstream but just relay request to - open standard channel from downstream to upstream, being the proxy non HOM the channels are - grouped. - * __Extended__: Proxy open an extended channel with upstream. When downstream ask to open - standard channels it just use the open extended channel with upstream to itself open - standard channels downstream. - * __ExtendedWithDeclarator__: Like `Extended` but do not relay on the pool to create new job. It - just connect to a TP and communicate to the pool which is the job that it want to work with. - 2. adress: ip address of the upstream - 3. port: upstream's port - 4. pub_key: is the public key that upstream will use to sign the upstream cert needed for the - noise handshake. - 5. jd_values: optional value only needed when `channel_kind` is `ExtendedWithDeclarator` is - composed by: - 1. address: ip of the JD that we want to use with this upstream - 2. port: port of the JD that we want to use with this upstream - 3. pub_key: pub_key of the JD that we want to use with this upstream -2. tp_address: optional value only needed when at least one `upstream` in `upstreams` has the kind - `ExtendedWithDeclarator`. Is the address in the form `[ip:port]` of the TP. -3. listen_address: the address at which the `mining-proxy` will accept downstream connection. -4. listen_mining_port: the port at which the `mining-proxy` will accept downstream connection. -5. max_supported_version: the `mining-proxy` will not connect to upstream the are using an Sv2 - version higher that the one specified here (default to 2) -6. min_supported_version: the `mining-proxy` will not connect to upstream the are using an Sv2 - version smaller that the one specified here (default to 2) -7. downstream_share_per_minute: how many share per minute downstream is supposed to produce. The - `mining-proxy` will use this value and the expected downstream hash rate (communicate vie - `penStandardMiningChannel` to calculate the right downstream target. - -### Test miner <-> proxy <-> pool stack - -Terminal 1: -``` -% cd examples/sv2-proxy -% cargo run --bin pool -``` - -Terminal 2: -Run mining proxy: - -``` -% # For help run `cargo run -- --help` -% cd roles/v2/mining-proxy -% cargo run -``` - -Terminal 3: -``` -% cd examples/sv2-proxy -% cargo run --bin mining-device -``` diff --git a/roles/mining-proxy/config-examples/proxy-config-example.toml b/roles/mining-proxy/config-examples/proxy-config-example.toml deleted file mode 100644 index 5fc7605149..0000000000 --- a/roles/mining-proxy/config-examples/proxy-config-example.toml +++ /dev/null @@ -1,13 +0,0 @@ -upstreams = [ - { channel_kind = "Extended", address = "0.0.0.0", port = 34254, pub_key = "9auqWEzQDVyd2oe1JVGFLMLHZtCo2FFqZwtKA5gd9xbuEu7PH72"} -] -listen_address = "127.0.0.1" -listen_mining_port = 34255 -max_supported_version = 2 -min_supported_version = 2 -downstream_share_per_minute = 1 -# This value is used by the proxy to communicate to the pool the expected hash rate when we open an -# extended channel, based on it the pool will set a target for the channel -expected_total_downstream_hr = 10_000 -# If set to true the proxy will try to reconnect to an upstream that drop the connection -reconnect = true diff --git a/roles/mining-proxy/flamegraph.svg b/roles/mining-proxy/flamegraph.svg deleted file mode 100644 index 4e44857e02..0000000000 --- a/roles/mining-proxy/flamegraph.svg +++ /dev/null @@ -1,419 +0,0 @@ -Flame Graph Reset ZoomSearch [unknown] (12 samples, 9.09%)[unknown][unknown] (3 samples, 2.27%)[..[unknown] (2 samples, 1.52%)[unknown] (1 samples, 0.76%)[unknown] (22 samples, 16.67%)[unknown][unknown] (21 samples, 15.91%)[unknown][unknown] (14 samples, 10.61%)[unknown][unknown] (10 samples, 7.58%)[unknown][unknown] (4 samples, 3.03%)[un..__GI___ctype_init (2 samples, 1.52%)__GI___sigsetjmp (1 samples, 0.76%)__GI__setjmp (4 samples, 3.03%)__G..__sigjmp_save (1 samples, 0.76%)<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once (1 samples, 0.76%)<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once (1 samples, 0.76%)core::ops::function::FnOnce::call_once{{vtable.shim}} (1 samples, 0.76%)std::thread::Builder::spawn_unchecked::{{closure}} (1 samples, 0.76%)__prctl (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)__GI___sigaltstack (5 samples, 3.79%)__GI..[unknown] (5 samples, 3.79%)[unk..[unknown] (5 samples, 3.79%)[unk..[unknown] (5 samples, 3.79%)[unk..[unknown] (4 samples, 3.03%)[un..[unknown] (1 samples, 0.76%)__clone3 (39 samples, 29.55%)__clone3start_thread (39 samples, 29.55%)start_threadstd::sys::unix::thread::Thread::new::thread_start (9 samples, 6.82%)std::sys:..std::sys::unix::stack_overflow::Handler::new (8 samples, 6.06%)std::sys..std::sys::unix::stack_overflow::imp::make_handler (8 samples, 6.06%)std::sys..std::sys::unix::stack_overflow::imp::get_stack (3 samples, 2.27%)s..std::sys::unix::stack_overflow::imp::get_stackp (3 samples, 2.27%)s..std::sys::unix::os::page_size (3 samples, 2.27%)s..__GI___sysconf (3 samples, 2.27%)_..[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)_dl_start_final (1 samples, 0.76%)_dl_sysdep_start (1 samples, 0.76%)dl_platform_init (1 samples, 0.76%)init_cpu_features (1 samples, 0.76%)dl_init_cacheinfo (1 samples, 0.76%)handle_amd (1 samples, 0.76%)mining-proxy (58 samples, 43.94%)mining-proxy_start (5 samples, 3.79%)_sta.._dl_start (4 samples, 3.03%)_dl..rtld_timer_start (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)<noise_sv2::Initiator as noise_sv2::handshake::Step>::step (1 samples, 0.76%)snow::handshakestate::HandshakeState::read_message (1 samples, 0.76%)snow::handshakestate::HandshakeState::_read_message (1 samples, 0.76%)snow::handshakestate::HandshakeState::dh (1 samples, 0.76%)<snow::resolvers::default::Dh25519 as snow::types::Dh>::dh (1 samples, 0.76%)curve25519_dalek::montgomery::<impl core::ops::arith::Mul<curve25519_dalek::montgomery::MontgomeryPoint> for curve25519_dalek::scalar::Scalar>::mul (1 samples, 0.76%)curve25519_dalek::montgomery::<impl core::ops::arith::Mul<&curve25519_dalek::montgomery::MontgomeryPoint> for &curve25519_dalek::scalar::Scalar>::mul (1 samples, 0.76%)<&curve25519_dalek::montgomery::MontgomeryPoint as core::ops::arith::Mul<&curve25519_dalek::scalar::Scalar>>::mul (1 samples, 0.76%)curve25519_dalek::montgomery::differential_add_and_double (1 samples, 0.76%)<&curve25519_dalek::backend::serial::u64::field::FieldElement51 as core::ops::arith::Mul<&curve25519_dalek::backend::serial::u64::field::FieldElement51>>::mul (1 samples, 0.76%)<tokio::loom::std::parking_lot::MutexGuard<T> as core::ops::deref::DerefMut>::deref_mut (1 samples, 0.76%)<lock_api::mutex::MutexGuard<R,T> as core::ops::deref::DerefMut>::deref_mut (1 samples, 0.76%)alloc::collections::vec_deque::VecDeque<T,A>::buffer_read (1 samples, 0.76%)alloc::collections::vec_deque::VecDeque<T,A>::ptr (1 samples, 0.76%)alloc::raw_vec::RawVec<T,A>::ptr (1 samples, 0.76%)core::ptr::unique::Unique<T>::as_ptr (1 samples, 0.76%)alloc::collections::vec_deque::VecDeque<T,A>::pop_front (2 samples, 1.52%)alloc::collections::vec_deque::VecDeque<T,A>::is_empty (1 samples, 0.76%)parking_lot::raw_mutex::RawMutex::unlock_slow (1 samples, 0.76%)parking_lot_core::parking_lot::unpark_one (1 samples, 0.76%)parking_lot_core::parking_lot::lock_bucket (1 samples, 0.76%)parking_lot_core::word_lock::WordLock::lock (1 samples, 0.76%)core::sync::atomic::AtomicUsize::compare_exchange_weak (1 samples, 0.76%)core::sync::atomic::atomic_compare_exchange_weak (1 samples, 0.76%)core::mem::drop (2 samples, 1.52%)core::ptr::drop_in_place<tokio::loom::std::parking_lot::MutexGuard<tokio::runtime::blocking::pool::Shared>> (2 samples, 1.52%)core::ptr::drop_in_place<lock_api::mutex::MutexGuard<parking_lot::raw_mutex::RawMutex,tokio::runtime::blocking::pool::Shared>> (2 samples, 1.52%)<lock_api::mutex::MutexGuard<R,T> as core::ops::drop::Drop>::drop (2 samples, 1.52%)<parking_lot::raw_mutex::RawMutex as lock_api::mutex::RawMutex>::unlock (2 samples, 1.52%)parking_lot_core::parking_lot::deadlock::release_resource (1 samples, 0.76%)core::sync::atomic::AtomicU8::load (1 samples, 0.76%)parking_lot_core::parking_lot::park::{{closure}} (4 samples, 3.03%)par..core::cell::Cell<T>::set (1 samples, 0.76%)core::cell::Cell<T>::replace (1 samples, 0.76%)core::mem::replace (1 samples, 0.76%)core::ptr::write (1 samples, 0.76%)parking_lot_core::parking_lot::Bucket::new (1 samples, 0.76%)core::cell::UnsafeCell<T>::new (1 samples, 0.76%)parking_lot_core::parking_lot::park (6 samples, 4.55%)parki..parking_lot_core::parking_lot::with_thread_data (6 samples, 4.55%)parki..std::thread::local::LocalKey<T>::try_with (2 samples, 1.52%)parking_lot_core::parking_lot::with_thread_data::THREAD_DATA::__getit (2 samples, 1.52%)std::thread::local::fast::Key<T>::get (2 samples, 1.52%)std::thread::local::fast::Key<T>::try_initialize (2 samples, 1.52%)std::thread::local::lazy::LazyKeyInner<T>::initialize (2 samples, 1.52%)core::ops::function::FnOnce::call_once (2 samples, 1.52%)parking_lot_core::parking_lot::with_thread_data::THREAD_DATA::__init (2 samples, 1.52%)parking_lot_core::parking_lot::ThreadData::new (2 samples, 1.52%)parking_lot_core::parking_lot::grow_hashtable (2 samples, 1.52%)parking_lot_core::parking_lot::HashTable::new (2 samples, 1.52%)std::time::Instant::now (1 samples, 0.76%)std::sys::unix::time::inner::Instant::now (1 samples, 0.76%)std::sys::unix::time::inner::now (1 samples, 0.76%)__clock_gettime_2 (1 samples, 0.76%)__vdso_clock_gettime (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)<I as core::iter::traits::collect::IntoIterator>::into_iter (2 samples, 1.52%)core::hint::spin_loop (1 samples, 0.76%)parking_lot::raw_mutex::RawMutex::lock_slow (13 samples, 9.85%)parking_lot::r..parking_lot_core::spinwait::SpinWait::spin (4 samples, 3.03%)par..parking_lot_core::spinwait::cpu_relax (4 samples, 3.03%)par..core::iter::range::<impl core::iter::traits::iterator::Iterator for core::ops::range::Range<A>>::next (1 samples, 0.76%)<core::ops::range::Range<T> as core::iter::range::RangeIteratorImpl>::spec_next (1 samples, 0.76%)<u32 as core::iter::range::Step>::forward_unchecked (1 samples, 0.76%)tokio::loom::std::parking_lot::Mutex<T>::lock (14 samples, 10.61%)tokio::loom::st..lock_api::mutex::Mutex<R,T>::lock (14 samples, 10.61%)lock_api::mutex..<parking_lot::raw_mutex::RawMutex as lock_api::mutex::RawMutex>::lock (14 samples, 10.61%)<parking_lot::r..parking_lot_core::parking_lot::deadlock::acquire_resource (1 samples, 0.76%)<parking_lot_core::parking_lot::ParkResult as core::cmp::PartialEq>::eq (2 samples, 1.52%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)core::ptr::drop_in_place<core::option::Option<parking_lot_core::parking_lot::ThreadData>> (1 samples, 0.76%)core::sync::atomic::AtomicI32::load (7 samples, 5.30%)core::..core::sync::atomic::atomic_load (5 samples, 3.79%)core..[unknown] (1 samples, 0.76%)<parking_lot_core::thread_parker::imp::ThreadParker as parking_lot_core::thread_parker::ThreadParkerT>::park (12 samples, 9.09%)<parking_lot_..parking_lot_core::thread_parker::imp::ThreadParker::futex_wait (3 samples, 2.27%)p..syscall (1 samples, 0.76%)[unknown] (1 samples, 0.76%)tokio::runtime::thread_pool::park::Inner::park_condvar (20 samples, 15.15%)tokio::runtime::thread_..tokio::loom::std::parking_lot::Condvar::wait (20 samples, 15.15%)tokio::loom::std::parki..parking_lot::condvar::Condvar::wait (20 samples, 15.15%)parking_lot::condvar::C..parking_lot::condvar::Condvar::wait_until_internal (20 samples, 15.15%)parking_lot::condvar::C..parking_lot_core::parking_lot::park (18 samples, 13.64%)parking_lot_core::par..parking_lot_core::parking_lot::with_thread_data (18 samples, 13.64%)parking_lot_core::par..parking_lot_core::parking_lot::park::{{closure}} (17 samples, 12.88%)parking_lot_core::p..parking_lot_core::parking_lot::deadlock::on_unpark (1 samples, 0.76%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park (2 samples, 1.52%)<tokio::process::imp::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)<tokio::signal::unix::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)<tokio::io::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)tokio::io::driver::Driver::turn (2 samples, 1.52%)<tokio::runtime::thread_pool::park::Parker as tokio::park::Park>::park (23 samples, 17.42%)<tokio::runtime::thread_poo..tokio::runtime::thread_pool::park::Inner::park (23 samples, 17.42%)tokio::runtime::thread_pool..tokio::runtime::thread_pool::park::Inner::park_driver (3 samples, 2.27%)t..<tokio::runtime::driver::Driver as tokio::park::Park>::park (3 samples, 2.27%)<..<tokio::park::either::Either<A,B> as tokio::park::Park>::park (3 samples, 2.27%)<..<tokio::time::driver::Driver<P> as tokio::park::Park>::park (3 samples, 2.27%)<..tokio::time::driver::Driver<P>::park_internal (3 samples, 2.27%)t..tokio::time::driver::<impl tokio::time::driver::handle::Handle>::process (1 samples, 0.76%)tokio::time::driver::ClockTime::now (1 samples, 0.76%)tokio::time::driver::ClockTime::instant_to_tick (1 samples, 0.76%)core::time::Duration::as_millis (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Context::park_timeout (24 samples, 18.18%)tokio::runtime::thread_pool:..core::ptr::drop_in_place<core::option::Option<tokio::runtime::thread_pool::park::Parker>> (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Core::maintenance (1 samples, 0.76%)tokio::runtime::task::inject::Inject<T>::is_closed (1 samples, 0.76%)core::ptr::drop_in_place<tokio::loom::std::parking_lot::MutexGuard<tokio::runtime::task::inject::Pointers>> (1 samples, 0.76%)core::ptr::drop_in_place<lock_api::mutex::MutexGuard<parking_lot::raw_mutex::RawMutex,tokio::runtime::task::inject::Pointers>> (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Core::transition_from_parked (1 samples, 0.76%)tokio::runtime::thread_pool::idle::Idle::is_parked (1 samples, 0.76%)core::slice::<impl [T]>::contains (1 samples, 0.76%)<T as core::slice::cmp::SliceContains>::slice_contains (1 samples, 0.76%)<core::slice::iter::Iter<T> as core::iter::traits::iterator::Iterator>::any (1 samples, 0.76%)<core::slice::iter::Iter<T> as core::iter::traits::iterator::Iterator>::next (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Context::park (27 samples, 20.45%)tokio::runtime::thread_pool::wor..tokio::runtime::thread_pool::worker::Core::transition_to_parked (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Shared::notify_if_work_pending (1 samples, 0.76%)tokio::runtime::thread_pool::queue::Steal<T>::is_empty (1 samples, 0.76%)tokio::runtime::thread_pool::queue::Inner<T>::is_empty (1 samples, 0.76%)tokio::runtime::thread_pool::queue::Inner<T>::len (1 samples, 0.76%)core::sync::atomic::AtomicU32::load (1 samples, 0.76%)tokio::runtime::task::inject::Inject<T>::pop (1 samples, 0.76%)tokio::macros::scoped_tls::ScopedKey<T>::set (30 samples, 22.73%)tokio::macros::scoped_tls::ScopedKey..tokio::runtime::thread_pool::worker::run::{{closure}} (30 samples, 22.73%)tokio::runtime::thread_pool::worker:..tokio::runtime::thread_pool::worker::Context::run (30 samples, 22.73%)tokio::runtime::thread_pool::worker:..tokio::runtime::thread_pool::worker::Core::next_task (3 samples, 2.27%)t..core::option::Option<T>::or_else (3 samples, 2.27%)c..tokio::runtime::thread_pool::worker::Core::next_task::{{closure}} (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Worker::inject (1 samples, 0.76%)tokio::runtime::blocking::pool::Inner::run (50 samples, 37.88%)tokio::runtime::blocking::pool::Inner::runtokio::runtime::blocking::pool::Task::run (31 samples, 23.48%)tokio::runtime::blocking::pool::Task:..tokio::runtime::task::UnownedTask<S>::run (31 samples, 23.48%)tokio::runtime::task::UnownedTask<S>:..tokio::runtime::task::raw::RawTask::poll (31 samples, 23.48%)tokio::runtime::task::raw::RawTask::p..tokio::runtime::task::raw::poll (31 samples, 23.48%)tokio::runtime::task::raw::polltokio::runtime::task::harness::Harness<T,S>::poll (31 samples, 23.48%)tokio::runtime::task::harness::Harnes..tokio::runtime::task::harness::Harness<T,S>::poll_inner (31 samples, 23.48%)tokio::runtime::task::harness::Harnes..tokio::runtime::task::harness::poll_future (31 samples, 23.48%)tokio::runtime::task::harness::poll_f..std::panic::catch_unwind (31 samples, 23.48%)std::panic::catch_unwindstd::panicking::try (31 samples, 23.48%)std::panicking::try__rust_try (31 samples, 23.48%)__rust_trystd::panicking::try::do_call (31 samples, 23.48%)std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (31 samples, 23.48%)<core::panic::unwind_safe::AssertUnwi..tokio::runtime::task::harness::poll_future::{{closure}} (31 samples, 23.48%)tokio::runtime::task::harness::poll_f..tokio::runtime::task::core::CoreStage<T>::poll (31 samples, 23.48%)tokio::runtime::task::core::CoreStage..tokio::loom::std::unsafe_cell::UnsafeCell<T>::with_mut (31 samples, 23.48%)tokio::loom::std::unsafe_cell::Unsafe..tokio::runtime::task::core::CoreStage<T>::poll::{{closure}} (31 samples, 23.48%)tokio::runtime::task::core::CoreStage..<tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll (31 samples, 23.48%)<tokio::runtime::blocking::task::Bloc..tokio::runtime::thread_pool::worker::Launch::launch::{{closure}} (31 samples, 23.48%)tokio::runtime::thread_pool::worker::..tokio::runtime::thread_pool::worker::run (31 samples, 23.48%)tokio::runtime::thread_pool::worker::..tokio::util::atomic_cell::AtomicCell<T>::take (1 samples, 0.76%)tokio::util::atomic_cell::AtomicCell<T>::swap (1 samples, 0.76%)<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (51 samples, 38.64%)<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::fu..std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}} (51 samples, 38.64%)std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}}std::sys_common::backtrace::__rust_begin_short_backtrace (51 samples, 38.64%)std::sys_common::backtrace::__rust_begin_short_backtracetokio::runtime::blocking::pool::Spawner::spawn_thread::{{closure}} (51 samples, 38.64%)tokio::runtime::blocking::pool::Spawner::spawn_thread::{{closur..tokio::runtime::context::enter (1 samples, 0.76%)tokio::runtime::context::try_enter (1 samples, 0.76%)std::thread::local::LocalKey<T>::try_with (1 samples, 0.76%)tokio::runtime::context::CONTEXT::__getit (1 samples, 0.76%)std::thread::local::fast::Key<T>::get (1 samples, 0.76%)std::thread::local::fast::Key<T>::try_initialize (1 samples, 0.76%)std::thread::local::fast::Key<T>::try_register_dtor (1 samples, 0.76%)core::cell::Cell<T>::set (1 samples, 0.76%)std::panic::catch_unwind (52 samples, 39.39%)std::panic::catch_unwindstd::panicking::try (52 samples, 39.39%)std::panicking::try__rust_try (52 samples, 39.39%)__rust_trystd::panicking::try::do_call (52 samples, 39.39%)std::panicking::try::do_callcore::mem::manually_drop::ManuallyDrop<T>::take (1 samples, 0.76%)core::ptr::read (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)__clone3 (56 samples, 42.42%)__clone3start_thread (56 samples, 42.42%)start_threadstd::sys::unix::thread::Thread::new::thread_start (56 samples, 42.42%)std::sys::unix::thread::Thread::new::thread_start<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once (56 samples, 42.42%)<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_o..<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once (56 samples, 42.42%)<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_o..core::ops::function::FnOnce::call_once{{vtable.shim}} (56 samples, 42.42%)core::ops::function::FnOnce::call_once{{vtable.shim}}std::thread::Builder::spawn_unchecked::{{closure}} (56 samples, 42.42%)std::thread::Builder::spawn_unchecked::{{closure}}std::sys::unix::thread::guard::current (4 samples, 3.03%)std..__pthread_getattr_np (4 samples, 3.03%)__p..__GI___libc_malloc (4 samples, 3.03%)__G..tcache_init.part.0 (4 samples, 3.03%)tca..arena_get2.part.0 (4 samples, 3.03%)are..alloc_new_heap (3 samples, 2.27%)a..__GI___mmap64 (2 samples, 1.52%)[unknown] (2 samples, 1.52%)[unknown] (2 samples, 1.52%)[unknown] (2 samples, 1.52%)[unknown] (1 samples, 0.76%)[unknown] (1 samples, 0.76%)__pthread_getaffinity_alias (1 samples, 0.76%)[unknown] (1 samples, 0.76%)core::ops::function::FnOnce::call_once{{vtable.shim}} (2 samples, 1.52%)std::thread::Builder::spawn_unchecked::{{closure}} (2 samples, 1.52%)std::panic::catch_unwind (2 samples, 1.52%)std::panicking::try (2 samples, 1.52%)__rust_try (2 samples, 1.52%)std::panicking::try::do_call (2 samples, 1.52%)<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (2 samples, 1.52%)std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}} (2 samples, 1.52%)std::sys_common::backtrace::__rust_begin_short_backtrace (2 samples, 1.52%)tokio::runtime::blocking::pool::Spawner::spawn_thread::{{closure}} (2 samples, 1.52%)tokio::runtime::blocking::pool::Inner::run (2 samples, 1.52%)tokio::runtime::blocking::pool::Task::run (2 samples, 1.52%)tokio::runtime::task::UnownedTask<S>::run (2 samples, 1.52%)tokio::runtime::task::raw::RawTask::poll (2 samples, 1.52%)tokio::runtime::task::raw::poll (2 samples, 1.52%)tokio::runtime::task::harness::Harness<T,S>::poll (2 samples, 1.52%)tokio::runtime::task::harness::Harness<T,S>::poll_inner (2 samples, 1.52%)tokio::runtime::task::harness::poll_future (2 samples, 1.52%)std::panic::catch_unwind (2 samples, 1.52%)std::panicking::try (2 samples, 1.52%)__rust_try (2 samples, 1.52%)std::panicking::try::do_call (2 samples, 1.52%)<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (2 samples, 1.52%)tokio::runtime::task::harness::poll_future::{{closure}} (2 samples, 1.52%)tokio::runtime::task::core::CoreStage<T>::poll (2 samples, 1.52%)tokio::loom::std::unsafe_cell::UnsafeCell<T>::with_mut (2 samples, 1.52%)tokio::runtime::task::core::CoreStage<T>::poll::{{closure}} (2 samples, 1.52%)<tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Launch::launch::{{closure}} (2 samples, 1.52%)tokio::runtime::thread_pool::worker::run (2 samples, 1.52%)tokio::macros::scoped_tls::ScopedKey<T>::set (2 samples, 1.52%)tokio::runtime::thread_pool::worker::run::{{closure}} (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Context::run (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Context::park (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Context::park_timeout (2 samples, 1.52%)<tokio::runtime::thread_pool::park::Parker as tokio::park::Park>::park (2 samples, 1.52%)tokio::runtime::thread_pool::park::Inner::park (2 samples, 1.52%)tokio::runtime::thread_pool::park::Inner::park_driver (2 samples, 1.52%)<tokio::runtime::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park (2 samples, 1.52%)<tokio::time::driver::Driver<P> as tokio::park::Park>::park (2 samples, 1.52%)tokio::time::driver::Driver<P>::park_internal (2 samples, 1.52%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park (2 samples, 1.52%)<tokio::process::imp::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)<tokio::signal::unix::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)<tokio::io::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)tokio::io::driver::Driver::turn (2 samples, 1.52%)mio::poll::Poll::poll (2 samples, 1.52%)mio::sys::unix::selector::epoll::Selector::select (2 samples, 1.52%)core::result::Result<T,E>::map (2 samples, 1.52%)mio::sys::unix::selector::epoll::Selector::select::{{closure}} (2 samples, 1.52%)alloc::vec::Vec<T,A>::set_len (2 samples, 1.52%)mining_proxy::lib::upstream_mining::UpstreamMiningNode::connect::{{closure}} (1 samples, 0.76%)noise_sv2::Initiator::from_raw_k (1 samples, 0.76%)ed25519_dalek::public::PublicKey::from_bytes (1 samples, 0.76%)curve25519_dalek::edwards::CompressedEdwardsY::decompress (1 samples, 0.76%)curve25519_dalek::field::<impl curve25519_dalek::backend::serial::u64::field::FieldElement51>::sqrt_ratio_i (1 samples, 0.76%)<&curve25519_dalek::backend::serial::u64::field::FieldElement51 as core::ops::arith::Mul<&curve25519_dalek::backend::serial::u64::field::FieldElement51>>::mul (1 samples, 0.76%)__memmove_avx_unaligned (1 samples, 0.76%)start_thread (2 samples, 1.52%)std::sys::unix::thread::Thread::new::thread_start (2 samples, 1.52%)<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once (2 samples, 1.52%)<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once (2 samples, 1.52%)core::ops::function::FnOnce::call_once{{vtable.shim}} (2 samples, 1.52%)std::thread::Builder::spawn_unchecked::{{closure}} (2 samples, 1.52%)std::panic::catch_unwind (2 samples, 1.52%)std::panicking::try (2 samples, 1.52%)__rust_try (2 samples, 1.52%)std::panicking::try::do_call (2 samples, 1.52%)<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (2 samples, 1.52%)std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}} (2 samples, 1.52%)std::sys_common::backtrace::__rust_begin_short_backtrace (2 samples, 1.52%)tokio::runtime::blocking::pool::Spawner::spawn_thread::{{closure}} (2 samples, 1.52%)tokio::runtime::blocking::pool::Inner::run (2 samples, 1.52%)tokio::runtime::blocking::pool::Task::run (2 samples, 1.52%)tokio::runtime::task::UnownedTask<S>::run (2 samples, 1.52%)tokio::runtime::task::raw::RawTask::poll (2 samples, 1.52%)tokio::runtime::task::raw::poll (2 samples, 1.52%)tokio::runtime::task::harness::Harness<T,S>::poll (2 samples, 1.52%)tokio::runtime::task::harness::Harness<T,S>::poll_inner (2 samples, 1.52%)tokio::runtime::task::harness::poll_future (2 samples, 1.52%)std::panic::catch_unwind (2 samples, 1.52%)std::panicking::try (2 samples, 1.52%)__rust_try (2 samples, 1.52%)std::panicking::try::do_call (2 samples, 1.52%)<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (2 samples, 1.52%)tokio::runtime::task::harness::poll_future::{{closure}} (2 samples, 1.52%)tokio::runtime::task::core::CoreStage<T>::poll (2 samples, 1.52%)tokio::loom::std::unsafe_cell::UnsafeCell<T>::with_mut (2 samples, 1.52%)tokio::runtime::task::core::CoreStage<T>::poll::{{closure}} (2 samples, 1.52%)<tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Launch::launch::{{closure}} (2 samples, 1.52%)tokio::runtime::thread_pool::worker::run (2 samples, 1.52%)tokio::macros::scoped_tls::ScopedKey<T>::set (2 samples, 1.52%)tokio::runtime::thread_pool::worker::run::{{closure}} (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Context::run (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Context::park (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Context::park_timeout (2 samples, 1.52%)<tokio::runtime::thread_pool::park::Parker as tokio::park::Park>::park (2 samples, 1.52%)tokio::runtime::thread_pool::park::Inner::park (2 samples, 1.52%)tokio::runtime::thread_pool::park::Inner::park_driver (2 samples, 1.52%)<tokio::runtime::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park (2 samples, 1.52%)<tokio::time::driver::Driver<P> as tokio::park::Park>::park (2 samples, 1.52%)tokio::time::driver::Driver<P>::park_internal (2 samples, 1.52%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park (2 samples, 1.52%)<tokio::process::imp::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)<tokio::signal::unix::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)<tokio::io::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)tokio::io::driver::Driver::turn (2 samples, 1.52%)mio::poll::Poll::poll (2 samples, 1.52%)mio::sys::unix::selector::epoll::Selector::select (2 samples, 1.52%)std::panic::catch_unwind (1 samples, 0.76%)std::panicking::try (1 samples, 0.76%)__rust_try (1 samples, 0.76%)std::panicking::try::do_call (1 samples, 0.76%)<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (1 samples, 0.76%)std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}} (1 samples, 0.76%)std::sys_common::backtrace::__rust_begin_short_backtrace (1 samples, 0.76%)tokio::runtime::blocking::pool::Spawner::spawn_thread::{{closure}} (1 samples, 0.76%)tokio::runtime::blocking::pool::Inner::run (1 samples, 0.76%)tokio::runtime::blocking::pool::Task::run (1 samples, 0.76%)tokio::runtime::task::UnownedTask<S>::run (1 samples, 0.76%)tokio::runtime::task::raw::RawTask::poll (1 samples, 0.76%)tokio::runtime::task::raw::poll (1 samples, 0.76%)tokio::runtime::task::harness::Harness<T,S>::poll (1 samples, 0.76%)tokio::runtime::task::harness::Harness<T,S>::poll_inner (1 samples, 0.76%)tokio::runtime::task::harness::poll_future (1 samples, 0.76%)std::panic::catch_unwind (1 samples, 0.76%)std::panicking::try (1 samples, 0.76%)__rust_try (1 samples, 0.76%)std::panicking::try::do_call (1 samples, 0.76%)<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (1 samples, 0.76%)tokio::runtime::task::harness::poll_future::{{closure}} (1 samples, 0.76%)tokio::runtime::task::core::CoreStage<T>::poll (1 samples, 0.76%)tokio::loom::std::unsafe_cell::UnsafeCell<T>::with_mut (1 samples, 0.76%)tokio::runtime::task::core::CoreStage<T>::poll::{{closure}} (1 samples, 0.76%)<tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Launch::launch::{{closure}} (1 samples, 0.76%)tokio::runtime::thread_pool::worker::run (1 samples, 0.76%)tokio::macros::scoped_tls::ScopedKey<T>::set (1 samples, 0.76%)tokio::runtime::thread_pool::worker::run::{{closure}} (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Context::run (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Context::park (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Context::park_timeout (1 samples, 0.76%)<tokio::runtime::thread_pool::park::Parker as tokio::park::Park>::park (1 samples, 0.76%)tokio::runtime::thread_pool::park::Inner::park (1 samples, 0.76%)tokio::runtime::thread_pool::park::Inner::park_driver (1 samples, 0.76%)<tokio::runtime::driver::Driver as tokio::park::Park>::park (1 samples, 0.76%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park (1 samples, 0.76%)<tokio::time::driver::Driver<P> as tokio::park::Park>::park (1 samples, 0.76%)tokio::time::driver::Driver<P>::park_internal (1 samples, 0.76%)tokio::time::driver::Driver<P>::park_timeout (1 samples, 0.76%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park_timeout (1 samples, 0.76%)<tokio::process::imp::driver::Driver as tokio::park::Park>::park_timeout (1 samples, 0.76%)<tokio::signal::unix::driver::Driver as tokio::park::Park>::park_timeout (1 samples, 0.76%)tokio::signal::unix::driver::Driver::process (1 samples, 0.76%)tokio::io::driver::registration::Registration::poll_read_ready (1 samples, 0.76%)tokio::io::driver::registration::Registration::poll_ready (1 samples, 0.76%)tokio::coop::poll_proceed (1 samples, 0.76%)std::thread::local::LocalKey<T>::with (1 samples, 0.76%)std::thread::local::LocalKey<T>::try_with (1 samples, 0.76%)tokio::coop::poll_proceed::{{closure}} (1 samples, 0.76%)core::cell::Cell<T>::set (1 samples, 0.76%)core::mem::drop (1 samples, 0.76%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park (1 samples, 0.76%)<tokio::process::imp::driver::Driver as tokio::park::Park>::park (1 samples, 0.76%)<tokio::signal::unix::driver::Driver as tokio::park::Park>::park (1 samples, 0.76%)<tokio::io::driver::Driver as tokio::park::Park>::park (1 samples, 0.76%)tokio::io::driver::Driver::turn (1 samples, 0.76%)tokio::io::driver::Driver::dispatch (1 samples, 0.76%)tokio::io::driver::scheduled_io::ScheduledIo::wake (1 samples, 0.76%)tokio::io::driver::scheduled_io::ScheduledIo::wake0 (1 samples, 0.76%)core::option::Option<T>::take (1 samples, 0.76%)std::panicking::try::do_call (2 samples, 1.52%)<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (2 samples, 1.52%)std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}} (2 samples, 1.52%)std::sys_common::backtrace::__rust_begin_short_backtrace (2 samples, 1.52%)tokio::runtime::blocking::pool::Spawner::spawn_thread::{{closure}} (2 samples, 1.52%)tokio::runtime::blocking::pool::Inner::run (2 samples, 1.52%)tokio::runtime::blocking::pool::Task::run (2 samples, 1.52%)tokio::runtime::task::UnownedTask<S>::run (2 samples, 1.52%)tokio::runtime::task::raw::RawTask::poll (2 samples, 1.52%)tokio::runtime::task::raw::poll (2 samples, 1.52%)tokio::runtime::task::harness::Harness<T,S>::poll (2 samples, 1.52%)tokio::runtime::task::harness::Harness<T,S>::poll_inner (2 samples, 1.52%)tokio::runtime::task::harness::poll_future (2 samples, 1.52%)std::panic::catch_unwind (2 samples, 1.52%)std::panicking::try (2 samples, 1.52%)__rust_try (2 samples, 1.52%)std::panicking::try::do_call (2 samples, 1.52%)<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (2 samples, 1.52%)tokio::runtime::task::harness::poll_future::{{closure}} (2 samples, 1.52%)tokio::runtime::task::core::CoreStage<T>::poll (2 samples, 1.52%)tokio::loom::std::unsafe_cell::UnsafeCell<T>::with_mut (2 samples, 1.52%)tokio::runtime::task::core::CoreStage<T>::poll::{{closure}} (2 samples, 1.52%)<tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Launch::launch::{{closure}} (2 samples, 1.52%)tokio::runtime::thread_pool::worker::run (2 samples, 1.52%)tokio::macros::scoped_tls::ScopedKey<T>::set (2 samples, 1.52%)tokio::runtime::thread_pool::worker::run::{{closure}} (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Context::run (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Context::park (2 samples, 1.52%)tokio::runtime::thread_pool::worker::Context::park_timeout (2 samples, 1.52%)<tokio::runtime::thread_pool::park::Parker as tokio::park::Park>::park (2 samples, 1.52%)tokio::runtime::thread_pool::park::Inner::park (2 samples, 1.52%)tokio::runtime::thread_pool::park::Inner::park_driver (2 samples, 1.52%)<tokio::runtime::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park (2 samples, 1.52%)<tokio::time::driver::Driver<P> as tokio::park::Park>::park (2 samples, 1.52%)tokio::time::driver::Driver<P>::park_internal (2 samples, 1.52%)tokio::time::driver::Driver<P>::park_timeout (1 samples, 0.76%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park_timeout (1 samples, 0.76%)<tokio::process::imp::driver::Driver as tokio::park::Park>::park_timeout (1 samples, 0.76%)<tokio::signal::unix::driver::Driver as tokio::park::Park>::park_timeout (1 samples, 0.76%)tokio::signal::unix::driver::Driver::process (1 samples, 0.76%)tokio::io::driver::registration::Registration::poll_read_ready (1 samples, 0.76%)tokio::io::driver::registration::Registration::poll_ready (1 samples, 0.76%)tokio::io::driver::scheduled_io::ScheduledIo::poll_readiness (1 samples, 0.76%)tokio::loom::std::parking_lot::Mutex<T>::lock (1 samples, 0.76%)lock_api::mutex::Mutex<R,T>::lock (1 samples, 0.76%)<parking_lot::raw_mutex::RawMutex as lock_api::mutex::RawMutex>::lock (1 samples, 0.76%)core::sync::atomic::AtomicU8::compare_exchange_weak (1 samples, 0.76%)core::sync::atomic::atomic_compare_exchange_weak (1 samples, 0.76%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park (2 samples, 1.52%)<tokio::process::imp::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)<tokio::signal::unix::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)<tokio::io::driver::Driver as tokio::park::Park>::park (2 samples, 1.52%)tokio::io::driver::Driver::turn (2 samples, 1.52%)mio::poll::Poll::poll (2 samples, 1.52%)mio::sys::unix::selector::epoll::Selector::select (2 samples, 1.52%)core::result::Result<T,E>::map (2 samples, 1.52%)std::sys::unix::thread::Thread::new::thread_start (3 samples, 2.27%)s..<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once (3 samples, 2.27%)<..<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once (3 samples, 2.27%)<..core::ops::function::FnOnce::call_once{{vtable.shim}} (3 samples, 2.27%)c..std::thread::Builder::spawn_unchecked::{{closure}} (3 samples, 2.27%)s..std::panic::catch_unwind (3 samples, 2.27%)s..std::panicking::try (3 samples, 2.27%)s..__rust_try (3 samples, 2.27%)_..std::panicking::try::do_call (3 samples, 2.27%)s..<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (3 samples, 2.27%)<..std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}} (3 samples, 2.27%)s..std::sys_common::backtrace::__rust_begin_short_backtrace (3 samples, 2.27%)s..tokio::runtime::blocking::pool::Spawner::spawn_thread::{{closure}} (3 samples, 2.27%)t..tokio::runtime::blocking::pool::Inner::run (3 samples, 2.27%)t..tokio::runtime::blocking::pool::Task::run (3 samples, 2.27%)t..tokio::runtime::task::UnownedTask<S>::run (3 samples, 2.27%)t..tokio::runtime::task::raw::RawTask::poll (3 samples, 2.27%)t..tokio::runtime::task::raw::poll (3 samples, 2.27%)t..tokio::runtime::task::harness::Harness<T,S>::poll (3 samples, 2.27%)t..tokio::runtime::task::harness::Harness<T,S>::poll_inner (3 samples, 2.27%)t..tokio::runtime::task::harness::poll_future (3 samples, 2.27%)t..std::panic::catch_unwind (3 samples, 2.27%)s..std::panicking::try (3 samples, 2.27%)s..__rust_try (3 samples, 2.27%)_..std::panicking::try::do_call (3 samples, 2.27%)s..<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (3 samples, 2.27%)<..tokio::runtime::task::harness::poll_future::{{closure}} (3 samples, 2.27%)t..tokio::runtime::task::core::CoreStage<T>::poll (3 samples, 2.27%)t..tokio::loom::std::unsafe_cell::UnsafeCell<T>::with_mut (3 samples, 2.27%)t..tokio::runtime::task::core::CoreStage<T>::poll::{{closure}} (3 samples, 2.27%)t..<tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll (3 samples, 2.27%)<..tokio::runtime::thread_pool::worker::Launch::launch::{{closure}} (3 samples, 2.27%)t..tokio::runtime::thread_pool::worker::run (3 samples, 2.27%)t..tokio::macros::scoped_tls::ScopedKey<T>::set (3 samples, 2.27%)t..tokio::runtime::thread_pool::worker::run::{{closure}} (3 samples, 2.27%)t..tokio::runtime::thread_pool::worker::Context::run (3 samples, 2.27%)t..tokio::runtime::thread_pool::worker::Context::park (3 samples, 2.27%)t..tokio::runtime::thread_pool::worker::Context::park_timeout (3 samples, 2.27%)t..<tokio::runtime::thread_pool::park::Parker as tokio::park::Park>::park (3 samples, 2.27%)<..tokio::runtime::thread_pool::park::Inner::park (3 samples, 2.27%)t..tokio::runtime::thread_pool::park::Inner::park_driver (3 samples, 2.27%)t..<tokio::runtime::driver::Driver as tokio::park::Park>::park (3 samples, 2.27%)<..<tokio::park::either::Either<A,B> as tokio::park::Park>::park (3 samples, 2.27%)<..<tokio::time::driver::Driver<P> as tokio::park::Park>::park (3 samples, 2.27%)<..tokio::time::driver::Driver<P>::park_internal (3 samples, 2.27%)t..tokio::time::driver::Driver<P>::park_timeout (1 samples, 0.76%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park_timeout (1 samples, 0.76%)<tokio::process::imp::driver::Driver as tokio::park::Park>::park_timeout (1 samples, 0.76%)<tokio::signal::unix::driver::Driver as tokio::park::Park>::park_timeout (1 samples, 0.76%)<tokio::io::driver::Driver as tokio::park::Park>::park_timeout (1 samples, 0.76%)tokio::io::driver::Driver::turn (1 samples, 0.76%)mio::poll::Poll::poll (1 samples, 0.76%)mio::sys::unix::selector::epoll::Selector::select (1 samples, 0.76%)core::result::Result<T,E>::map (2 samples, 1.52%)mio::sys::unix::selector::epoll::Selector::select::{{closure}} (1 samples, 0.76%)alloc::vec::Vec<T,A>::set_len (1 samples, 0.76%)<tokio::io::driver::Driver as tokio::park::Park>::park_timeout (3 samples, 2.27%)<..tokio::io::driver::Driver::turn (3 samples, 2.27%)t..mio::poll::Poll::poll (3 samples, 2.27%)m..mio::sys::unix::selector::epoll::Selector::select (3 samples, 2.27%)m..epoll_wait (1 samples, 0.76%)__GI___pthread_disable_asynccancel (1 samples, 0.76%)std::thread::Builder::spawn_unchecked::{{closure}} (4 samples, 3.03%)std..std::panic::catch_unwind (4 samples, 3.03%)std..std::panicking::try (4 samples, 3.03%)std..__rust_try (4 samples, 3.03%)__r..std::panicking::try::do_call (4 samples, 3.03%)std..<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (4 samples, 3.03%)<co..std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}} (4 samples, 3.03%)std..std::sys_common::backtrace::__rust_begin_short_backtrace (4 samples, 3.03%)std..tokio::runtime::blocking::pool::Spawner::spawn_thread::{{closure}} (4 samples, 3.03%)tok..tokio::runtime::blocking::pool::Inner::run (4 samples, 3.03%)tok..tokio::runtime::blocking::pool::Task::run (4 samples, 3.03%)tok..tokio::runtime::task::UnownedTask<S>::run (4 samples, 3.03%)tok..tokio::runtime::task::raw::RawTask::poll (4 samples, 3.03%)tok..tokio::runtime::task::raw::poll (4 samples, 3.03%)tok..tokio::runtime::task::harness::Harness<T,S>::poll (4 samples, 3.03%)tok..tokio::runtime::task::harness::Harness<T,S>::poll_inner (4 samples, 3.03%)tok..tokio::runtime::task::harness::poll_future (4 samples, 3.03%)tok..std::panic::catch_unwind (4 samples, 3.03%)std..std::panicking::try (4 samples, 3.03%)std..__rust_try (4 samples, 3.03%)__r..std::panicking::try::do_call (4 samples, 3.03%)std..<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (4 samples, 3.03%)<co..tokio::runtime::task::harness::poll_future::{{closure}} (4 samples, 3.03%)tok..tokio::runtime::task::core::CoreStage<T>::poll (4 samples, 3.03%)tok..tokio::loom::std::unsafe_cell::UnsafeCell<T>::with_mut (4 samples, 3.03%)tok..tokio::runtime::task::core::CoreStage<T>::poll::{{closure}} (4 samples, 3.03%)tok..<tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll (4 samples, 3.03%)<to..tokio::runtime::thread_pool::worker::Launch::launch::{{closure}} (4 samples, 3.03%)tok..tokio::runtime::thread_pool::worker::run (4 samples, 3.03%)tok..tokio::macros::scoped_tls::ScopedKey<T>::set (4 samples, 3.03%)tok..tokio::runtime::thread_pool::worker::run::{{closure}} (4 samples, 3.03%)tok..tokio::runtime::thread_pool::worker::Context::run (4 samples, 3.03%)tok..tokio::runtime::thread_pool::worker::Context::park (4 samples, 3.03%)tok..tokio::runtime::thread_pool::worker::Context::park_timeout (4 samples, 3.03%)tok..<tokio::runtime::thread_pool::park::Parker as tokio::park::Park>::park (4 samples, 3.03%)<to..tokio::runtime::thread_pool::park::Inner::park (4 samples, 3.03%)tok..tokio::runtime::thread_pool::park::Inner::park_driver (4 samples, 3.03%)tok..<tokio::runtime::driver::Driver as tokio::park::Park>::park (4 samples, 3.03%)<to..<tokio::park::either::Either<A,B> as tokio::park::Park>::park (4 samples, 3.03%)<to..<tokio::time::driver::Driver<P> as tokio::park::Park>::park (4 samples, 3.03%)<to..tokio::time::driver::Driver<P>::park_internal (4 samples, 3.03%)tok..tokio::time::driver::Driver<P>::park_timeout (4 samples, 3.03%)tok..<tokio::park::either::Either<A,B> as tokio::park::Park>::park_timeout (4 samples, 3.03%)<to..<tokio::process::imp::driver::Driver as tokio::park::Park>::park_timeout (4 samples, 3.03%)<to..<tokio::signal::unix::driver::Driver as tokio::park::Park>::park_timeout (4 samples, 3.03%)<to..tokio::signal::unix::driver::Driver::process (1 samples, 0.76%)tokio::io::driver::registration::Registration::poll_read_ready (1 samples, 0.76%)tokio::io::driver::registration::Registration::poll_ready (1 samples, 0.76%)tokio::coop::poll_proceed (1 samples, 0.76%)std::thread::local::LocalKey<T>::with (1 samples, 0.76%)std::thread::local::LocalKey<T>::try_with (1 samples, 0.76%)all (132 samples, 100%)tokio-runtime-w (74 samples, 56.06%)tokio-runtime-wtokio::runtime::task::harness::Harness<T,S>::poll_inner (1 samples, 0.76%)tokio::runtime::task::harness::poll_future (1 samples, 0.76%)std::panic::catch_unwind (1 samples, 0.76%)std::panicking::try (1 samples, 0.76%)__rust_try (1 samples, 0.76%)std::panicking::try::do_call (1 samples, 0.76%)<core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce< (1 samples, 0.76%)tokio::runtime::task::harness::poll_future::{{closure}} (1 samples, 0.76%)tokio::runtime::task::core::CoreStage<T>::poll (1 samples, 0.76%)tokio::loom::std::unsafe_cell::UnsafeCell<T>::with_mut (1 samples, 0.76%)tokio::runtime::task::core::CoreStage<T>::poll::{{closure}} (1 samples, 0.76%)<tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Launch::launch::{{closure}} (1 samples, 0.76%)tokio::runtime::thread_pool::worker::run (1 samples, 0.76%)tokio::macros::scoped_tls::ScopedKey<T>::set (1 samples, 0.76%)tokio::runtime::thread_pool::worker::run::{{closure}} (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Context::run (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Context::park (1 samples, 0.76%)tokio::runtime::thread_pool::worker::Context::park_timeout (1 samples, 0.76%)<tokio::runtime::thread_pool::park::Parker as tokio::park::Park>::park (1 samples, 0.76%)tokio::runtime::thread_pool::park::Inner::park (1 samples, 0.76%)tokio::runtime::thread_pool::park::Inner::park_driver (1 samples, 0.76%)<tokio::runtime::driver::Driver as tokio::park::Park>::park (1 samples, 0.76%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park (1 samples, 0.76%)<tokio::time::driver::Driver<P> as tokio::park::Park>::park (1 samples, 0.76%)tokio::time::driver::Driver<P>::park_internal (1 samples, 0.76%)<tokio::park::either::Either<A,B> as tokio::park::Park>::park (1 samples, 0.76%)<tokio::process::imp::driver::Driver as tokio::park::Park>::park (1 samples, 0.76%)<tokio::signal::unix::driver::Driver as tokio::park::Park>::park (1 samples, 0.76%)<tokio::io::driver::Driver as tokio::park::Park>::park (1 samples, 0.76%)tokio::io::driver::Driver::turn (1 samples, 0.76%)tokio::io::driver::Driver::dispatch (1 samples, 0.76%)tokio::io::driver::scheduled_io::ScheduledIo::wake (1 samples, 0.76%)tokio::io::driver::scheduled_io::ScheduledIo::wake0 (1 samples, 0.76%)tokio::util::wake_list::WakeList::new (1 samples, 0.76%)core::mem::maybe_uninit::MaybeUninit<T>::assume_init (1 samples, 0.76%)core::mem::manually_drop::ManuallyDrop<T>::into_inner (1 samples, 0.76%)__memcpy_avx_unaligned_erms (1 samples, 0.76%) \ No newline at end of file diff --git a/roles/mining-proxy/perf.data b/roles/mining-proxy/perf.data deleted file mode 100644 index 149ca1256e146200339815c51e239755c5bcac26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1138556 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 diff --git a/roles/mining-proxy/perf.data.old b/roles/mining-proxy/perf.data.old deleted file mode 100644 index 714684477dbfb763e3428ee1bdb03a34b8ab8bf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 741540 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
  • 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 Result { - // Parse CLI arguments - let args = Args::parse(); - - // Build configuration from the provided file path - let config_path = args.config_path.to_str().ok_or_else(|| { - error!("Invalid configuration path."); - Error::BadCliArgs - })?; - - let settings = Config::builder() - .add_source(File::new(config_path, FileFormat::Toml)) - .build() - .map_err(|e| { - error!("Failed to build config: {}", e); - Error::BadCliArgs - })?; - - // Deserialize settings into MiningProxyConfig - let config = settings - .try_deserialize::() - .map_err(|e| { - error!("Failed to deserialize config: {}", e); - Error::BadCliArgs - })?; - Ok(config) -} diff --git a/roles/mining-proxy/src/lib/downstream_mining.rs b/roles/mining-proxy/src/lib/downstream_mining.rs deleted file mode 100644 index 9940aa878a..0000000000 --- a/roles/mining-proxy/src/lib/downstream_mining.rs +++ /dev/null @@ -1,529 +0,0 @@ -use std::{convert::TryInto, sync::Arc}; - -use async_channel::{Receiver, SendError, Sender}; -use tokio::{net::TcpListener, sync::oneshot::Receiver as TokioReceiver}; -use tracing::{debug, info, trace, warn}; - -use super::{ - routing_logic::{CommonRouter, CommonRoutingLogic, MiningRouter, MiningRoutingLogic}, - upstream_mining::{StdFrame as UpstreamFrame, UpstreamMiningNode}, -}; -use stratum_common::{ - network_helpers_sv2::plain_connection::PlainConnection, - roles_logic_sv2::{ - self, codec_sv2, - codec_sv2::{binary_sv2, StandardEitherFrame, StandardSv2Frame}, - common_messages_sv2::{SetupConnection, SetupConnectionSuccess}, - common_properties::{CommonDownstreamData, IsDownstream, IsMiningDownstream}, - errors::Error, - handlers::{ - common::{ParseCommonMessagesFromDownstream, SendTo as SendToCommon}, - mining::{ParseMiningMessagesFromDownstream, SendTo, SupportedChannelTypes}, - }, - mining_sv2::*, - parsers::{AnyMessage, Mining, MiningDeviceMessages}, - utils::Mutex, - }, -}; - -pub type Message = MiningDeviceMessages<'static>; -pub type StdFrame = StandardSv2Frame; -pub type EitherFrame = StandardEitherFrame; - -/// 1 to 1 connection with a downstream node that implement the mining (sub)protocol can be either -/// a mining device or a downstream proxy. -/// A downstream can only be linked with an upstream at a time. Support multi upstreams for -/// downstream do not make much sense. -#[derive(Debug, Clone)] -pub struct DownstreamMiningNode { - id: u32, - receiver: Receiver, - sender: Sender, - pub status: DownstreamMiningNodeStatus, - upstream: Option>>, -} - -#[derive(Debug, Clone)] -pub enum DownstreamMiningNodeStatus { - Initializing, - Paired(CommonDownstreamData), - ChannelOpened(Channel), -} - -#[derive(Debug, Clone)] -#[allow(clippy::enum_variant_names)] -pub enum Channel { - DownstreamHomUpstreamGroup { - data: CommonDownstreamData, - channel_id: u32, - group_id: u32, - }, - DownstreamHomUpstreamExtended { - data: CommonDownstreamData, - channel_id: u32, - }, -} - -impl DownstreamMiningNodeStatus { - fn is_paired(&self) -> bool { - match self { - DownstreamMiningNodeStatus::Initializing => false, - DownstreamMiningNodeStatus::Paired(_) => true, - DownstreamMiningNodeStatus::ChannelOpened(_) => true, - } - } - - fn pair(&mut self, data: CommonDownstreamData) { - match self { - DownstreamMiningNodeStatus::Initializing => { - let self_ = Self::Paired(data); - let _ = std::mem::replace(self, self_); - } - _ => panic!("Try to pair an already paired downstream"), - } - } - - pub fn get_channel(&mut self) -> &mut Channel { - match self { - DownstreamMiningNodeStatus::Initializing => { - panic!("Downstream is not initialized no channle opened yet") - } - DownstreamMiningNodeStatus::Paired(_channels) => { - panic!("Downstream is paired but not channle opened yet") - } - DownstreamMiningNodeStatus::ChannelOpened(k) => k, - } - } - - fn open_channel_for_down_hom_up_group(&mut self, channel_id: u32, group_id: u32) { - match self { - DownstreamMiningNodeStatus::Initializing => panic!(), - DownstreamMiningNodeStatus::Paired(data) => { - let channel = Channel::DownstreamHomUpstreamGroup { - data: *data, - channel_id, - group_id, - }; - let self_ = Self::ChannelOpened(channel); - let _ = std::mem::replace(self, self_); - } - DownstreamMiningNodeStatus::ChannelOpened(..) => panic!("Channel already opened"), - } - } - - fn open_channel_for_down_hom_up_extended(&mut self, channel_id: u32, _group_id: u32) { - match self { - DownstreamMiningNodeStatus::Initializing => panic!(), - DownstreamMiningNodeStatus::Paired(data) => { - let channel = Channel::DownstreamHomUpstreamExtended { - data: *data, - channel_id, - }; - let self_ = Self::ChannelOpened(channel); - let _ = std::mem::replace(self, self_); - } - DownstreamMiningNodeStatus::ChannelOpened(..) => panic!("Channel already opened"), - } - } -} - -impl PartialEq for DownstreamMiningNode { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl DownstreamMiningNode { - /// Return mining channel specific data - pub fn get_channel(&mut self) -> &mut Channel { - self.status.get_channel() - } - - pub fn open_channel_for_down_hom_up_group(&mut self, channel_id: u32, group_id: u32) { - self.status - .open_channel_for_down_hom_up_group(channel_id, group_id); - } - pub fn open_channel_for_down_hom_up_extended(&mut self, channel_id: u32, group_id: u32) { - self.status - .open_channel_for_down_hom_up_extended(channel_id, group_id); - } - - pub fn new(receiver: Receiver, sender: Sender, id: u32) -> Self { - Self { - receiver, - sender, - status: DownstreamMiningNodeStatus::Initializing, - upstream: None, - id, - } - } - - /// Send SetupConnectionSuccess to donwstream and start processing new messages coming from - /// downstream - pub async fn start( - self_mutex: Arc>, - setup_connection_success: SetupConnectionSuccess, - ) { - if self_mutex - .safe_lock(|self_| self_.status.is_paired()) - .unwrap() - { - let setup_connection_success: MiningDeviceMessages = setup_connection_success.into(); - - { - DownstreamMiningNode::send( - self_mutex.clone(), - setup_connection_success.try_into().unwrap(), - ) - .await - .unwrap(); - } - let receiver = self_mutex - .safe_lock(|self_| self_.receiver.clone()) - .unwrap(); - - while let Ok(message) = receiver.recv().await { - let incoming: StdFrame = message.try_into().unwrap(); - Self::next(self_mutex.clone(), incoming).await; - } - Self::exit(self_mutex); - } else { - panic!() - } - } - - /// Parse the received message and relay it to the right upstream - pub async fn next(self_mutex: Arc>, mut incoming: StdFrame) { - let message_type = incoming.get_header().unwrap().msg_type(); - let payload = incoming.payload(); - - let next_message_to_send = ParseMiningMessagesFromDownstream::handle_message_mining( - self_mutex.clone(), - message_type, - payload, - ); - - match next_message_to_send { - Ok(SendTo::RelaySameMessageToRemote(upstream_mutex)) => { - let sv2_frame: codec_sv2::Sv2Frame = - incoming.map(|payload| payload.try_into().unwrap()); - UpstreamMiningNode::send(upstream_mutex.clone(), sv2_frame) - .await - .unwrap(); - } - Ok(SendTo::RelayNewMessageToRemote(upstream_mutex, message)) => { - let message = AnyMessage::Mining(message); - let frame: UpstreamFrame = message.try_into().unwrap(); - UpstreamMiningNode::send(upstream_mutex.clone(), frame) - .await - .unwrap(); - } - Ok(SendTo::Respond(message)) => { - let message = MiningDeviceMessages::Mining(message); - let frame: StdFrame = message.try_into().unwrap(); - DownstreamMiningNode::send(self_mutex.clone(), frame) - .await - .unwrap(); - } - Ok(SendTo::Multiple(sends_to)) => { - for message in sends_to { - match message { - roles_logic_sv2::handlers::SendTo_::Respond(m) => match m { - Mining::NewMiningJob(_) => { - let message = MiningDeviceMessages::Mining(m); - let frame: StdFrame = message.try_into().unwrap(); - DownstreamMiningNode::send(self_mutex.clone(), frame) - .await - .unwrap(); - } - Mining::OpenStandardMiningChannelSuccess(_) => { - let message = MiningDeviceMessages::Mining(m); - let frame: StdFrame = message.try_into().unwrap(); - DownstreamMiningNode::send(self_mutex.clone(), frame) - .await - .unwrap(); - } - Mining::SetNewPrevHash(_) => { - let message = MiningDeviceMessages::Mining(m); - let frame: StdFrame = message.try_into().unwrap(); - DownstreamMiningNode::send(self_mutex.clone(), frame) - .await - .unwrap(); - } - m => panic!("{:?}", m), - }, - m => panic!("{:?}", m), - } - } - } - Ok(SendTo::None(_)) => (), - Ok(_) => panic!(), - Err(_) => todo!(), - } - } - - /// Send a message downstream - pub async fn send( - self_mutex: Arc>, - sv2_frame: StdFrame, - ) -> Result<(), SendError> { - let either_frame = sv2_frame.into(); - let sender = self_mutex.safe_lock(|self_| self_.sender.clone()).unwrap(); - match sender.send(either_frame).await { - Ok(_) => Ok(()), - Err(_) => { - todo!() - } - } - } - - pub fn exit(self_: Arc>) { - if let Some(up) = self_.safe_lock(|s| s.upstream.clone()).unwrap() { - UpstreamMiningNode::remove_dowstream(up, &self_); - }; - self_ - .safe_lock(|s| { - s.receiver.close(); - }) - .unwrap(); - } -} - -/// It impl UpstreamMining cause the proxy act as an upstream node for the DownstreamMiningNode -impl ParseMiningMessagesFromDownstream for DownstreamMiningNode { - fn get_channel_type(&self) -> SupportedChannelTypes { - SupportedChannelTypes::Group - } - - fn is_work_selection_enabled(&self) -> bool { - false - } - - fn is_downstream_authorized( - _self_mutex: Arc>, - _user_identity: &binary_sv2::Str0255, - ) -> Result { - Ok(true) - } - - fn handle_open_standard_mining_channel( - &mut self, - req: OpenStandardMiningChannel, - ) -> Result, Error> { - info!( - "Received OpenStandardMiningChannel from: {} with id: {}", - std::str::from_utf8(req.user_identity.as_ref()).unwrap_or("Unknown identity"), - req.get_request_id_as_u32() - ); - debug!("OpenStandardMiningChannel: {}", req); - let downstream_mining_data = self.get_downstream_mining_data(); - let routing_logic = super::get_routing_logic(); - - let upstream = match routing_logic { - MiningRoutingLogic::Proxy(r_logic) => { - trace!("On OpenStandardMiningChannel r_logic is: {:?}", r_logic); - let up = r_logic - .safe_lock(|r_logic| { - r_logic.on_open_standard_channel( - Arc::new(Mutex::new(self.clone())), - &mut req.clone(), - &downstream_mining_data, - ) - })?; - trace!("On OpenStandardMiningChannel best candidate is: {:?}", up); - Some(up?) - } - // Variant just used for phantom data is ok to panic - MiningRoutingLogic::_P(_) => panic!("Must use either MiningRoutingLogic::None or MiningRoutingLogic::Proxy for `routing_logic` param"), - _ => unreachable!() - }; - - let channel_id = upstream - .as_ref() - .expect("No upstream initialized") - .safe_lock(|s| s.channel_ids.safe_lock(|r| r.next()).unwrap()) - .unwrap(); - let cloned = upstream.as_ref().expect("No upstream initialized").clone(); - - upstream - .as_ref() - .expect("No upstream initialized") - .safe_lock(|up| { - if up.channel_kind.is_extended() { - let messages = up.open_standard_channel_down( - req.request_id.as_u32(), - req.nominal_hash_rate, - true, - channel_id, - ); - for m in &messages { - if let Mining::OpenStandardMiningChannelSuccess(m) = m { - self.open_channel_for_down_hom_up_extended( - m.channel_id, - m.group_channel_id, - ); - } - } - let messages = messages.into_iter().map(SendTo::Respond).collect(); - Ok(SendTo::Multiple(messages)) - } else { - Ok(SendTo::RelaySameMessageToRemote(cloned)) - } - }) - .unwrap() - } - - fn handle_open_extended_mining_channel( - &mut self, - _: OpenExtendedMiningChannel, - ) -> Result, Error> { - todo!() - } - - fn handle_update_channel( - &mut self, - _: UpdateChannel, - ) -> Result, Error> { - todo!() - } - - fn handle_submit_shares_standard( - &mut self, - m: SubmitSharesStandard, - ) -> Result, Error> { - info!("Received SubmitSharesStandard"); - debug!("SubmitSharesStandard {}", m); - // TODO maybe we want to check if shares meet target before - // sending them upstream If that is the case it should be - // done by GroupChannel not here - match &self.status { - DownstreamMiningNodeStatus::Initializing => todo!(), - DownstreamMiningNodeStatus::Paired(_) => todo!(), - DownstreamMiningNodeStatus::ChannelOpened(Channel::DownstreamHomUpstreamGroup { - .. - }) => { - let remote = self.upstream.as_ref().unwrap(); - let message = Mining::SubmitSharesStandard(m); - Ok(SendTo::RelayNewMessageToRemote(remote.clone(), message)) - } - DownstreamMiningNodeStatus::ChannelOpened(Channel::DownstreamHomUpstreamExtended { - .. - }) => { - // Safe unwrap is channel have been opened it means that the dowsntream is paired - // with an upstream - let remote = self.upstream.as_ref().unwrap(); - let res = UpstreamMiningNode::handle_std_shr(remote.clone(), m).unwrap(); - Ok(SendTo::Respond(res)) - } - } - } - - fn handle_submit_shares_extended( - &mut self, - _: SubmitSharesExtended, - ) -> Result, Error> { - todo!() - } - - fn handle_set_custom_mining_job( - &mut self, - _: SetCustomMiningJob, - ) -> Result, Error> { - todo!() - } -} - -impl ParseCommonMessagesFromDownstream for DownstreamMiningNode { - fn handle_setup_connection( - &mut self, - m: SetupConnection, - ) -> Result { - info!( - "Received `SetupConnection`: version={}, flags={:b}", - m.min_version, m.flags - ); - let routing_logic = super::get_common_routing_logic(); - match routing_logic { - CommonRoutingLogic::Proxy(r_logic) => { - trace!("On SetupConnection r_logic is {:?}", r_logic); - let result = r_logic.safe_lock(|r_logic| r_logic.on_setup_connection(&m))?; - let (data, message) = result?; - let upstream = match super::get_routing_logic() { - MiningRoutingLogic::Proxy(proxy_routing) => proxy_routing - .safe_lock(|r| r.downstream_to_upstream_map.get(&data).unwrap()[0].clone()) - .unwrap(), - _ => unreachable!(), - }; - self.upstream = Some(upstream); - - self.status.pair(data); - Ok(SendToCommon::RelayNewMessageToRemote( - Arc::new(Mutex::new(())), - message.into(), - )) - } - _ => unreachable!(), - } - } -} - -pub async fn listen_for_downstream_mining( - listener: TcpListener, - mut shutdown_rx: TokioReceiver<()>, -) { - let mut ids = roles_logic_sv2::utils::Id::new(); - loop { - tokio::select! { - accept_result = listener.accept() => { - let (stream, _) = accept_result.expect("failed to accept downstream connection"); - let (receiver, sender): (Receiver, Sender) = - PlainConnection::new(stream).await; - let node = DownstreamMiningNode::new(receiver, sender, ids.next()); - - let mut incoming: StdFrame = - node.receiver.recv().await.unwrap().try_into().unwrap(); - let message_type = incoming.get_header().unwrap().msg_type(); - let payload = incoming.payload(); - let node = Arc::new(Mutex::new(node)); - - // Call handle_setup_connection or fail - let common_msg = DownstreamMiningNode::handle_message_common( - node.clone(), - message_type, - payload, - ).expect("failed to process downstream message"); - - - if let SendToCommon::RelayNewMessageToRemote(_, relay_msg) = common_msg { - if let roles_logic_sv2::parsers::CommonMessages::SetupConnectionSuccess(setup_msg) = relay_msg { - DownstreamMiningNode::start(node, setup_msg).await; - } - } else { - warn!("Received unexpected message from downstream"); - } - } - _ = &mut shutdown_rx => { - info!("Closing listener"); - return; - } - } - } -} - -impl IsDownstream for DownstreamMiningNode { - fn get_downstream_mining_data(&self) -> CommonDownstreamData { - match self.status { - DownstreamMiningNodeStatus::Initializing => panic!(), - DownstreamMiningNodeStatus::Paired(data) => data, - DownstreamMiningNodeStatus::ChannelOpened(Channel::DownstreamHomUpstreamGroup { - data, - .. - }) => data, - DownstreamMiningNodeStatus::ChannelOpened(Channel::DownstreamHomUpstreamExtended { - data, - .. - }) => data, - } - } -} -impl IsMiningDownstream for DownstreamMiningNode {} diff --git a/roles/mining-proxy/src/lib/error.rs b/roles/mining-proxy/src/lib/error.rs deleted file mode 100644 index e385fa4cdd..0000000000 --- a/roles/mining-proxy/src/lib/error.rs +++ /dev/null @@ -1,35 +0,0 @@ -use async_channel::SendError; -use core::fmt; -use std::net::SocketAddr; -use stratum_common::roles_logic_sv2::{codec_sv2::StandardEitherFrame, parsers::AnyMessage}; - -pub type Message = AnyMessage<'static>; -pub type EitherFrame = StandardEitherFrame; - -#[derive(Debug)] -#[allow(clippy::large_enum_variant)] -#[allow(clippy::enum_variant_names)] -#[allow(dead_code)] -pub enum Error { - SendError(SendError), - UpstreamNotAvailabe(SocketAddr), - SetupConnectionError(String), - BadCliArgs, -} - -impl From> for Error { - fn from(error: SendError) -> Self { - Error::SendError(error) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::SendError(e) => write!(f, "Send error: {e}"), - Error::UpstreamNotAvailabe(addr) => write!(f, "Upstream not available: {addr}"), - Error::SetupConnectionError(msg) => write!(f, "Setup connection error: {msg}"), - Error::BadCliArgs => write!(f, "Bad CLI arguments provided"), - } - } -} diff --git a/roles/mining-proxy/src/lib/mod.rs b/roles/mining-proxy/src/lib/mod.rs deleted file mode 100644 index bcfee738a4..0000000000 --- a/roles/mining-proxy/src/lib/mod.rs +++ /dev/null @@ -1,191 +0,0 @@ -pub mod downstream_mining; -pub mod error; -pub mod routing_logic; -pub mod selectors; -pub mod upstream_mining; - -use once_cell::sync::OnceCell; -use routing_logic::{CommonRoutingLogic, MiningProxyRoutingLogic, MiningRoutingLogic}; -use selectors::GeneralMiningSelector; -use serde::Deserialize; -use std::{net::SocketAddr, sync::Arc}; -use stratum_common::roles_logic_sv2::utils::{GroupId, Id, Mutex}; -use tokio::{net::TcpListener, sync::oneshot}; -use tracing::info; -use upstream_mining::UpstreamMiningNode; - -type RLogic = MiningProxyRoutingLogic< - downstream_mining::DownstreamMiningNode, - upstream_mining::UpstreamMiningNode, - upstream_mining::ProxyRemoteSelector, ->; - -/// Panic whene we are looking one of this 2 global mutex would force the proxy to go down as every -/// part of the program depend on them. -/// SAFTEY note: we use global mutable memory instead of a dedicated struct that use a dedicated -/// task to change the mutable state and communicate with the other parts of the program via -/// messages cause it is impossible for a task to panic while is using one of the two below Mutex. -/// So it make sense to use shared mutable memory to lower the complexity of the codebase and to -/// have some performance gain. -pub static ROUTING_LOGIC: OnceCell> = OnceCell::new(); -static MIN_EXTRANONCE_SIZE: u16 = 6; -static EXTRANONCE_RANGE_1_LENGTH: usize = 4; - -pub async fn initialize_upstreams(min_version: u16, max_version: u16) { - let upstreams = ROUTING_LOGIC - .get() - .expect("BUG: ROUTING_LOGIC has not been set yet") - .safe_lock(|r_logic| r_logic.upstream_selector.upstreams.clone()) - .unwrap(); - let available_upstreams = upstream_mining::scan(upstreams, min_version, max_version).await; - ROUTING_LOGIC - .get() - .unwrap() - .safe_lock(|rl| rl.upstream_selector.update_upstreams(available_upstreams)) - .unwrap(); -} - -fn remove_upstream(id: u32) { - let upstreams = ROUTING_LOGIC - .get() - .expect("BUG: ROUTING_LOGIC has not been set yet") - .safe_lock(|r_logic| r_logic.upstream_selector.upstreams.clone()) - .unwrap(); - let mut updated_upstreams = vec![]; - for upstream in upstreams { - if upstream.safe_lock(|s| s.get_id()).unwrap() != id { - updated_upstreams.push(upstream) - } - } - ROUTING_LOGIC - .get() - .unwrap() - .safe_lock(|rl| rl.upstream_selector.update_upstreams(updated_upstreams)) - .unwrap(); -} - -pub fn get_routing_logic() -> MiningRoutingLogic< - downstream_mining::DownstreamMiningNode, - upstream_mining::UpstreamMiningNode, - upstream_mining::ProxyRemoteSelector, - RLogic, -> { - MiningRoutingLogic::Proxy( - ROUTING_LOGIC - .get() - .expect("BUG: ROUTING_LOGIC was not set yet"), - ) -} -pub fn get_common_routing_logic() -> CommonRoutingLogic { - CommonRoutingLogic::Proxy( - ROUTING_LOGIC - .get() - .expect("BUG: ROUTING_LOGIC was not set yet"), - ) -} - -#[derive(Debug, Deserialize, Clone)] -pub struct UpstreamMiningValues { - pub address: String, - pub port: u16, - pub pub_key: key_utils::Secp256k1PublicKey, - pub channel_kind: ChannelKind, -} - -#[derive(Debug, Deserialize, Clone, Copy)] -pub enum ChannelKind { - Group, - Extended, -} - -#[derive(Debug, Deserialize, Clone)] -pub struct MiningProxyConfig { - pub upstreams: Vec, - pub listen_address: String, - pub listen_mining_port: u16, - pub max_supported_version: u16, - pub min_supported_version: u16, - pub downstream_share_per_minute: f32, - pub expected_total_downstream_hr: f32, - pub reconnect: bool, -} -pub async fn initialize_r_logic( - upstreams: &[UpstreamMiningValues], - group_id: Arc>, - config: MiningProxyConfig, -) -> RLogic { - let channel_ids = Arc::new(Mutex::new(Id::new())); - let mut upstream_mining_nodes = Vec::with_capacity(upstreams.len()); - for (index, upstream_) in upstreams.iter().enumerate() { - let socket = SocketAddr::new(upstream_.address.parse().unwrap(), upstream_.port); - - let upstream = Arc::new(Mutex::new(UpstreamMiningNode::new( - index as u32, - socket, - upstream_.pub_key.into_bytes(), - upstream_.channel_kind, - group_id.clone(), - channel_ids.clone(), - config.downstream_share_per_minute, - None, - None, - config.expected_total_downstream_hr, - config.reconnect, - ))); - - match upstream_.channel_kind { - ChannelKind::Group => (), - ChannelKind::Extended => (), - } - - upstream_mining_nodes.push(upstream); - } - let upstream_selector = GeneralMiningSelector::new(upstream_mining_nodes); - MiningProxyRoutingLogic { - upstream_selector, - downstream_id_generator: Id::new(), - downstream_to_upstream_map: std::collections::HashMap::new(), - } -} - -pub async fn start_mining_proxy(config: MiningProxyConfig) { - let group_id = Arc::new(Mutex::new(GroupId::new())); - ROUTING_LOGIC - .set(Mutex::new( - initialize_r_logic(&config.upstreams, group_id, config.clone()).await, - )) - .expect("BUG: Failed to set ROUTING_LOGIC"); - - info!("Initializing upstream scanner"); - initialize_upstreams(config.min_supported_version, config.max_supported_version).await; - info!("Initializing downstream listener"); - - let socket = SocketAddr::new( - config.listen_address.parse().unwrap(), - config.listen_mining_port, - ); - let listener = TcpListener::bind(socket).await.unwrap(); - - info!("Listening for downstream mining connections on {}", socket); - - let (shutdown_tx, shutdown_rx) = oneshot::channel(); - - let (_, res) = tokio::join!( - // Wait for downstream connection - downstream_mining::listen_for_downstream_mining(listener, shutdown_rx), - // handle SIGTERM/QUIT / ctrl+c - tokio::spawn(async { - tokio::signal::ctrl_c() - .await - .expect("Failed to listen to signals"); - let _ = shutdown_tx.send(()); - info!("Interrupt received"); - }) - ); - - if let Err(e) = res { - panic!("Failed to wait for clean exit: {:?}", e); - } - - info!("Shutdown done"); -} diff --git a/roles/mining-proxy/src/lib/routing_logic.rs b/roles/mining-proxy/src/lib/routing_logic.rs deleted file mode 100644 index 4aee83dcb8..0000000000 --- a/roles/mining-proxy/src/lib/routing_logic.rs +++ /dev/null @@ -1,422 +0,0 @@ -//! # Routing Logic -//! -//! This module contains the routing logic used by handlers to determine where a message should be -//! relayed or responded to. -//! -//! The routing logic defines a set of traits and structures to manage message routing in Stratum -//! V2. The following components are included: -//! -//! - **`CommonRouter`**: Trait implemented by routers for the common (sub)protocol. -//! - **`MiningRouter`**: Trait implemented by routers for the mining (sub)protocol. -//! - **`CommonRoutingLogic`**: Enum defining the various routing logic for the common protocol -//! (e.g., Proxy, None). -//! - **`MiningRoutingLogic`**: Enum defining the routing logic for the mining protocol (e.g., -//! Proxy, None). -//! - **`NoRouting`**: Marker router that implements both `CommonRouter` and `MiningRouter` for -//! cases where no routing logic is required. -//! - **`MiningProxyRoutingLogic`**: Routing logic valid for a standard Sv2 mining proxy, -//! implementing both `CommonRouter` and `MiningRouter`. -//! -//! ## Future Work -//! -//! - Consider hiding all traits from the public API and exporting only marker traits. -//! - Improve upstream selection logic to be configurable by the caller. - -use super::{ - downstream_mining::DownstreamMiningNode, - selectors::{ - DownstreamMiningSelector, GeneralMiningSelector, NullDownstreamMiningSelector, - UpstreamMiningSelctor, - }, - upstream_mining::HasDownstreamSelector, -}; -use std::{collections::HashMap, fmt::Debug as D, marker::PhantomData, sync::Arc}; -use stratum_common::roles_logic_sv2::{ - common_messages_sv2::{ - has_requires_std_job, Protocol, SetupConnection, SetupConnectionSuccess, - }, - common_properties::{ - CommonDownstreamData, IsDownstream, IsMiningDownstream, IsMiningUpstream, IsUpstream, - PairSettings, - }, - mining_sv2::{OpenStandardMiningChannel, OpenStandardMiningChannelSuccess}, - utils::{Id, Mutex}, - Error, -}; - -/// Defines routing logic for common protocol messages. -/// -/// Implemented by handlers (such as -/// [`roles_logic_sv2::handlers::common::ParseCommonMessagesFromUpstream`] -/// and [`roles_logic_sv2::handlers::common::ParseCommonMessagesFromDownstream`]) to determine the -/// behavior for common protocol routing. -pub trait CommonRouter: std::fmt::Debug { - /// Handles a `SetupConnection` message for the common protocol. - fn on_setup_connection( - &mut self, - message: &SetupConnection, - ) -> Result<(CommonDownstreamData, SetupConnectionSuccess), Error>; -} - -/// Defines routing logic for mining protocol messages. -/// -/// Implemented by handlers (such as -/// [`roles_logic_sv2::handlers::mining::ParseMiningMessagesFromUpstream`] -/// and [`roles_logic_sv2::handlers::mining::ParseMiningMessagesFromDownstream`]) to determine the -/// behavior for mining protocol routing. This trait extends [`CommonRouter`] to handle -/// mining-specific routing logic. -pub trait MiningRouter< - Down: IsMiningDownstream, - Up: IsMiningUpstream + HasDownstreamSelector, - Sel: DownstreamMiningSelector, ->: CommonRouter -{ - /// Handles an `OpenStandardMiningChannel` message from a downstream. - fn on_open_standard_channel( - &mut self, - downstream: Arc>, - request: &mut OpenStandardMiningChannel, - downstream_mining_data: &CommonDownstreamData, - ) -> Result>, Error>; - - /// Handles an `OpenStandardMiningChannelSuccess` message from an upstream. - fn on_open_standard_channel_success( - &mut self, - upstream: &mut Up, - request: &mut OpenStandardMiningChannelSuccess, - ) -> Result>, Error>; -} - -/// A no-operation router for scenarios where no routing logic is needed. -/// -/// Implements both `CommonRouter` and `MiningRouter` but panics if invoked. -#[derive(Debug)] -pub struct NoRouting(); - -impl CommonRouter for NoRouting { - fn on_setup_connection( - &mut self, - _: &SetupConnection, - ) -> Result<(CommonDownstreamData, SetupConnectionSuccess), Error> { - unreachable!() - } -} - -impl - MiningRouter for NoRouting -{ - fn on_open_standard_channel( - &mut self, - _downstream: Arc>, - _request: &mut OpenStandardMiningChannel, - _downstream_mining_data: &CommonDownstreamData, - ) -> Result>, Error> { - unreachable!() - } - - fn on_open_standard_channel_success( - &mut self, - _upstream: &mut Up, - _request: &mut OpenStandardMiningChannelSuccess, - ) -> Result>, Error> { - unreachable!() - } -} - -/// Routing logic options for the common protocol. -#[derive(Debug)] -pub enum CommonRoutingLogic { - /// Proxy routing logic for the common protocol. - Proxy(&'static Mutex), - /// No routing logic. - None, -} - -/// Routing logic options for the mining protocol. -#[derive(Debug)] -pub enum MiningRoutingLogic< - Down: IsMiningDownstream + D, - Up: IsMiningUpstream + D + HasDownstreamSelector, - Sel: DownstreamMiningSelector + D, - Router: 'static + MiningRouter, -> { - /// Proxy routing logic for the mining protocol. - Proxy(&'static Mutex), - /// No routing logic. - None, - /// Marker for the generic parameters. - _P(PhantomData<(Down, Up, Sel)>), -} - -impl Clone for CommonRoutingLogic { - fn clone(&self) -> Self { - match self { - Self::None => Self::None, - Self::Proxy(x) => Self::Proxy(x), - } - } -} - -impl< - Down: IsMiningDownstream + D, - Up: IsMiningUpstream + D + HasDownstreamSelector, - Sel: DownstreamMiningSelector + D, - Router: MiningRouter, - > Clone for MiningRoutingLogic -{ - fn clone(&self) -> Self { - match self { - Self::None => Self::None, - Self::Proxy(x) => Self::Proxy(x), - // Variant used only for PhantomData safe to panic here - Self::_P(_) => panic!(), - } - } -} - -/// Routing logic for a standard Sv2 mining proxy. -#[derive(Debug)] -pub struct MiningProxyRoutingLogic< - Down: IsMiningDownstream + D, - Up: IsMiningUpstream + D, - Sel: DownstreamMiningSelector + D, -> { - /// Selector for upstream entities. - pub upstream_selector: GeneralMiningSelector, - /// ID generator for downstream entities. - pub downstream_id_generator: Id, - /// Mapping from downstream to upstream entities. - pub downstream_to_upstream_map: HashMap>>>, -} - -impl< - Down: IsMiningDownstream + D, - Up: IsMiningUpstream + D + HasDownstreamSelector, - Sel: DownstreamMiningSelector + D, - > CommonRouter for MiningProxyRoutingLogic -{ - /// Handles the `SetupConnection` message. - /// - /// This method initializes the connection between a downstream and an upstream by determining - /// the appropriate upstream based on the provided protocol, versions, and flags. - fn on_setup_connection( - &mut self, - message: &SetupConnection, - ) -> Result<(CommonDownstreamData, SetupConnectionSuccess), Error> { - let protocol = message.protocol; - let min_v = message.min_version; - let max_v = message.max_version; - let flags = message.flags; - let pair_settings = PairSettings { - protocol, - min_v, - max_v, - flags, - }; - let header_only = has_requires_std_job(pair_settings.flags); - match (protocol, header_only) { - (Protocol::MiningProtocol, true) => { - self.on_setup_connection_mining_header_only(&pair_settings) - } - // TODO: Add handler for other protocols. - _ => Err(Error::UnimplementedProtocol), - } - } -} - -impl< - Up: IsMiningUpstream + D + HasDownstreamSelector, - Sel: DownstreamMiningSelector + D, - > MiningRouter - for MiningProxyRoutingLogic -{ - // Handles the `OpenStandardMiningChannel` message. - // - // This method processes the request to open a standard mining channel. It selects a suitable - // upstream, updates the request ID to ensure uniqueness, and then delegates to - // `on_open_standard_channel_request_header_only` to finalize the process. - fn on_open_standard_channel( - &mut self, - downstream: Arc>, - request: &mut OpenStandardMiningChannel, - downstream_mining_data: &CommonDownstreamData, - ) -> Result>, Error> { - let upstreams = self - .downstream_to_upstream_map - .get(downstream_mining_data) - .ok_or(Error::NoCompatibleUpstream(*downstream_mining_data))?; - // If we are here, a list of possible upstreams has already been selected. - // TODO: The upstream selection logic should be specified by the caller. - let upstream = - Self::select_upstreams(&mut upstreams.to_vec()).ok_or(Error::NoUpstreamsConnected)?; - let old_id = request.get_request_id_as_u32(); - let new_req_id = upstream.safe_lock(|u| u.get_mapper().unwrap().on_open_channel(old_id))?; - request.update_id(new_req_id); - self.on_open_standard_channel_request_header_only(downstream, request) - } - - // Handles the `OpenStandardMiningChannelSuccess` message. - // - // This method processes the success message received from an upstream when a standard mining - // channel is opened. It maps the request ID back to the original ID from the downstream and - // updates the associated group and channel IDs in the upstream. - fn on_open_standard_channel_success( - &mut self, - upstream: &mut Up, - request: &mut OpenStandardMiningChannelSuccess, - ) -> Result>, Error> - where - Up: IsUpstream + HasDownstreamSelector, - { - let upstream_request_id = request.get_request_id_as_u32(); - let original_request_id = upstream - .get_mapper() - .ok_or(Error::RequestIdNotMapped(upstream_request_id))? - .remove(upstream_request_id) - .ok_or(Error::RequestIdNotMapped(upstream_request_id)); - - request.update_id(original_request_id?); - - let selector = upstream.get_remote_selector(); - selector.on_open_standard_channel_success( - upstream_request_id, - request.group_channel_id, - request.channel_id, - ) - } -} - -// Selects the upstream with the lowest total hash rate. -// # Panics -// This function panics if the slice is empty, as it is internally guaranteed that this function -// will only be called with non-empty vectors. -fn minor_total_hr_upstream(ups: &mut [Arc>]) -> Arc> -where - Up: IsMiningUpstream + D, -{ - ups.iter_mut() - .reduce(|acc, item| { - // Safely locks and compares the total hash rate of each upstream. - if acc.safe_lock(|x| x.total_hash_rate()).unwrap() - < item.safe_lock(|x| x.total_hash_rate()).unwrap() - { - acc - } else { - item - } - }) - .unwrap() - .clone() // Unwrap is safe because the function only operates on non-empty vectors. -} - -// Filters upstream entities that are not configured for header-only mining. -fn filter_header_only(ups: &mut [Arc>]) -> Vec>> -where - Up: IsMiningUpstream + D, -{ - ups.iter() - .filter(|up_mutex| { - up_mutex - .safe_lock(|up| !up.is_header_only()) - .unwrap_or_default() - }) - .cloned() - .collect() -} - -// Selects the most appropriate upstream entity based on specific criteria. -// -// # Criteria -// - If only one upstream is available, it is selected. -// - If multiple upstreams exist, preference is given to those not configured as header-only. -// - Among the remaining upstreams, the one with the lowest total hash rate is selected. -fn select_upstream(ups: &mut [Arc>]) -> Option>> -where - Up: IsMiningUpstream + D, -{ - if ups.is_empty() { - None - } else if ups.len() == 1 { - Some(ups[0].clone()) - } else if !filter_header_only::(ups).is_empty() { - Some(minor_total_hr_upstream::( - &mut filter_header_only::(ups), - )) - } else { - Some(minor_total_hr_upstream::(ups)) - } -} - -impl< - Down: IsMiningDownstream + D, - Up: IsMiningUpstream + D + HasDownstreamSelector, - Sel: DownstreamMiningSelector + D, - > MiningProxyRoutingLogic -{ - // Selects an upstream entity from a list of available upstreams. - fn select_upstreams(ups: &mut [Arc>]) -> Option>> { - select_upstream::(ups) - } - - /// Handles the `SetupConnection` process for header-only mining downstream's. - /// - /// This method selects compatible upstreams, assigns connection flags, and maps the - /// downstream to the selected upstreams. - pub fn on_setup_connection_mining_header_only( - &mut self, - pair_settings: &PairSettings, - ) -> Result<(CommonDownstreamData, SetupConnectionSuccess), Error> { - let mut upstreams = self.upstream_selector.on_setup_connection(pair_settings)?; - let upstream = - Self::select_upstreams(&mut upstreams.0).ok_or(Error::NoUpstreamsConnected)?; - let downstream_data = CommonDownstreamData { - header_only: true, - work_selection: false, - version_rolling: false, - }; - let message = SetupConnectionSuccess { - used_version: 2, - flags: upstream.safe_lock(|u| u.get_flags())?, - }; - self.downstream_to_upstream_map - .insert(downstream_data, vec![upstream]); - Ok((downstream_data, message)) - } - - /// Handles a standard channel opening request for header-only mining downstreams. - pub fn on_open_standard_channel_request_header_only( - &mut self, - downstream: Arc>, - request: &OpenStandardMiningChannel, - ) -> Result>, Error> { - let downstream_mining_data = downstream.safe_lock(|d| d.get_downstream_mining_data())?; - let upstream = self - .downstream_to_upstream_map - .get(&downstream_mining_data) - .ok_or(Error::NoCompatibleUpstream(downstream_mining_data))?[0] - .clone(); - upstream.safe_lock(|u| { - let selector = u.get_remote_selector(); - selector.on_open_standard_channel_request(request.request_id.as_u32(), downstream) - })?; - Ok(upstream) - } -} - -//pub type NoRoutingLogic = RoutingLogic; -//impl + D> -// NoRoutingLogic { pub fn new() -> Self -// where -// Self: D, -// { -// RoutingLogic::None -// } -//} -// -//impl + D> -// Default for NoRoutingLogic -//{ -// fn default() -> Self { -// Self::new() -// } -//} diff --git a/roles/mining-proxy/src/lib/selectors.rs b/roles/mining-proxy/src/lib/selectors.rs deleted file mode 100644 index e9db105dd6..0000000000 --- a/roles/mining-proxy/src/lib/selectors.rs +++ /dev/null @@ -1,346 +0,0 @@ -//! # Selectors and Message Routing -//! -//! This module provides selectors and routing logic for managing downstream and upstream nodes -//! in a mining proxy environment. Selectors help determine the appropriate remote(s) to relay or -//! send messages to. - -use nohash_hasher::BuildNoHashHasher; -use std::{collections::HashMap, fmt::Debug as D, sync::Arc}; -use stratum_common::roles_logic_sv2::{ - common_properties::{IsDownstream, IsMiningDownstream, IsMiningUpstream, PairSettings}, - utils::Mutex, - Error, -}; - -/// Proxy selector for routing messages to downstream mining nodes. -/// -/// Maintains mappings for request IDs, channel IDs, and downstream nodes to facilitate message -/// routing. -#[derive(Debug, Clone, Default)] -pub struct ProxyDownstreamMiningSelector { - // Maps request IDs to their corresponding downstream nodes. - request_id_to_remotes: HashMap>, BuildNoHashHasher>, - - // Maps group channel IDs to a list of downstream nodes. - channel_id_to_downstreams: HashMap>>, BuildNoHashHasher>, - - // Maps standard channel IDs to a single downstream node. - channel_id_to_downstream: HashMap>, BuildNoHashHasher>, -} - -impl ProxyDownstreamMiningSelector { - /// Creates a new [`ProxyDownstreamMiningSelector`] instance. - pub fn new() -> Self { - // `BuildNoHashHasher` is an optimization to bypass the hashing step for integer keys - Self { - request_id_to_remotes: HashMap::with_hasher(BuildNoHashHasher::default()), - channel_id_to_downstreams: HashMap::with_hasher(BuildNoHashHasher::default()), - channel_id_to_downstream: HashMap::with_hasher(BuildNoHashHasher::default()), - } - } - - /// Creates a new [`ProxyDownstreamMiningSelector`] instance wrapped in an `Arc`. - pub fn new_as_mutex() -> Arc> - where - Self: Sized, - { - Arc::new(Mutex::new(Self::new())) - } -} - -impl ProxyDownstreamMiningSelector { - // Removes a specific downstream node from all mappings. - fn _remove_downstream(&mut self, d: &Arc>) { - self.request_id_to_remotes.retain(|_, v| !Arc::ptr_eq(v, d)); - self.channel_id_to_downstream - .retain(|_, v| !Arc::ptr_eq(v, d)); - } -} - -impl DownstreamMiningSelector - for ProxyDownstreamMiningSelector -{ - /// Records a request to open a standard channel with an associated downstream node. - fn on_open_standard_channel_request(&mut self, request_id: u32, downstream: Arc>) { - self.request_id_to_remotes.insert(request_id, downstream); - } - - fn on_open_standard_channel_success( - &mut self, - request_id: u32, - g_channel_id: u32, - channel_id: u32, - ) -> Result>, Error> { - let downstream = self - .request_id_to_remotes - .remove(&request_id) - .ok_or(Error::UnknownRequestId(request_id))?; - self.channel_id_to_downstream - .insert(channel_id, downstream.clone()); - match self.channel_id_to_downstreams.get_mut(&g_channel_id) { - None => { - self.channel_id_to_downstreams - .insert(g_channel_id, vec![downstream.clone()]); - } - Some(x) => x.push(downstream.clone()), - } - Ok(downstream) - } - - // Retrieves all downstream nodes associated with a standard/group channel ID. - fn get_downstreams_in_channel(&self, channel_id: u32) -> Option<&Vec>>> { - self.channel_id_to_downstreams.get(&channel_id) - } - - fn remove_downstreams_in_channel(&mut self, channel_id: u32) -> Vec>> { - let downs = self - .channel_id_to_downstreams - .remove(&channel_id) - .unwrap_or_default(); - for d in &downs { - self._remove_downstream(d); - } - downs - } - - fn remove_downstream(&mut self, d: &Arc>) { - for dws in self.channel_id_to_downstreams.values_mut() { - dws.retain(|node| !Arc::ptr_eq(node, d)); - } - - self._remove_downstream(d); - } - - fn downstream_from_channel_id(&self, channel_id: u32) -> Option>> { - self.channel_id_to_downstream.get(&channel_id).cloned() - } - - fn get_all_downstreams(&self) -> Vec>> { - self.channel_id_to_downstream.values().cloned().collect() - } -} - -impl DownstreamSelector for ProxyDownstreamMiningSelector {} - -/// Specialized trait for selectors managing downstream mining nodes. -/// -/// Logic for an upstream mining node to locate the correct downstream node to which a message -/// should be sent or relayed. -pub trait DownstreamMiningSelector: - DownstreamSelector -{ - /// Handles a downstream node's request to open a standard channel. - fn on_open_standard_channel_request( - &mut self, - request_id: u32, - downstream: Arc>, - ); - - /// Handles the successful opening of a standard channel with a downstream node. Returns an - /// error if the request ID is unknown. - fn on_open_standard_channel_success( - &mut self, - request_id: u32, - g_channel_id: u32, - channel_id: u32, - ) -> Result>, Error>; - - /// Retrieves all downstream nodes associated with a channel ID. - fn get_downstreams_in_channel(&self, channel_id: u32) -> Option<&Vec>>>; - - /// Removes all downstream nodes associated with a channel, returning all removed downstream - /// nodes. - fn remove_downstreams_in_channel(&mut self, channel_id: u32) -> Vec>>; - - /// Removes a specific downstream. - fn remove_downstream(&mut self, d: &Arc>); - - // Retrieves the downstream node associated with a specific standard channel ID. - // - // Only for standard channels. - fn downstream_from_channel_id(&self, channel_id: u32) -> Option>>; - - /// Retrieves all downstream nodes managed by the selector. - fn get_all_downstreams(&self) -> Vec>>; -} - -/// Base trait for selectors managing downstream nodes. -pub trait DownstreamSelector {} - -/// No-op selector for cases where routing logic is unnecessary. -/// -/// Primarily used for testing, it implements all required traits, but panics with an -/// [`unreachable`] if called. -#[derive(Debug, Clone, Copy, Default)] -pub struct NullDownstreamMiningSelector(); - -impl NullDownstreamMiningSelector { - /// Creates a new [`NullDownstreamMiningSelector`] instance. - pub fn new() -> Self { - NullDownstreamMiningSelector() - } - - /// Creates a new [`NullDownstreamMiningSelector`] instance wrapped in an `Arc`. - pub fn new_as_mutex() -> Arc> - where - Self: Sized, - { - Arc::new(Mutex::new(Self::new())) - } -} - -impl DownstreamMiningSelector for NullDownstreamMiningSelector { - /// [`unreachable`] in this no-op implementation. - fn on_open_standard_channel_request( - &mut self, - _request_id: u32, - _downstream: Arc>, - ) { - unreachable!("on_open_standard_channel_request") - } - - /// [`unreachable`] in this no-op implementation. - fn on_open_standard_channel_success( - &mut self, - _request_id: u32, - _channel_id: u32, - _channel_id_2: u32, - ) -> Result>, Error> { - unreachable!("on_open_standard_channel_success") - } - - /// [`unreachable`] in this no-op implementation. - fn get_downstreams_in_channel(&self, _channel_id: u32) -> Option<&Vec>>> { - unreachable!("get_downstreams_in_channel") - } - - /// [`unreachable`] in this no-op implementation. - fn remove_downstreams_in_channel(&mut self, _channel_id: u32) -> Vec>> { - unreachable!("remove_downstreams_in_channel") - } - - /// [`unreachable`] in this no-op implementation. - fn remove_downstream(&mut self, _d: &Arc>) { - unreachable!("remove_downstream") - } - - /// [`unreachable`] in this no-op implementation. - fn downstream_from_channel_id(&self, _channel_id: u32) -> Option>> { - unreachable!("downstream_from_channel_id") - } - - /// [`unreachable`] in this no-op implementation. - fn get_all_downstreams(&self) -> Vec>> { - unreachable!("get_all_downstreams") - } -} - -impl DownstreamSelector for NullDownstreamMiningSelector {} - -/// Base trait for selectors managing upstream nodes. -pub trait UpstreamSelector {} - -/// Specialized trait for selectors managing upstream mining nodes. -/// -/// This trait is implemented by roles with multiple upstream connections, such as proxies or -/// pools. It provides logic to route messages received by the implementing role (e.g., from mining -/// devices or downstream proxies) to the appropriate upstream nodes. -/// -/// For example, a mining proxy with multiple upstream pools would implement this trait to handle -/// upstream connection setup, failover, and routing logic. -pub trait UpstreamMiningSelctor< - Down: IsMiningDownstream, - Up: IsMiningUpstream, - Sel: DownstreamMiningSelector, ->: UpstreamSelector -{ - /// Handles the setup of connections to upstream nodes with the given [`PairSettings`]. - /// - /// Returns an error if the upstream and downstream node(s) are unpairable. - #[allow(clippy::type_complexity)] - fn on_setup_connection( - &mut self, - pair_settings: &PairSettings, - ) -> Result<(Vec>>, u32), Error>; - - /// Retrieves an upstream node by its ID, if exists. - fn get_upstream(&self, upstream_id: u32) -> Option>>; -} - -/// Selector for routing messages to downstream nodes based on pair settings and flags. -/// -/// Tracks upstream nodes and their IDs for efficient lookups. -#[derive(Debug)] -pub struct GeneralMiningSelector< - Sel: DownstreamMiningSelector, - Down: IsMiningDownstream, - Up: IsMiningUpstream, -> { - /// List of upstream nodes. - pub upstreams: Vec>>, - - /// Mapping of upstream IDs to their corresponding upstream nodes. - pub id_to_upstream: HashMap>, BuildNoHashHasher>, - - sel: std::marker::PhantomData, - - down: std::marker::PhantomData, -} - -impl, Up: IsMiningUpstream, Down: IsMiningDownstream> - GeneralMiningSelector -{ - /// Creates a new [`GeneralMiningSelector`] instance with the given upstream nodes. - pub fn new(upstreams: Vec>>) -> Self { - let mut id_to_upstream = HashMap::with_hasher(BuildNoHashHasher::default()); - for up in &upstreams { - id_to_upstream.insert(up.safe_lock(|u| u.get_id()).unwrap(), up.clone()); - } - Self { - upstreams, - id_to_upstream, - sel: std::marker::PhantomData, - down: std::marker::PhantomData, - } - } - - /// Updates the list of upstream nodes. - pub fn update_upstreams(&mut self, upstreams: Vec>>) { - self.upstreams = upstreams; - } -} - -impl, Down: IsMiningDownstream, Up: IsMiningUpstream> - UpstreamSelector for GeneralMiningSelector -{ -} - -impl, Down: IsMiningDownstream, Up: IsMiningUpstream> - UpstreamMiningSelctor for GeneralMiningSelector -{ - fn on_setup_connection( - &mut self, - pair_settings: &PairSettings, - ) -> Result<(Vec>>, u32), Error> { - let mut supported_upstreams = vec![]; - let mut supported_flags: u32 = 0; - for node in &self.upstreams { - let is_pairable = node - .safe_lock(|node| node.is_pairable(pair_settings)) - .unwrap(); - if is_pairable { - supported_flags |= node.safe_lock(|n| n.get_flags()).unwrap(); - supported_upstreams.push(node.clone()); - } - } - if !supported_upstreams.is_empty() { - return Ok((supported_upstreams, supported_flags)); - } - - Err(Error::NoPairableUpstream((2, 2, 0))) - } - - fn get_upstream(&self, upstream_id: u32) -> Option>> { - self.id_to_upstream.get(&upstream_id).cloned() - } -} diff --git a/roles/mining-proxy/src/lib/upstream_mining.rs b/roles/mining-proxy/src/lib/upstream_mining.rs deleted file mode 100644 index dd67f3e483..0000000000 --- a/roles/mining-proxy/src/lib/upstream_mining.rs +++ /dev/null @@ -1,1340 +0,0 @@ -#![allow(dead_code)] - -use core::convert::TryInto; -use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration}; - -use async_channel::{Receiver, SendError, Sender}; -use async_recursion::async_recursion; -use nohash_hasher::BuildNoHashHasher; -use tokio::{net::TcpStream, task}; -use tracing::{debug, error, info}; - -use super::{ - downstream_mining::{Channel, DownstreamMiningNode, StdFrame as DownstreamFrame}, - routing_logic::{MiningRouter, MiningRoutingLogic}, - selectors::{DownstreamMiningSelector, ProxyDownstreamMiningSelector as Prs}, - EXTRANONCE_RANGE_1_LENGTH, -}; -use stratum_common::{ - network_helpers_sv2::noise_connection::Connection, - roles_logic_sv2::{ - bitcoin::TxOut, - channel_logic::{ - channel_factory::{ - ExtendedChannelKind, OnNewShare, ProxyExtendedChannelFactory, Share, - }, - proxy_group_channel::GroupChannels, - }, - codec_sv2, - codec_sv2::{HandshakeRole, Initiator, StandardEitherFrame, StandardSv2Frame}, - common_messages_sv2::{Protocol, SetupConnection}, - common_properties::{ - IsMiningDownstream, IsMiningUpstream, IsUpstream, RequestIdMapper, UpstreamChannel, - }, - errors::Error, - handlers::mining::{ParseMiningMessagesFromUpstream, SendTo, SupportedChannelTypes}, - job_dispatcher::GroupChannelJobDispatcher, - mining_sv2::*, - parsers::{AnyMessage, CommonMessages, Mining, MiningDeviceMessages}, - template_distribution_sv2::SubmitSolution, - utils::{GroupId, Id, Mutex}, - }, -}; - -pub type Message = AnyMessage<'static>; -pub type StdFrame = StandardSv2Frame; -pub type EitherFrame = StandardEitherFrame; -pub type ProxyRemoteSelector = Prs; - -#[derive(Debug)] -#[allow(clippy::large_enum_variant)] -pub enum ChannelKind { - Group(GroupChannels), - Extended(Option), -} -impl ChannelKind { - pub fn is_extended(&self) -> bool { - match self { - ChannelKind::Group(_) => false, - ChannelKind::Extended(_) => true, - } - } - - fn is_initialized(&self) -> bool { - !matches!(self, ChannelKind::Extended(None)) - } - - fn get_factory(&mut self) -> &mut ProxyExtendedChannelFactory { - match self { - ChannelKind::Extended(Some(f)) => f, - _ => panic!("Channel factory not available"), - } - } - - fn initialize_factory( - &mut self, - group_id: Arc>, - extranonces: ExtendedExtranonce, - downstream_share_per_minute: f32, - upstream_target: Target, - up_id: u32, - ) { - match self { - ChannelKind::Group(_) => panic!("Impossible to initialize factory for group channel"), - ChannelKind::Extended(Some(_)) => panic!("Factory already initialized"), - ChannelKind::Extended(None) => { - let kind = ExtendedChannelKind::Proxy { upstream_target }; - let factory = ProxyExtendedChannelFactory::new( - group_id, - extranonces, - None, - downstream_share_per_minute, - kind, - Some(vec![]), - up_id, - ); - *self = Self::Extended(Some(factory)); - } - } - } - - fn reset(&mut self) { - match self { - ChannelKind::Group(_) => { - *self = ChannelKind::Group(GroupChannels::new()); - } - ChannelKind::Extended(_) => { - *self = ChannelKind::Extended(None); - } - } - } -} - -impl From for ChannelKind { - fn from(v: super::ChannelKind) -> Self { - match v { - super::ChannelKind::Group => Self::Group(GroupChannels::new()), - super::ChannelKind::Extended => Self::Extended(None), - } - } -} - -/// 1 to 1 connection with a pool -/// Can be either a mining pool or another proxy -/// 1 to 1 connection with an upstream node that implement the mining (sub)protocol can be either a -/// a pool or an upstream proxy. -#[derive(Debug, Clone)] -struct UpstreamMiningConnection { - receiver: Receiver, - sender: Sender, -} - -impl UpstreamMiningConnection { - async fn send(&mut self, sv2_frame: StdFrame) -> Result<(), SendError> { - info!("SEND"); - let either_frame = sv2_frame.into(); - match self.sender.send(either_frame).await { - Ok(_) => Ok(()), - Err(e) => Err(e), - } - } -} - -#[derive(Clone, Copy, Debug)] -pub struct Sv2MiningConnection { - version: u16, - setup_connection_flags: u32, - #[allow(dead_code)] - setup_connection_success_flags: u32, -} - -// Efficient stack do use JobDispatcher so the smaller variant (None) do not impact performance -// cause is used in already non performant environments. That to justify the below allow. -// https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_varianT -#[allow(clippy::large_enum_variant)] -#[derive(Debug)] -pub enum JobDispatcher { - Group(GroupChannelJobDispatcher), - None, -} - -/// Can be either a mining pool or another proxy -#[derive(Debug)] -pub struct UpstreamMiningNode { - id: u32, - total_hash_rate: u64, - address: SocketAddr, - connection: Option, - sv2_connection: Option, - authority_public_key: [u8; 32], - /// group_channel id/channel_id -> dispatcher - pub channel_id_to_job_dispatcher: HashMap>, - /// Each relayed message that has a `request_id` field must have a unique `request_id` number, - /// connection-wise. - /// The `request_id` from the downstream is NOT guaranteed to be unique, so it must be changed. - request_id_mapper: RequestIdMapper, - downstream_selector: ProxyRemoteSelector, - pub channel_kind: ChannelKind, - group_id: Arc>, - pub channel_ids: Arc>, - downstream_share_per_minute: f32, - pub solution_sender: Option>>, - pub recv_coinbase_out: Option, Vec)>>, - #[allow(dead_code)] - tx_outs: HashMap, Vec>, - // When a future job is received from an extended channel this is transformed to severla std - // job for HOM downstream. If the job is future we need to keep track of the original job id - // and the new job ids used for the std job and also which downstream received which id. - // When a set new prev hash is received if it refer one of these ids we use this map and - // build the right set new pre hash for each downstream. TODO who is clearing the map? - #[allow(clippy::type_complexity)] - job_up_to_down_ids: - HashMap>, u32)>, BuildNoHashHasher>, - downstream_hash_rate: f32, - reconnect: bool, -} - -/// It assume that endpoint NEVER change flags and version! -/// I can open both extended and group channel with upstream. -impl UpstreamMiningNode { - #[allow(clippy::too_many_arguments)] - pub fn new( - id: u32, - address: SocketAddr, - authority_public_key: [u8; 32], - channel_kind: super::ChannelKind, - group_id: Arc>, - channel_ids: Arc>, - downstream_share_per_minute: f32, - solution_sender: Option>>, - recv_coinbase_out: Option, Vec)>>, - downstream_hash_rate: f32, - reconnect: bool, - ) -> Self { - let request_id_mapper = RequestIdMapper::new(); - let downstream_selector = ProxyRemoteSelector::new(); - Self { - id, - total_hash_rate: 0, - address, - connection: None, - sv2_connection: None, - authority_public_key, - channel_id_to_job_dispatcher: HashMap::with_hasher(BuildNoHashHasher::default()), - request_id_mapper, - downstream_selector, - channel_kind: channel_kind.into(), - group_id, - channel_ids, - downstream_share_per_minute, - solution_sender, - recv_coinbase_out, - tx_outs: HashMap::new(), - job_up_to_down_ids: HashMap::with_hasher(BuildNoHashHasher::default()), - downstream_hash_rate, - reconnect, - } - } - fn on_p_hash( - &mut self, - mut m: SetNewPrevHash<'static>, - ) -> Result, Error> { - match self.job_up_to_down_ids.get(&m.job_id) { - Some(downstreams) => { - let mut res = vec![]; - for (downstream, job_id) in downstreams { - m.job_id = *job_id; - let message = Mining::SetNewPrevHash(m.clone().into_static()); - res.push(SendTo::RelayNewMessageToRemote( - downstream.clone(), - message.clone(), - )); - } - self.job_up_to_down_ids = HashMap::with_hasher(BuildNoHashHasher::default()); - Ok(SendTo::Multiple(res)) - } - None => { - let downstrems = self.downstream_selector.get_all_downstreams(); - let mut res = vec![]; - m.job_id = 0; - let message = Mining::SetNewPrevHash(m.into_static()); - for downstream in downstrems { - res.push(SendTo::RelayNewMessageToRemote(downstream, message.clone())); - } - self.job_up_to_down_ids = HashMap::with_hasher(BuildNoHashHasher::default()); - Ok(SendTo::Multiple(res)) - } - } - } - - /// Try send a message to the upstream node. - /// If the node is connected and there are no error return Ok(()) - /// If the node is connected and there is an error the message is not sent and an error is - /// returned and the upstream is marked as not connected. - /// If the node is not connected it try to connect and send the message and everything is ok - /// the upstream is marked as connected and Ok(()) is returned if not an error is returned. - pub async fn send( - self_mutex: Arc>, - sv2_frame: StdFrame, - ) -> Result<(), super::error::Error> { - let (has_sv2_connection, mut connection, address) = self_mutex - .safe_lock(|self_| { - ( - self_.sv2_connection.is_some(), - self_.connection.clone(), - self_.address, - ) - }) - .unwrap(); - //let mut self_ = self_mutex.lock().await; - - match (connection.as_mut(), has_sv2_connection) { - (Some(connection), true) => match connection.send(sv2_frame).await { - Ok(_) => Ok(()), - Err(e) => { - error!( - "Error sending message to upstream node. Trying to reconnect to {}: {}", - address, e - ); - Self::connect(self_mutex.clone()).await.unwrap(); - // It assume that enpoint NEVER change flags and version! - match Self::setup_connection(self_mutex).await { - Ok(()) => Ok(()), - Err(()) => panic!(), - } - } - }, - // It assume that no downstream try to send messages before that the upstream is - // initialized. This assumption is enforced by the fact that - // UpstreamMiningNode::pair only pair downstream noder with already - // initialized upstream nodes! - (Some(connection), false) => match connection.send(sv2_frame).await { - Ok(_) => Ok(()), - Err(e) => Err(e.into()), - }, - (None, _) => { - Self::connect(self_mutex.clone()).await?; - let mut connection = self_mutex - .safe_lock(|self_| self_.connection.clone()) - .unwrap(); - match connection.as_mut().unwrap().send(sv2_frame).await { - Ok(_) => match Self::setup_connection(self_mutex).await { - Ok(()) => Ok(()), - Err(()) => panic!(), - }, - Err(e) => { - error!( - "Error sending message to upstream node at {} with error {}", - address, e - ); - //Self::connect(self_mutex.clone()).await.unwrap(); - Err(e.into()) - } - } - } - } - } - - async fn receive(self_mutex: Arc>) -> Result { - let mut connection = self_mutex - .safe_lock(|self_| self_.connection.clone()) - .unwrap(); - match connection.as_mut() { - Some(connection) => match connection.receiver.recv().await { - Ok(m) => Ok(m.try_into().unwrap()), - Err(_) => { - let address = self_mutex.safe_lock(|s| s.address).unwrap(); - error!("Upstream node {} is not available", address); - Err(super::error::Error::UpstreamNotAvailabe(address)) - } - }, - None => { - error!("No connection was found."); - todo!() - } - } - } - - async fn connect(self_mutex: Arc>) -> Result<(), super::error::Error> { - let has_connection = self_mutex - .safe_lock(|self_| self_.connection.is_some()) - .unwrap(); - match has_connection { - true => Ok(()), - false => { - let (address, authority_public_key) = self_mutex - .safe_lock(|self_| (self_.address, self_.authority_public_key)) - .unwrap(); - let socket = TcpStream::connect(address).await.map_err(|_| { - error!("Upstream node {} is not available", address); - super::error::Error::UpstreamNotAvailabe(address) - })?; - info!( - "Connected to upstream node {}: now handling noise handshake", - address - ); - - let initiator = Initiator::from_raw_k(authority_public_key).unwrap(); - let (receiver, sender) = - Connection::new(socket, HandshakeRole::Initiator(initiator)) - .await - .expect("impossible to conenct"); - let connection = UpstreamMiningConnection { receiver, sender }; - self_mutex - .safe_lock(|self_| { - self_.connection = Some(connection); - }) - .unwrap(); - info!("handshare done"); - Ok(()) - } - } - } - - #[async_recursion] - async fn setup_connection(self_mutex: Arc>) -> Result<(), ()> { - let sv2_connection = self_mutex.safe_lock(|self_| self_.sv2_connection).unwrap(); - - match sv2_connection { - None => Ok(()), - Some(sv2_connection) => { - let flags = sv2_connection.setup_connection_flags; - let version = sv2_connection.version; - let frame = self_mutex - .safe_lock(|self_| self_.new_setup_connection_frame(flags, version, version)) - .unwrap(); - Self::send(self_mutex.clone(), frame) - .await - .map_err(|e| (error!("Failed to send {:?}", e)))?; - - let cloned = self_mutex.clone(); - let mut response = task::spawn(async { Self::receive(cloned).await }) - .await - .unwrap() - .unwrap(); - - let message_type = response.get_header().unwrap().msg_type(); - let payload = response.payload(); - match (message_type, payload).try_into() { - Ok(CommonMessages::SetupConnectionSuccess(_)) => { - let receiver = self_mutex - .safe_lock(|self_| self_.connection.clone().unwrap().receiver) - .unwrap(); - Self::relay_incoming_messages(self_mutex, receiver); - Ok(()) - } - _ => panic!(), - } - } - } - } - - fn relay_incoming_messages( - self_: Arc>, - //_downstreams: HashMap, - receiver: Receiver, - ) { - task::spawn(async move { - loop { - if let Ok(message) = receiver.recv().await { - let m: StdFrame = message.try_into().unwrap(); - let incoming: StdFrame = m; - Self::next(self_.clone(), incoming).await; - } else { - Self::exit(self_); - break; - } - } - }); - } - pub fn get_id(&self) -> u32 { - self.id - } - - pub fn remove_dowstream(self_: Arc>, down: &Arc>) { - self_ - .safe_lock(|s| s.downstream_selector.remove_downstream(down)) - .unwrap(); - } - - fn exit(self_: Arc>) { - if !self_.safe_lock(|s| s.reconnect).unwrap() { - super::remove_upstream(self_.safe_lock(|s| s.id).unwrap()); - } - let downstreams = self_ - .safe_lock(|s| s.downstream_selector.get_all_downstreams()) - .unwrap(); - let mut dowstreams_: Vec>> = vec![]; - for d in downstreams { - if let Some(id) = d - .safe_lock(|d| match &d.status { - super::downstream_mining::DownstreamMiningNodeStatus::Initializing => None, - super::downstream_mining::DownstreamMiningNodeStatus::Paired(_) => None, - super::downstream_mining::DownstreamMiningNodeStatus::ChannelOpened( - channel, - ) => match channel { - Channel::DownstreamHomUpstreamGroup { channel_id, .. } => Some(*channel_id), - Channel::DownstreamHomUpstreamExtended { channel_id, .. } => { - Some(*channel_id) - } - }, - }) - .unwrap() - { - self_ - .safe_lock(|s| s.downstream_selector.remove_downstreams_in_channel(id)) - .unwrap(); - { - dowstreams_.push(d); - } - } - } - for d in dowstreams_ { - // TODO make sure that each reference have been dropped - if Arc::strong_count(&d) > 1 { - //todo!() - } - DownstreamMiningNode::exit(d); - } - if self_.safe_lock(|s| s.reconnect).unwrap() { - self_.safe_lock(|s| s.connection = None).unwrap(); - let flags = self_ - .safe_lock(|s| s.sv2_connection.unwrap().setup_connection_flags) - .unwrap(); - self_.safe_lock(|s| s.sv2_connection = None).unwrap(); - self_.safe_lock(|s| s.channel_kind.reset()).unwrap(); - tokio::task::spawn(async move { - tokio::time::sleep(std::time::Duration::from_secs(10)).await; - Self::setup_flag_and_version(self_, Some(flags), 2, 2) - .await - .unwrap(); - }); - } - } - - async fn match_next_message( - self_mutex: Arc>, - to_send: Result, Error>, - incoming: StdFrame, - ) { - match to_send { - Ok(SendTo::RelaySameMessageToRemote(downstream)) => { - let sv2_frame: codec_sv2::Sv2Frame = - incoming.map(|payload| payload.try_into().unwrap()); - - DownstreamMiningNode::send(downstream.clone(), sv2_frame) - .await - .unwrap(); - } - Ok(SendTo::RelayNewMessageToRemote(downstream_mutex, message)) => { - let message = MiningDeviceMessages::Mining(message); - let frame: DownstreamFrame = message.try_into().unwrap(); - DownstreamMiningNode::send(downstream_mutex, frame) - .await - .unwrap(); - } - Ok(SendTo::Respond(message)) => { - let message = AnyMessage::Mining(message); - let frame: StdFrame = message.try_into().unwrap(); - UpstreamMiningNode::send(self_mutex, frame).await.unwrap(); - } - Ok(SendTo::Multiple(sends_to)) => { - for send_to in sends_to { - match send_to { - SendTo::RelayNewMessageToRemote(downstream_mutex, message) => { - let message = MiningDeviceMessages::Mining(message); - let frame: DownstreamFrame = message.try_into().unwrap(); - DownstreamMiningNode::send(downstream_mutex, frame) - .await - .unwrap(); - } - SendTo::RelaySameMessageToRemote(downstream_mutex) => { - let frame: codec_sv2::Sv2Frame< - MiningDeviceMessages, - buffer_sv2::Slice, - > = incoming.clone().map(|payload| payload.try_into().unwrap()); - DownstreamMiningNode::send(downstream_mutex, frame) - .await - .unwrap(); - } - SendTo::Respond(message) => { - let message = AnyMessage::Mining(message); - let frame: StdFrame = message.try_into().unwrap(); - UpstreamMiningNode::send(self_mutex.clone(), frame) - .await - .unwrap(); - } - SendTo::None(_) => (), - SendTo::Multiple(_) => panic!("Nested SendTo::Multiple not supported"), - _ => panic!(), - } - } - } - Ok(SendTo::None(_)) => (), - Ok(_) => panic!(), - Err(Error::NoDownstreamsConnected) => (), - Err(e) => panic!("{:?}", e), - } - } - - pub async fn next(self_mutex: Arc>, mut incoming: StdFrame) { - let message_type = incoming.get_header().unwrap().msg_type(); - let payload = incoming.payload(); - - let next_message_to_send = - UpstreamMiningNode::handle_message_mining(self_mutex.clone(), message_type, payload); - Self::match_next_message(self_mutex, next_message_to_send, incoming).await; - } - - #[async_recursion] - async fn setup_flag_and_version( - self_mutex: Arc>, - flags: Option, - min_version: u16, - max_version: u16, - ) -> Result<(), super::error::Error> { - let flags = flags.unwrap_or(0b0000_0000_0000_0000_0000_0000_0000_0110); - let (frame, downstream_hr) = self_mutex - .safe_lock(|self_| { - ( - self_.new_setup_connection_frame(flags, min_version, max_version), - self_.downstream_hash_rate, - ) - }) - .unwrap(); - Self::send(self_mutex.clone(), frame).await?; - - let cloned = self_mutex.clone(); - let mut response = task::spawn(async { Self::receive(cloned).await }) - .await - .unwrap() - .unwrap(); - - let message_type = response.get_header().unwrap().msg_type(); - let payload = response.payload(); - match (message_type, payload).try_into() { - Ok(CommonMessages::SetupConnectionSuccess(m)) => { - let receiver = self_mutex - .safe_lock(|self_| { - self_.sv2_connection = Some(Sv2MiningConnection { - version: m.used_version, - setup_connection_flags: flags, - setup_connection_success_flags: m.flags, - }); - self_.connection.clone().unwrap().receiver - }) - .unwrap(); - Self::relay_incoming_messages(self_mutex.clone(), receiver); - if self_mutex - .safe_lock(|s| s.channel_kind.is_extended()) - .unwrap() - { - Self::open_extended_channel(self_mutex.clone(), downstream_hr).await - } - Ok(()) - } - Ok(CommonMessages::SetupConnectionError(m)) => { - if m.flags != 0 { - let flags = flags ^ m.flags; - // We need to send SetupConnection again as we do not yet know the version of - // upstream - // debounce this? - Self::setup_flag_and_version(self_mutex, Some(flags), min_version, max_version) - .await - } else { - let error_message = std::str::from_utf8(m.error_code.inner_as_ref()) - .unwrap() - .to_string(); - Err(super::error::Error::SetupConnectionError(error_message)) - } - } - Ok(_) => todo!(), - Err(_) => todo!(), - } - } - - async fn open_extended_channel(self_mutex: Arc>, nominal_hash_rate: f32) { - let message = AnyMessage::Mining(Mining::OpenExtendedMiningChannel( - OpenExtendedMiningChannel { - request_id: 0, - user_identity: "proxy".to_string().try_into().unwrap(), - nominal_hash_rate, - max_target: [ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - ] - .into(), - min_extranonce_size: super::MIN_EXTRANONCE_SIZE, - }, - )); - Self::send(self_mutex.clone(), message.try_into().unwrap()) - .await - .unwrap(); - - Self::wait_for_channel_factory(self_mutex).await; - } - - async fn wait_for_channel_factory(self_mutex: Arc>) { - while !self_mutex - .safe_lock(|s| s.channel_kind.is_initialized()) - .unwrap() - { - tokio::time::sleep(Duration::from_secs(1)).await; - } - } - - fn new_setup_connection_frame( - &self, - flags: u32, - min_version: u16, - max_version: u16, - ) -> StdFrame { - let endpoint_host = self - .address - .ip() - .to_string() - .into_bytes() - .try_into() - .unwrap(); - let vendor = String::new().try_into().unwrap(); - let hardware_version = String::new().try_into().unwrap(); - let firmware = String::new().try_into().unwrap(); - let device_id = String::new().try_into().unwrap(); - let setup_connection: AnyMessage = SetupConnection { - protocol: Protocol::MiningProtocol, - min_version, - max_version, - flags, - endpoint_host, - endpoint_port: self.address.port(), - vendor, - hardware_version, - firmware, - device_id, - } - .into(); - setup_connection.try_into().unwrap() - } - - pub fn open_standard_channel_down( - &mut self, - request_id: u32, - downstream_hash_rate: f32, - id_header_only: bool, - channel_id: u32, - ) -> Vec> { - match &mut self.channel_kind { - // When channel kind is Group (that means that no extended channels is open between - // proxy and this upstream) opening channel is handled by upstream and proxy must only - // relay messages - ChannelKind::Group(_) => { - panic!("Open satandard channel down for group up not supported") - } - ChannelKind::Extended(Some(factory)) => { - self.downstream_selector - .on_open_standard_channel_success(request_id, 0, channel_id) - .unwrap(); - let messages = factory - .add_standard_channel( - request_id, - downstream_hash_rate, - id_header_only, - channel_id, - ) - .unwrap(); - messages.into_iter().map(|x| x.into_static()).collect() - } - _ => panic!("Channel factory not initialized"), - } - } - - pub fn handle_std_shr( - self_: Arc>, - share_: SubmitSharesStandard, - ) -> Result, Error> { - if self_.safe_lock(|s| s.channel_kind.is_extended()).unwrap() { - let share = self_ - .safe_lock(|s| { - let factory = s.channel_kind.get_factory(); - factory.on_submit_shares_standard(share_.clone()) - }) - .unwrap()?; - match share { - OnNewShare::SendErrorDownstream(e) => { - tracing::error!("Received invalid share"); - Ok(Mining::SubmitSharesError(e)) - } - OnNewShare::SendSubmitShareUpstream((s, _)) => match s { - Share::Extended(s) => { - let message = Mining::SubmitSharesExtended(s); - let message = AnyMessage::Mining(message); - let frame: StdFrame = message.try_into().unwrap(); - tokio::task::spawn(async move { - UpstreamMiningNode::send(self_.clone(), frame) - .await - .unwrap(); - }); - let success = SubmitSharesSuccess { - channel_id: share_.channel_id, - last_sequence_number: share_.sequence_number, - new_submits_accepted_count: 1, - new_shares_sum: 1, - }; - let message = Mining::SubmitSharesSuccess(success); - Ok(message) - } - Share::Standard(_) => unreachable!(), - }, - OnNewShare::RelaySubmitShareUpstream => todo!(), - OnNewShare::ShareMeetBitcoinTarget((share, Some(template_id), coinbase, _)) => { - match share { - Share::Extended(s) => { - let solution = SubmitSolution { - template_id, - version: s.version, - header_timestamp: s.ntime, - header_nonce: s.nonce, - coinbase_tx: coinbase.try_into().unwrap(), - }; - let sender = self_ - .safe_lock(|s| s.solution_sender.clone()) - .unwrap() - .unwrap(); - // The below channel should never be full is ok to block - sender.send_blocking(solution).unwrap(); - - let message = Mining::SubmitSharesExtended(s); - let message = AnyMessage::Mining(message); - let frame: StdFrame = message.try_into().unwrap(); - tokio::task::spawn(async move { - UpstreamMiningNode::send(self_.clone(), frame) - .await - .unwrap(); - }); - let success = SubmitSharesSuccess { - channel_id: share_.channel_id, - last_sequence_number: share_.sequence_number, - new_submits_accepted_count: 1, - new_shares_sum: 1, - }; - let message = Mining::SubmitSharesSuccess(success); - Ok(message) - } - Share::Standard(_) => { - // on_submit_shares_standard call check_target that in the case of a - // Proxy and a share that is below the - // bitcoin target if the share is a standard - // share call share.into_extended making this branch unreachable. - unreachable!() - } - } - } - // When we have a ShareMeetBitcoinTarget it means that the proxy know the bitcoin - // target that means that the proxy must have JD capabilities that means that the - // second tuple elements can not be None but must be Some(template_id) - OnNewShare::ShareMeetBitcoinTarget(..) => unreachable!(), - OnNewShare::ShareMeetDownstreamTarget => { - let success = SubmitSharesSuccess { - channel_id: share_.channel_id, - last_sequence_number: share_.sequence_number, - new_submits_accepted_count: 1, - new_shares_sum: 1, - }; - let message = Mining::SubmitSharesSuccess(success); - Ok(message) - } - } - } else { - unreachable!("Calling share_into_extended for an non extended upstream make no sense") - } - } - - // Example of how next could be implemented more efficently if no particular good log are - // needed it just relay the majiority of messages downstream without serializing and - // deserializing them. In order to find the Downstream at which the message must bu relayed the - // channel id must be deserialized, but knowing the message type that is a very easy task is - // either 4 bytes after the header or the first 4 bytes after the header + 4 bytes - // #[cfg(test)] - // #[allow(unused)] - // pub async fn next_faster(&mut self, mut incoming: StdFrame) { - // let message_type = incoming.get_header().unwrap().msg_type(); - - // // When a channel is opened we need to setup the channel id in order to relay next - // messages // to the right Downstream - // if todo!() { // check if message_type is channel related - - // // When a mining message is received (that is not a channel related message) always - // relay it downstream } else if todo!() { // check if message_type is is a mining - // message // everything here can be just relayed downstream - - // // Other sub(protocol) messages - // } else { - // todo!() - // } - // } -} - -pub trait HasDownstreamSelector { - fn get_remote_selector(&mut self) -> &mut ProxyRemoteSelector; -} - -impl HasDownstreamSelector for UpstreamMiningNode { - fn get_remote_selector(&mut self) -> &mut ProxyRemoteSelector { - &mut self.downstream_selector - } -} - -impl ParseMiningMessagesFromUpstream for UpstreamMiningNode { - fn get_channel_type(&self) -> SupportedChannelTypes { - SupportedChannelTypes::GroupAndExtended - } - - fn is_work_selection_enabled(&self) -> bool { - true - } - - fn handle_open_standard_mining_channel_success( - &mut self, - m: OpenStandardMiningChannelSuccess, - ) -> Result, Error> { - let routing_logic = super::get_routing_logic(); - let remote = match routing_logic { - MiningRoutingLogic::None => None, - MiningRoutingLogic::Proxy(r_logic) => { - let up = r_logic - .safe_lock(|r_logic| { - r_logic.on_open_standard_channel_success(self, &mut m.clone()) - })?; - Some(up?) - } - MiningRoutingLogic::_P(_) => panic!("Must use either MiningRoutingLogic::None or MiningRoutingLogic::Proxy for `routing_logic` param"), - }; - match &mut self.channel_kind { - ChannelKind::Group(group) => { - let down_is_header_only = remote - .as_ref() - .unwrap() - .safe_lock(|remote| remote.is_header_only()) - .unwrap(); - let remote = remote.unwrap(); - if down_is_header_only { - let mut res = vec![SendTo::RelaySameMessageToRemote(remote.clone())]; - for message in group.on_channel_success_for_hom_downtream(&m)? { - res.push(SendTo::RelayNewMessageToRemote(remote.clone(), message)); - } - remote - .safe_lock(|r| { - r.open_channel_for_down_hom_up_group(m.channel_id, m.group_channel_id) - }) - .unwrap(); - Ok(SendTo::Multiple(res)) - } else { - // Here we want to support only the case where downstream is non HOM and want to - // open extended channels with the proxy. Dowstream non HOM - // that try to open standard channel (grouped in groups) do - // not make much sense so for now is not supported - panic!() - } - } - // If we opened and extended channel upstreams we should not receive this message - ChannelKind::Extended(_) => todo!(), - } - } - - fn handle_open_extended_mining_channel_success( - &mut self, - m: OpenExtendedMiningChannelSuccess, - ) -> Result, Error> { - info!( - "Received OpenExtendedMiningChannelSuccess with request id: {} and channel id: {}", - m.request_id, m.channel_id - ); - debug!("OpenStandardMiningChannelSuccess: {}", m); - let extranonce_prefix: Extranonce = m.extranonce_prefix.clone().into(); - let range_0 = 0..m.extranonce_prefix.clone().to_vec().len(); - let range_1 = range_0.end..(range_0.end + EXTRANONCE_RANGE_1_LENGTH); - let range_2 = range_1.end..(range_0.end + m.extranonce_size as usize); - let extranonces = ExtendedExtranonce::from_upstream_extranonce( - extranonce_prefix, - range_0, - range_1, - range_2, - ) - .unwrap(); - - self.channel_kind.initialize_factory( - self.group_id.clone(), - extranonces, - self.downstream_share_per_minute, - m.target.clone().into(), - m.channel_id, - ); - Ok(SendTo::None(None)) - } - - fn handle_open_mining_channel_error( - &mut self, - _m: OpenMiningChannelError, - ) -> Result, Error> { - todo!("460") - } - - fn handle_update_channel_error( - &mut self, - _m: UpdateChannelError, - ) -> Result, Error> { - todo!("470") - } - - fn handle_close_channel( - &mut self, - _m: CloseChannel, - ) -> Result, Error> { - todo!("480") - } - - fn handle_set_extranonce_prefix( - &mut self, - _m: SetExtranoncePrefix, - ) -> Result, Error> { - todo!("490") - } - - fn handle_submit_shares_success( - &mut self, - m: SubmitSharesSuccess, - ) -> Result, Error> { - info!("Received SubmitSharesSuccess"); - debug!("SubmitSharesSuccess: {}", m); - match &self - .downstream_selector - .downstream_from_channel_id(m.channel_id) - { - Some(d) => Ok(SendTo::RelaySameMessageToRemote(d.clone())), - None => { - info!("Share success"); - Ok(SendTo::None(None)) - } - } - } - - fn handle_submit_shares_error( - &mut self, - m: SubmitSharesError, - ) -> Result, Error> { - error!( - "Received SubmitSharesError with error code {}", - std::str::from_utf8(m.error_code.as_ref()).unwrap_or("unknown error code") - ); - Ok(SendTo::None(None)) - } - - // TODO this is usefull only for non hom upstream do we really want to support non hom upstream - // it do not make much sense IMO. - // For now I comment the code and put here an Error - fn handle_new_mining_job( - &mut self, - _m: NewMiningJob, - ) -> Result, Error> { - todo!() - //// One and only one downstream cause the message is not extended - //match &self - // .downstream_selector - // .get_downstreams_in_channel(m.channel_id) - //{ - // Some(downstreams) => { - // let downstream = &downstreams[0]; - // crate::add_job_id( - // m.job_id, - // self.id, - // downstream.safe_lock(|d| d.prev_job_id).unwrap(), - // ); - // Ok(SendTo::RelaySameMessageToRemote(downstream.clone())) - // } - // None => Err(Error::NoDownstreamsConnected), - //} - } - - fn handle_new_extended_mining_job( - &mut self, - m: NewExtendedMiningJob, - ) -> Result, Error> { - info!( - "Received new extended mining job for channel id: {} with job id: {} is_future: {}", - m.channel_id, - m.job_id, - m.is_future() - ); - debug!("NewExtendedMiningJob: {}", m); - let mut res = vec![]; - match &mut self.channel_kind { - ChannelKind::Group(group) => { - group.on_new_extended_mining_job(&m); - let downstreams = self - .downstream_selector - .get_downstreams_in_channel(m.channel_id) - .ok_or(Error::NoDownstreamsConnected)?; - for downstream in downstreams { - match downstream.safe_lock(|r| r.get_channel().clone()).unwrap() { - Channel::DownstreamHomUpstreamGroup { - channel_id, - group_id, - .. - } => { - let message = - group.last_received_job_to_standard_job(channel_id, group_id)?; - - res.push(SendTo::RelayNewMessageToRemote( - downstream.clone(), - Mining::NewMiningJob(message), - )); - } - _ => unreachable!(), - } - } - } - ChannelKind::Extended(Some(factory)) => { - if let Ok(messages) = factory.on_new_extended_mining_job(m.clone().as_static()) { - let mut new_p_hash_added = false; - let is_future = m.is_future(); - let original_job_id = m.job_id; - if is_future { - self.job_up_to_down_ids.insert(original_job_id, vec![]); - }; - for (id, message) in messages { - match &message { - Mining::NewExtendedMiningJob(_) => { - // TODO implement it if support for non HOM downstream is needed - todo!() - } - Mining::NewMiningJob(m) => { - let downstream = self - .downstream_selector - .downstream_from_channel_id(id) - .ok_or(Error::NoDownstreamsConnected)?; - if is_future { - let ids = - self.job_up_to_down_ids.get_mut(&original_job_id).unwrap(); - ids.push((downstream.clone(), m.job_id)); - }; - res.push(SendTo::RelayNewMessageToRemote( - downstream, - Mining::NewMiningJob(m.clone()), - )); - } - Mining::SetNewPrevHash(m) => { - if !new_p_hash_added { - let downstreams = - self.downstream_selector.get_all_downstreams(); - for downstream in downstreams { - res.push(SendTo::RelayNewMessageToRemote( - downstream.clone(), - Mining::SetNewPrevHash(m.clone()), - )); - } - new_p_hash_added = true; - } - } - _ => todo!(), - } - } - } else { - todo!() - } - } - ChannelKind::Extended(None) => panic!("Factory not initialized"), - } - Ok(SendTo::Multiple(res)) - } - - fn handle_set_new_prev_hash( - &mut self, - m: SetNewPrevHash, - ) -> Result, Error> { - info!( - "Received SetNewPrevHash channel id: {}, job id: {}", - m.channel_id, m.job_id - ); - debug!("SetNewPrevHash: {}", m); - match &mut self.channel_kind { - ChannelKind::Group(group) => { - group.update_new_prev_hash(&m); - - let downstreams = self - .downstream_selector - .get_downstreams_in_channel(m.channel_id) - .ok_or(Error::NoDownstreamsConnected)?; - - let mut res = vec![]; - for downstream in downstreams { - let message = Mining::SetNewPrevHash(m.clone().into_static()); - res.push(SendTo::RelayNewMessageToRemote(downstream.clone(), message)); - } - Ok(SendTo::Multiple(res)) - } - ChannelKind::Extended(factory) => { - if factory - .as_mut() - .expect("Factory not initialized") - .on_new_prev_hash(m.clone().into_static()) - .is_ok() - { - self.on_p_hash(m.into_static().clone()) - } else { - todo!() - } - } - } - } - - fn handle_set_custom_mining_job_success( - &mut self, - m: SetCustomMiningJobSuccess, - ) -> Result, Error> { - info!( - "Received SetCustomMiningJobSuccess for channel id: {} for job id: {}", - m.channel_id, m.job_id - ); - debug!("SetCustomMiningJobSuccess: {}", m); - Ok(SendTo::None(None)) - } - - fn handle_set_custom_mining_job_error( - &mut self, - _m: SetCustomMiningJobError, - ) -> Result, Error> { - todo!("560") - } - - fn handle_set_target(&mut self, _m: SetTarget) -> Result, Error> { - todo!("570") - } - - fn handle_set_group_channel( - &mut self, - _m: SetGroupChannel, - ) -> Result, Error> { - todo!() - } -} - -pub async fn scan( - nodes: Vec>>, - min_version: u16, - max_version: u16, -) -> Vec>> { - let res = Arc::new(Mutex::new(Vec::with_capacity(nodes.len()))); - let spawn_tasks: Vec> = nodes - .iter() - .map(|node| { - let node = node.clone(); - let cloned = res.clone(); - task::spawn(async move { - if let Err(e) = UpstreamMiningNode::setup_flag_and_version( - node.clone(), - None, - min_version, - max_version, - ) - .await - { - error!("{:?}", e) - } else { - cloned.safe_lock(|r| r.push(node.clone())).unwrap(); - } - }) - }) - .collect(); - for task in spawn_tasks { - task.await.unwrap(); - } - res.safe_lock(|r| r.clone()).unwrap() -} - -impl IsUpstream for UpstreamMiningNode { - fn get_version(&self) -> u16 { - self.sv2_connection.unwrap().version - } - - fn get_flags(&self) -> u32 { - self.sv2_connection.unwrap().setup_connection_flags - } - - fn get_supported_protocols(&self) -> Vec { - vec![Protocol::MiningProtocol] - } - - fn get_id(&self) -> u32 { - self.id - } - - fn get_mapper(&mut self) -> Option<&mut RequestIdMapper> { - Some(&mut self.request_id_mapper) - } -} -impl IsMiningUpstream for UpstreamMiningNode { - fn total_hash_rate(&self) -> u64 { - self.total_hash_rate - } - fn add_hash_rate(&mut self, to_add: u64) { - self.total_hash_rate += to_add; - } - fn get_opened_channels(&mut self) -> &mut Vec { - todo!() - } - fn update_channels(&mut self, _channel: UpstreamChannel) { - todo!() - } -} - -#[cfg(test)] -mod tests { - use std::net::{IpAddr, Ipv4Addr}; - - use super::*; - - #[test] - fn new_upstream_minining_node() { - let id = 0; - let address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - let authority_public_key = [ - 215, 11, 47, 78, 34, 232, 25, 192, 195, 168, 170, 209, 95, 181, 40, 114, 154, 226, 176, - 190, 90, 169, 238, 89, 191, 183, 97, 63, 194, 119, 11, 31, - ]; - let ids = Arc::new(Mutex::new(GroupId::new())); - let channel_ids = Arc::new(Mutex::new(Id::new())); - let actual = UpstreamMiningNode::new( - id, - address, - authority_public_key, - super::super::ChannelKind::Group, - ids, - channel_ids, - 10.0, - None, - None, - 100_000.0, - false, - ); - - assert_eq!(actual.id, id); - - assert_eq!(actual.total_hash_rate, 0); - assert_eq!(actual.address, address); - - if actual.connection.is_some() { - panic!("`UpstreamMiningNode::connection` should be `None` on call to `UpstreamMiningNode::new()`"); - } - - if actual.sv2_connection.is_some() { - panic!("`UpstreamMiningNode::sv2_connection` should be `None` on call to `UpstreamMiningNode::new()`"); - } - - // How to test - // assert_eq!(actual.downstream_selector, ProxyRemoteSelector::new()); - - assert_eq!(actual.authority_public_key, authority_public_key); - assert!(actual.channel_id_to_job_dispatcher.is_empty()); - assert_eq!(actual.request_id_mapper, RequestIdMapper::new()); - } -} diff --git a/roles/mining-proxy/src/main.rs b/roles/mining-proxy/src/main.rs deleted file mode 100644 index 59bfde9a05..0000000000 --- a/roles/mining-proxy/src/main.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Configurable Sv2 it support extended and group channel -//! Upstream means another proxy or a pool -//! Downstream means another proxy or a mining device -//! -//! UpstreamMining is the trait that a proxy must implement in order to -//! understand Downstream mining messages. -//! -//! DownstreamMining is the trait that a proxy must implement in order to -//! understand Upstream mining messages -//! -//! Same thing for DownstreamCommon and UpstreamCommon but for common messages -//! -//! DownstreamMiningNode implement both UpstreamMining and UpstreamCommon -//! -//! UpstreamMiningNode implement both DownstreamMining and DownstreamCommon -//! -//! A Downstream that signal the capacity to handle group channels can open more than one channel. -//! A Downstream that signal the incapacity to handle group channels can open only one channel. -use tracing::error; - -use mining_proxy_sv2::start_mining_proxy; - -mod args; -use args::process_cli_args; - -/// 1. the proxy scan all the upstreams and map them -/// 2. downstream open a connection with proxy -/// 3. downstream send SetupConnection -/// 4. a mining_channels::Upstream is created -/// 5. upstream_mining::UpstreamMiningNodes is used to pair this downstream with the most suitable -/// upstream -/// 6. mining_channels::Upstream create a new downstream_mining::DownstreamMiningNode embedding -/// itself in it -/// 7. normal operation between the paired downstream_mining::DownstreamMiningNode and -/// upstream_mining::UpstreamMiningNode begin -#[tokio::main] -async fn main() { - tracing_subscriber::fmt::init(); - let config = match process_cli_args() { - Ok(c) => c, - Err(e) => { - error!("Mining Proxy Config error: {}", e); - return; - } - }; - start_mining_proxy(config).await; -} diff --git a/scripts/coverage-roles.sh b/scripts/coverage-roles.sh index 6ba4df60c6..a7b5de2fd4 100755 --- a/scripts/coverage-roles.sh +++ b/scripts/coverage-roles.sh @@ -10,7 +10,6 @@ cd roles tarpaulin crates=( - "mining-proxy" "pool" "test-utils/mining-device" "test-utils/mining-device-sv1" diff --git a/test/integration-tests/Cargo.lock b/test/integration-tests/Cargo.lock index ceb5385fd1..104ac721d9 100644 --- a/test/integration-tests/Cargo.lock +++ b/test/integration-tests/Cargo.lock @@ -1209,7 +1209,6 @@ dependencies = [ "key-utils", "mining_device", "mining_device_sv1", - "mining_proxy_sv2", "minreq", "once_cell", "pool_sv2", @@ -1416,26 +1415,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "mining_proxy_sv2" -version = "0.1.3" -dependencies = [ - "async-channel", - "async-recursion 0.3.2", - "buffer_sv2", - "clap", - "config", - "futures", - "key-utils", - "nohash-hasher", - "once_cell", - "serde", - "stratum-common", - "tokio", - "tracing", - "tracing-subscriber", -] - [[package]] name = "mining_sv2" version = "4.0.0" diff --git a/test/integration-tests/Cargo.toml b/test/integration-tests/Cargo.toml index e64b6d2f2b..fc6972455c 100644 --- a/test/integration-tests/Cargo.toml +++ b/test/integration-tests/Cargo.toml @@ -27,7 +27,6 @@ jd_server = { path = "../../roles/jd-server" } key-utils = { path = "../../utils/key-utils" } mining_device = { path = "../../roles/test-utils/mining-device" } mining_device_sv1 = { path = "../../roles/test-utils/mining-device-sv1" } -mining_proxy_sv2 = { path = "../../roles/mining-proxy" } pool_sv2 = { path = "../../roles/pool" } config-helpers = { path = "../../roles/roles-utils/config-helpers" } stratum-common = { path = "../../common" , features = ["with_network_helpers", "sv1"]} diff --git a/test/integration-tests/lib/mod.rs b/test/integration-tests/lib/mod.rs index e3dd2a18fb..13fd69463d 100644 --- a/test/integration-tests/lib/mod.rs +++ b/test/integration-tests/lib/mod.rs @@ -10,7 +10,6 @@ use rand::{rng, Rng}; use std::{ convert::{TryFrom, TryInto}, net::SocketAddr, - str::FromStr, sync::Once, }; use translator_sv2::TranslatorSv2; @@ -342,37 +341,6 @@ pub fn start_mining_device_sv2( }); } -pub fn start_mining_sv2_proxy(upstreams: &[SocketAddr]) -> SocketAddr { - use mining_proxy_sv2::{ChannelKind, UpstreamMiningValues}; - let upstreams = upstreams - .iter() - .map(|upstream| UpstreamMiningValues { - address: upstream.ip().to_string(), - port: upstream.port(), - pub_key: Secp256k1PublicKey::from_str( - "9auqWEzQDVyd2oe1JVGFLMLHZtCo2FFqZwtKA5gd9xbuEu7PH72", - ) - .unwrap(), - channel_kind: ChannelKind::Extended, - }) - .collect(); - let mining_proxy_listening_address = get_available_address(); - let config = mining_proxy_sv2::MiningProxyConfig { - upstreams, - listen_address: mining_proxy_listening_address.ip().to_string(), - listen_mining_port: mining_proxy_listening_address.port(), - max_supported_version: 2, - min_supported_version: 2, - downstream_share_per_minute: 1.0, - expected_total_downstream_hr: 10_000.0, - reconnect: true, - }; - tokio::spawn(async move { - mining_proxy_sv2::start_mining_proxy(config).await; - }); - mining_proxy_listening_address -} - #[cfg(feature = "sv1")] pub fn start_sv1_sniffer(upstream_address: SocketAddr) -> (sv1_sniffer::SnifferSV1, SocketAddr) { let listening_address = get_available_address(); From f0957e136d76c8e2a96560fd2b38d4b720317efa Mon Sep 17 00:00:00 2001 From: plebhash Date: Wed, 25 Jun 2025 15:07:41 -0300 Subject: [PATCH 069/338] deprecate roles_logic_sv2::common_properties --- .../src/channel_logic/channel_factory.rs | 683 +----------------- .../roles-logic-sv2/src/channel_logic/mod.rs | 1 - .../src/channel_logic/proxy_group_channel.rs | 252 ------- .../roles-logic-sv2/src/common_properties.rs | 344 --------- protocols/v2/roles-logic-sv2/src/errors.rs | 6 - .../v2/roles-logic-sv2/src/handlers/mining.rs | 10 +- .../v2/roles-logic-sv2/src/job_dispatcher.rs | 472 +----------- protocols/v2/roles-logic-sv2/src/lib.rs | 1 - roles/jd-client/src/lib/downstream.rs | 74 +- .../src/lib/upstream_sv2/upstream.rs | 45 -- .../src/lib/mining_pool/message_handler.rs | 2 +- roles/pool/src/lib/mining_pool/mod.rs | 22 +- .../src/lib/mining_pool/setup_connection.rs | 16 +- .../src/lib/downstream_sv1/downstream.rs | 18 +- roles/translator/src/lib/proxy/bridge.rs | 87 --- .../src/lib/upstream_sv2/upstream.rs | 45 -- 16 files changed, 50 insertions(+), 2028 deletions(-) delete mode 100644 protocols/v2/roles-logic-sv2/src/channel_logic/proxy_group_channel.rs delete mode 100644 protocols/v2/roles-logic-sv2/src/common_properties.rs diff --git a/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs b/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs index 2710f3095b..0293f383d2 100644 --- a/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs +++ b/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs @@ -2,9 +2,7 @@ //! //! This module contains logic for creating and managing channels. -use super::extended_to_standard_job; use crate::{ - common_properties::StandardChannel, job_creator::{self, JobsCreators}, parsers::Mining, utils::{GroupId, Id, Mutex}, @@ -13,10 +11,9 @@ use crate::{ use codec_sv2::binary_sv2; use mining_sv2::{ - ExtendedExtranonce, NewExtendedMiningJob, NewMiningJob, OpenExtendedMiningChannelSuccess, - OpenMiningChannelError, OpenStandardMiningChannelSuccess, SetCustomMiningJob, - SetCustomMiningJobSuccess, SetNewPrevHash, SubmitSharesError, SubmitSharesExtended, - SubmitSharesStandard, Target, + ExtendedExtranonce, NewExtendedMiningJob, OpenExtendedMiningChannelSuccess, + OpenMiningChannelError, SetCustomMiningJob, SetCustomMiningJobSuccess, SetNewPrevHash, + SubmitSharesError, SubmitSharesExtended, SubmitSharesStandard, Target, }; use hex::DisplayHex; @@ -210,9 +207,6 @@ impl Share { /// Basic logic shared between all the channel factories struct ChannelFactory { ids: Arc>, - standard_channels_for_non_hom_downstreams: - HashMap>, - standard_channels_for_hom_downstreams: HashMap>, extended_channels: HashMap, BuildNoHashHasher>, extranonces: ExtendedExtranonce, @@ -231,25 +225,6 @@ struct ChannelFactory { } impl ChannelFactory { - pub fn add_standard_channel( - &mut self, - request_id: u32, - downstream_hash_rate: f32, - is_header_only: bool, - id: u32, - ) -> Result, Error> { - match is_header_only { - true => { - self.new_standard_channel_for_hom_downstream(request_id, downstream_hash_rate, id) - } - false => self.new_standard_channel_for_non_hom_downstream( - request_id, - downstream_hash_rate, - id, - ), - } - } - /// Called when a `OpenExtendedMiningChannel` message is received. /// Here we save the downstream's target (based on hashrate) and the /// channel's extranonce details before returning the relevant SV2 mining messages @@ -348,314 +323,6 @@ impl ChannelFactory { self.extended_channels.insert(channel_id, success.clone()); Some(()) } - /// Called when an `OpenStandardChannel` message is received for a header only mining channel. - /// Here we save the downstream's target (based on hashrate) and and the - /// channel's extranonce details before returning the relevant SV2 mining messages - /// to be sent downstream. - fn new_standard_channel_for_hom_downstream( - &mut self, - request_id: u32, - downstream_hash_rate: f32, - id: u32, - ) -> Result, Error> { - let hom_group_id = 0; - let mut result = vec![]; - let channel_id = id; - let target = match crate::utils::hash_rate_to_target( - downstream_hash_rate.into(), - self.share_per_min.into(), - ) { - Ok(target) => target, - Err(e) => { - error!( - "Impossible to get target: {:?}. Request id: {:?}", - e, request_id - ); - return Err(e); - } - }; - let extranonce = self - .extranonces - .next_prefix_standard() - .map_err(|_| Error::ExtranonceSpaceEnded)?; - let standard_channel = StandardChannel { - channel_id, - group_id: hom_group_id, - target: target.clone().into(), - extranonce: extranonce.clone(), - }; - self.standard_channels_for_hom_downstreams - .insert(channel_id, standard_channel); - - // First message to be sent is OpenStandardMiningChannelSuccess - result.push(Mining::OpenStandardMiningChannelSuccess( - OpenStandardMiningChannelSuccess { - request_id: request_id.into(), - channel_id, - target, - extranonce_prefix: extranonce.into(), - group_channel_id: hom_group_id, - }, - )); - self.prepare_standard_jobs_and_p_hash(&mut result, channel_id)?; - self.channel_to_group_id.insert(channel_id, hom_group_id); - Ok(result) - } - - /// This function is called when downstream have a group channel - /// should not all standard channel's be non HOM?? - fn new_standard_channel_for_non_hom_downstream( - &mut self, - request_id: u32, - downstream_hash_rate: f32, - group_id: u32, - ) -> Result, Error> { - let mut result = vec![]; - let channel_id = self - .ids - .safe_lock(|ids| ids.new_channel_id(group_id)) - .unwrap(); - let complete_id = GroupId::into_complete_id(group_id, channel_id); - let target = match crate::utils::hash_rate_to_target( - downstream_hash_rate.into(), - self.share_per_min.into(), - ) { - Ok(target_) => target_, - Err(e) => { - info!( - "Impossible to get target: {:?}. Request id: {:?}", - e, request_id - ); - return Err(e); - } - }; - let extranonce = self - .extranonces - .next_prefix_standard() - .map_err(|_| Error::ExtranonceSpaceEnded)?; - let standard_channel = StandardChannel { - channel_id, - group_id, - target: target.clone().into(), - extranonce: extranonce.clone(), - }; - self.standard_channels_for_non_hom_downstreams - .insert(complete_id, standard_channel); - - // First message to be sent is OpenStandardMiningChannelSuccess - result.push(Mining::OpenStandardMiningChannelSuccess( - OpenStandardMiningChannelSuccess { - request_id: request_id.into(), - channel_id, - target, - extranonce_prefix: extranonce.into(), - group_channel_id: group_id, - }, - )); - self.prepare_jobs_and_p_hash(&mut result, complete_id); - self.channel_to_group_id.insert(channel_id, group_id); - Ok(result) - } - - // When a hom downstream opens a channel, we use this function to prepare all the standard jobs - // (future and not) that we need to be sent downstream - fn prepare_standard_jobs_and_p_hash( - &mut self, - result: &mut Vec, - channel_id: u32, - ) -> Result<(), Error> { - // Safe cause the function is private and we always add the channel before calling this - // funtion - let standard_channel = self - .standard_channels_for_hom_downstreams - .get(&channel_id) - .unwrap(); - // OPTIMIZATION this could be memoized somewhere cause is very likely that we will receive a - // lot od OpenStandardMiningChannel requests consecutively - let job_id = self.job_ids.next(); - let future_jobs: Option>> = self - .future_jobs - .iter() - .map(|j| { - extended_to_standard_job( - &j.0, - &standard_channel.extranonce.clone().to_vec()[..], - standard_channel.channel_id, - Some(job_id), - ) - }) - .collect(); - - // OPTIMIZATION the extranonce is cloned so many time but maybe is avoidable? - let last_valid_job = match &self.last_valid_job { - Some((j, _)) => Some( - extended_to_standard_job( - j, - &standard_channel.extranonce.clone().to_vec(), - standard_channel.channel_id, - Some(self.job_ids.next()), - ) - .ok_or(Error::ImpossibleToCalculateMerkleRoot)?, - ), - None => None, - }; - - // This is the same thing of just check if there is a prev hash add it to result. If there - // is last_job add it to result and add each future job to result. - // But using the pattern match is more clear how each option is handled - match ( - &self.last_prev_hash, - last_valid_job, - self.future_jobs.is_empty(), - ) { - // If we do not have anything just do nothing - (None, None, true) => Ok(()), - // If we have only future jobs we need to send them all after the - // SetupConnectionSuccess message - (None, None, false) => { - // Safe unwrap cause we check that self.future_jobs is not empty - let mut future_jobs = future_jobs.unwrap(); - while let Some(job) = future_jobs.pop() { - result.push(Mining::NewMiningJob(job)); - } - Ok(()) - } - // If we have just a prev hash we need to send it after the SetupConnectionSuccess - // message - (Some((prev_h, _)), None, true) => { - let prev_h = prev_h.into_set_p_hash(channel_id, None); - result.push(Mining::SetNewPrevHash(prev_h.clone())); - Ok(()) - } - // If we have a prev hash and a last valid job we need to send new mining job before the - // prev hash - (Some((prev_h, _)), Some(mut job), true) => { - let prev_h = prev_h.into_set_p_hash(channel_id, Some(job.job_id)); - - // set future_job to true - job.set_future(); - - result.push(Mining::NewMiningJob(job)); - result.push(Mining::SetNewPrevHash(prev_h.clone())); - Ok(()) - } - // If we have everything we need, send the future jobs and the the prev hash - (Some((prev_h, _)), Some(mut job), false) => { - let prev_h = prev_h.into_set_p_hash(channel_id, Some(job.job_id)); - - job.set_future(); - - result.push(Mining::NewMiningJob(job)); - result.push(Mining::SetNewPrevHash(prev_h.clone())); - - // Safe unwrap cause we check that self.future_jobs is not empty - let mut future_jobs = future_jobs.unwrap(); - - while let Some(job) = future_jobs.pop() { - result.push(Mining::NewMiningJob(job)); - } - Ok(()) - } - // This can not happen because we can not have a valid job without a prev hash - (None, Some(_), true) => unreachable!(), - // This can not happen because we can not have a valid job without a prev hash - (None, Some(_), false) => unreachable!(), - // This can not happen because as soon as a prev hash is received we flush the future - // jobs - (Some(_), None, false) => unreachable!(), - } - } - - // When a new non HOM downstream opens a channel, we use this function to prepare all the - // extended jobs (future and non) and the prev hash that we need to send downstream - fn prepare_jobs_and_p_hash(&mut self, result: &mut Vec, complete_id: u64) { - // If group is 0 it means that we are preparing jobs and p hash for a non HOM downstream - // that want to open a new extended channel in that case we want to use the channel id - // TODO verify that this is true also for the case where the channel factory is in a proxy - // and not in a pool. - let group_id = match GroupId::into_group_id(complete_id) { - 0 => GroupId::into_channel_id(complete_id), - a => a, - }; - // This is the same thing of just check if there is a prev hash add it to result if there - // is last_job add it to result and add each future job to result. - // But using the pattern match is more clear how each option is handled - match ( - self.last_prev_hash.as_mut(), - self.last_valid_job.as_mut(), - self.future_jobs.is_empty(), - ) { - // If we do not have anything just do nothing - (None, None, true) => (), - // If we have only future jobs we need to send them all after the - // SetupConnectionSuccess message - (None, None, false) => { - for (job, group_id_job_sent) in &mut self.future_jobs { - if !group_id_job_sent.contains(&group_id) { - let mut job = job.clone(); - job.channel_id = group_id; - group_id_job_sent.push(group_id); - result.push(Mining::NewExtendedMiningJob(job)); - } - } - } - // If we have just a prev hash we need to send it after the SetupConnectionSuccess - // message - (Some((prev_h, group_id_p_hash_sent)), None, true) => { - if !group_id_p_hash_sent.contains(&group_id) { - let prev_h = prev_h.into_set_p_hash(group_id, None); - group_id_p_hash_sent.push(group_id); - result.push(Mining::SetNewPrevHash(prev_h.clone())); - } - } - // If we have a prev hash and a last valid job we need to send before the prev hash and - // the the valid job - (Some((prev_h, group_id_p_hash_sent)), Some((job, group_id_job_sent)), true) => { - if !group_id_p_hash_sent.contains(&group_id) { - let prev_h = prev_h.into_set_p_hash(group_id, Some(job.job_id)); - group_id_p_hash_sent.push(group_id); - result.push(Mining::SetNewPrevHash(prev_h)); - } - if !group_id_job_sent.contains(&group_id) { - let mut job = job.clone(); - job.channel_id = group_id; - group_id_job_sent.push(group_id); - result.push(Mining::NewExtendedMiningJob(job)); - } - } - // If we have everything we need, send before the prev hash and then all the jobs - (Some((prev_h, group_id_p_hash_sent)), Some((job, group_id_job_sent)), false) => { - if !group_id_p_hash_sent.contains(&group_id) { - let prev_h = prev_h.into_set_p_hash(group_id, Some(job.job_id)); - group_id_p_hash_sent.push(group_id); - result.push(Mining::SetNewPrevHash(prev_h)); - } - - if !group_id_job_sent.contains(&group_id) { - let mut job = job.clone(); - job.channel_id = group_id; - group_id_job_sent.push(group_id); - result.push(Mining::NewExtendedMiningJob(job)); - } - - for (job, group_id_future_j_sent) in &mut self.future_jobs { - if !group_id_future_j_sent.contains(&group_id) { - let mut job = job.clone(); - job.channel_id = group_id; - group_id_future_j_sent.push(group_id); - result.push(Mining::NewExtendedMiningJob(job)); - } - } - } - // This can not happen because we can not have a valid job without a prev hash - (None, Some(_), true) => unreachable!(), - // This can not happen because we can not have a valid job without a prev hash - (None, Some(_), false) => unreachable!(), - // This can not happen because as soon as a prev hash is received we flush the future - // jobs - (Some(_), None, false) => unreachable!(), - } - } /// Called when a new prev hash is received. If the respective job is available in the future /// job queue, we move the future job into the valid job slot and store the prev hash as the @@ -675,14 +342,7 @@ impl ChannelFactory { } self.future_jobs = vec![]; self.last_prev_hash_ = Some(crate::utils::u256_to_block_hash(m.prev_hash.clone())); - let mut ids = vec![]; - for complete_id in self.standard_channels_for_non_hom_downstreams.keys() { - let group_id = GroupId::into_group_id(*complete_id); - if !ids.contains(&group_id) { - ids.push(group_id) - } - } - self.last_prev_hash = Some((m, ids)); + self.last_prev_hash = Some((m, vec![])); Ok(()) } @@ -696,28 +356,14 @@ impl ChannelFactory { (true, _) => { let mut result = HashMap::with_hasher(BuildNoHashHasher::default()); self.prepare_jobs_for_downstream_on_new_extended(&mut result, &m)?; - let mut ids = vec![]; - for complete_id in self.standard_channels_for_non_hom_downstreams.keys() { - let group_id = GroupId::into_group_id(*complete_id); - if !ids.contains(&group_id) { - ids.push(group_id) - } - } - self.future_jobs.push((m, ids)); + self.future_jobs.push((m, vec![])); Ok(result) } (false, Some(_)) => { let mut result = HashMap::with_hasher(BuildNoHashHasher::default()); self.prepare_jobs_for_downstream_on_new_extended(&mut result, &m)?; // If job is not future it must always be paired with the last received prev hash - let mut ids = vec![]; - for complete_id in self.standard_channels_for_non_hom_downstreams.keys() { - let group_id = GroupId::into_group_id(*complete_id); - if !ids.contains(&group_id) { - ids.push(group_id) - } - } - self.last_valid_job = Some((m, ids)); + self.last_valid_job = Some((m, vec![])); if let Some((_p_hash, _)) = &self.last_prev_hash { Ok(result) } else { @@ -737,26 +383,6 @@ impl ChannelFactory { result: &mut HashMap>, m: &NewExtendedMiningJob<'static>, ) -> Result<(), Error> { - for (id, channel) in &self.standard_channels_for_hom_downstreams { - let job_id = self.job_ids.next(); - let mut standard_job = extended_to_standard_job( - m, - &channel.extranonce.clone().to_vec()[..], - *id, - Some(job_id), - ) - .unwrap(); - standard_job.channel_id = *id; - let standard_job = Mining::NewMiningJob(standard_job); - result.insert(*id, standard_job); - } - for id in self.standard_channels_for_non_hom_downstreams.keys() { - let group_id = GroupId::into_group_id(*id); - let mut extended = m.clone(); - extended.channel_id = group_id; - let extended_job = Mining::NewExtendedMiningJob(extended); - result.insert(group_id, extended_job); - } for id in self.extended_channels.keys() { let mut extended = m.clone(); extended.channel_id = *id; @@ -941,38 +567,9 @@ impl ChannelFactory { } Some((dowstream_target, extranonce)) } - Share::Standard((share, group_id)) => match &self.kind { - ExtendedChannelKind::Pool => { - let complete_id = GroupId::into_complete_id(*group_id, share.channel_id); - let mut channel = self - .standard_channels_for_non_hom_downstreams - .get(&complete_id); - if channel.is_none() { - channel = self - .standard_channels_for_hom_downstreams - .get(&share.channel_id); - }; - Some(( - channel?.target.clone(), - channel?.extranonce.clone().to_vec(), - )) - } - ExtendedChannelKind::Proxy { .. } | ExtendedChannelKind::ProxyJd { .. } => { - let complete_id = GroupId::into_complete_id(*group_id, share.channel_id); - let mut channel = self - .standard_channels_for_non_hom_downstreams - .get(&complete_id); - if channel.is_none() { - channel = self - .standard_channels_for_hom_downstreams - .get(&share.channel_id); - }; - Some(( - channel?.target.clone(), - channel?.extranonce.clone().to_vec(), - )) - } - }, + Share::Standard((_share, _group_id)) => { + unimplemented!() + } } } /// Updates the downstream target for the given channel_id @@ -1006,12 +603,6 @@ impl PoolChannelFactory { ) -> Self { let inner = ChannelFactory { ids, - standard_channels_for_non_hom_downstreams: HashMap::with_hasher( - BuildNoHashHasher::default(), - ), - standard_channels_for_hom_downstreams: HashMap::with_hasher( - BuildNoHashHasher::default(), - ), extended_channels: HashMap::with_hasher(BuildNoHashHasher::default()), extranonces, share_per_min, @@ -1033,18 +624,6 @@ impl PoolChannelFactory { } } - /// Calls [`ChannelFactory::add_standard_channel`] - pub fn add_standard_channel( - &mut self, - request_id: u32, - downstream_hash_rate: f32, - is_header_only: bool, - id: u32, - ) -> Result, Error> { - self.inner - .add_standard_channel(request_id, downstream_hash_rate, is_header_only, id) - } - /// Calls [`ChannelFactory::new_extended_channel`] pub fn new_extended_channel( &mut self, @@ -1355,12 +934,6 @@ impl ProxyExtendedChannelFactory { }; let inner = ChannelFactory { ids, - standard_channels_for_non_hom_downstreams: HashMap::with_hasher( - BuildNoHashHasher::default(), - ), - standard_channels_for_hom_downstreams: HashMap::with_hasher( - BuildNoHashHasher::default(), - ), extended_channels: HashMap::with_hasher(BuildNoHashHasher::default()), extranonces, share_per_min, @@ -1381,18 +954,6 @@ impl ProxyExtendedChannelFactory { } } - /// Calls [`ChannelFactory::add_standard_channel`] - pub fn add_standard_channel( - &mut self, - request_id: u32, - downstream_hash_rate: f32, - id_header_only: bool, - id: u32, - ) -> Result, Error> { - self.inner - .add_standard_channel(request_id, downstream_hash_rate, id_header_only, id) - } - /// Calls [`ChannelFactory::new_extended_channel`] pub fn new_extended_channel( &mut self, @@ -1807,229 +1368,3 @@ impl ExtendedChannelKind { } } } -#[cfg(test)] -mod test { - use super::*; - use bitcoin::{Amount, PublicKey, Target, TxOut, WPubkeyHash}; - use codec_sv2::binary_sv2::{Seq0255, B064K, U256}; - use mining_sv2::OpenStandardMiningChannel; - - const BLOCK_REWARD: u64 = 2_000_000_000; - - // Block 1296 data - // 01000000 - // c1397d4a33adeeb3383803e9ac3db4b2c2c9d6737cbabc13a534d24600000000 - // 89687b66140ac9874656270e066ed7ef81d5133ada2d0133f09322a87b161738 - // 4eb87749 - // ffff001d - // 07cacb0e - const _PUB_K: &str = "04c6d0969c2d98a5c19ba7c36c7937c5edbd60ff2a01397c4afe54f16cd641667ea0049ba6f9e1796ba3c8e49e1b504c532ebbaaa1010c3f7d9b83a8ea7fd800e2"; - const _BLOCK_HASH: &str = "000000009a4aed3e8ba7a978c6b50fea886fb496d66e696090a91d527200b002"; - const VERSION: u32 = 1; - // version 01000000 - // inputs 01 - // prev out 0000000000000000000000000000000000000000000000000000000000000000ffffffff - // script len 07 - // script 04ffff001d0177 - // sequence ffffffff - // n inputs 01 - // amunt 00f2052a01000000 - // out lne 43 - // push 41 - // pub k 04c6d0969c2d98a5c19ba7c36c7937c5edbd60ff2a01397c4afe54f16cd641667ea0049ba6f9e1796ba3c8e49e1b504c532ebbaaa1010c3f7d9b83a8ea7fd800e2 - // checksig ac - // locktime 00000000 - const COINBASE: &str = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0177ffffffff0100f2052a01000000434104c6d0969c2d98a5c19ba7c36c7937c5edbd60ff2a01397c4afe54f16cd641667ea0049ba6f9e1796ba3c8e49e1b504c532ebbaaa1010c3f7d9b83a8ea7fd800e2ac00000000"; - const COINBASE_OUTPUT: &str = "4104c6d0969c2d98a5c19ba7c36c7937c5edbd60ff2a01397c4afe54f16cd641667ea0049ba6f9e1796ba3c8e49e1b504c532ebbaaa1010c3f7d9b83a8ea7fd800e2ac"; - const MERKLE_PATH: &str = "59bf8acbc9d60dfae841abecc3882b4181f2bdd8ac6c1d94001165ab3aef50b0"; - const NONCE: &str = "07cacb0e"; - const NTIME: &str = "4eb87749"; - - // Prev block data (1295) - //01000000 - //cf578a234f330c287354e24234ff6b86d6ab9e4ddd3e5ba71a6bcbf600000000 - //72d12b99bdb63762bedc5db30bcffbd7903721bc736dd683de37b1a3632f9000 - //time: 2e8c7749 -> 49778c2e -> 1232571438 - //nbits: ffff001d -> 4294901789 - //29444816 - const PREV_HASH: &str = "0000000046d234a513bcba7c73d6c9c2b2b43dace9033838b3eead334a7d39c1"; - const PREV_HEADER_TIMESTAMP: u32 = 1232571438; - const PREV_HEADER_NBITS: u32 = 486604799; - - fn _get_pub_key_hash() -> WPubkeyHash { - let into_bin = decode_hex(_PUB_K).unwrap(); - let pk = PublicKey::from_slice(&into_bin[..]); - let hash = pk.unwrap().pubkey_hash(); - WPubkeyHash::from_raw_hash(hash.to_raw_hash()) - } - - fn get_coinbase() -> (Vec, Vec, Vec) { - let parsed = decode_hex(COINBASE).unwrap(); - // Coinbase prefix in Sv2 is the bip34 block height in this tx there is no prefix - let prefix = parsed[42..42].to_vec(); - let extranonce = parsed[42..49].to_vec(); - let suffix = parsed[49..].to_vec(); - (prefix, extranonce, suffix) - } - - fn get_coinbase_outputs() -> B064K<'static> { - decode_hex(COINBASE_OUTPUT).unwrap().try_into().unwrap() - } - - fn get_merkle_path() -> Seq0255<'static, U256<'static>> { - let mut m_path = decode_hex(MERKLE_PATH).unwrap(); - m_path.reverse(); - let path: U256 = m_path.try_into().unwrap(); - vec![path].try_into().unwrap() - } - - fn nbit_to_target(nbit: u32) -> U256<'static> { - let mut target = Target::from_compact(CompactTarget::from_consensus(nbit)) - .to_be_bytes() - .to_vec(); - target.reverse(); - target.try_into().unwrap() - } - - fn decode_hex(s: &str) -> Result, std::num::ParseIntError> { - (0..s.len()) - .step_by(2) - .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) - .collect() - } - - #[test] - fn test_complete_mining_round() { - let (prefix, coinbase_extranonce, _) = get_coinbase(); - - // Initialize a Channel of type Pool - let out = TxOut {value: Amount::from_sat(BLOCK_REWARD), script_pubkey: decode_hex("4104c6d0969c2d98a5c19ba7c36c7937c5edbd60ff2a01397c4afe54f16cd641667ea0049ba6f9e1796ba3c8e49e1b504c532ebbaaa1010c3f7d9b83a8ea7fd800e2ac").unwrap().into()}; - let creator = JobsCreators::new(7); - let share_per_min = 1.0; - // Create an ExtendedExtranonce of len 7: - // upstream part is 0 bytes cause we are a pool so no more upstreams - // self part is 7 bytes - // downstream part is 0 cause in the test the downstream is HOM so we do not need to - // reserve space for downstream - let mut inner = coinbase_extranonce.clone(); - inner[6] = 0; - let extranonces = ExtendedExtranonce::new_with_inner_only_test(0..0, 0..0, 0..7, inner) - .expect("Failed to create ExtendedExtranonce with valid ranges"); - - let ids = Arc::new(Mutex::new(GroupId::new())); - let channel_kind = ExtendedChannelKind::Pool; - let mut channel = PoolChannelFactory::new( - ids, - extranonces, - creator, - share_per_min, - channel_kind, - vec![out], - ); - - // Build a NewTemplate - let new_template = NewTemplate { - template_id: 10, - future_template: true, - version: VERSION, - coinbase_tx_version: 1, - coinbase_prefix: prefix.try_into().unwrap(), - coinbase_tx_input_sequence: u32::MAX, - coinbase_tx_value_remaining: 5_000_000_000, - coinbase_tx_outputs_count: 0, - coinbase_tx_outputs: get_coinbase_outputs(), - coinbase_tx_locktime: 0, - merkle_path: get_merkle_path(), - }; - - // "Send" the NewTemplate to the channel - let _ = channel.on_new_template(&mut (new_template.clone())); - - // Build a PrevHash - let mut p_hash = decode_hex(PREV_HASH).unwrap(); - p_hash.reverse(); - let prev_hash = SetNewPrevHashFromTp { - template_id: 10, - prev_hash: p_hash.try_into().unwrap(), - header_timestamp: PREV_HEADER_TIMESTAMP, - n_bits: PREV_HEADER_NBITS, - target: nbit_to_target(PREV_HEADER_NBITS), - }; - - // "Send" the SetNewPrevHash to channel - let _ = channel.on_new_prev_hash_from_tp(&prev_hash); - - // Build open standard channel - let open_standard_channel = OpenStandardMiningChannel { - request_id: 100.into(), - user_identity: "Gigi".to_string().try_into().unwrap(), - nominal_hash_rate: 100_000_000_000_000.0, - max_target: [255; 32].into(), - }; - - // "Send" the OpenStandardMiningChannel to channel - let result = loop { - let id = channel.new_standard_id_for_hom(); - let result = channel - .add_standard_channel( - open_standard_channel.get_request_id_as_u32(), - open_standard_channel.nominal_hash_rate, - true, - id, - ) - .unwrap(); - let downsteram_extranonce = match &result[0] { - Mining::OpenStandardMiningChannelSuccess(msg) => { - msg.extranonce_prefix.clone().to_vec() - } - _ => panic!(), - }; - if downsteram_extranonce == coinbase_extranonce { - break result; - } - }; - let mut result = result.iter(); - - // Get the expected job id and channel_id - let mut channel_id = u32::MAX; - let job_id = loop { - match result.next().unwrap() { - Mining::OpenStandardMiningChannelSuccess(success) => { - channel_id = success.channel_id - } - Mining::SetNewPrevHash(_) => (), - Mining::NewMiningJob(job) => break job.job_id, - _ => panic!(), - } - }; - // make sure job management in channel factory is updated - (0..job_id - 1).for_each(|_| { - channel.job_creator.reset_new_templates(None); - let _ = channel.on_new_template(&mut (new_template.clone())); - let _ = channel.on_new_prev_hash_from_tp(&prev_hash); - }); - - // Build the success share - let share = SubmitSharesStandard { - channel_id, - sequence_number: 2, - job_id, - nonce: u32::from_le_bytes(decode_hex(NONCE).unwrap().try_into().unwrap()), - ntime: u32::from_le_bytes(decode_hex(NTIME).unwrap().try_into().unwrap()), - version: 1, - }; - - // "Send" the Share to channel - match channel.on_submit_shares_standard(share).unwrap() { - OnNewShare::SendErrorDownstream(e) => panic!( - "{:?} \n {}", - e, - std::str::from_utf8(&e.error_code.to_vec()[..]).unwrap() - ), - OnNewShare::SendSubmitShareUpstream(_) => panic!(), - OnNewShare::RelaySubmitShareUpstream => panic!(), - OnNewShare::ShareMeetBitcoinTarget(_) => assert!(true), - OnNewShare::ShareMeetDownstreamTarget => panic!(), - }; - } -} diff --git a/protocols/v2/roles-logic-sv2/src/channel_logic/mod.rs b/protocols/v2/roles-logic-sv2/src/channel_logic/mod.rs index 18653124a4..205dd6fb5e 100644 --- a/protocols/v2/roles-logic-sv2/src/channel_logic/mod.rs +++ b/protocols/v2/roles-logic-sv2/src/channel_logic/mod.rs @@ -7,7 +7,6 @@ //! - [`proxy_group_channel`] pub mod channel_factory; -pub mod proxy_group_channel; use mining_sv2::{NewExtendedMiningJob, NewMiningJob}; use std::convert::TryInto; diff --git a/protocols/v2/roles-logic-sv2/src/channel_logic/proxy_group_channel.rs b/protocols/v2/roles-logic-sv2/src/channel_logic/proxy_group_channel.rs deleted file mode 100644 index 41e23c93da..0000000000 --- a/protocols/v2/roles-logic-sv2/src/channel_logic/proxy_group_channel.rs +++ /dev/null @@ -1,252 +0,0 @@ -//! # Proxy Group Channel -//! -//! This module contains logic for managing Standard Channels via Group Channels. - -use crate::{common_properties::StandardChannel, parsers::Mining, Error}; - -use mining_sv2::{ - NewExtendedMiningJob, NewMiningJob, OpenStandardMiningChannelSuccess, SetNewPrevHash, -}; - -use super::extended_to_standard_job; -use nohash_hasher::BuildNoHashHasher; -use std::collections::HashMap; - -/// Wrapper around `GroupChannel` for managing multiple group channels -#[derive(Debug, Clone, Default)] -pub struct GroupChannels { - channels: HashMap, -} -impl GroupChannels { - /// Constructor - pub fn new() -> Self { - Self { - channels: HashMap::new(), - } - } - /// Called when when a group channel created. We add the channel in its - /// respective group and call [`GroupChannel::on_channel_success_for_hom_downtream`] - pub fn on_channel_success_for_hom_downtream( - &mut self, - m: &OpenStandardMiningChannelSuccess, - ) -> Result>, Error> { - let group_id = m.group_channel_id; - - self.channels - .entry(group_id) - .or_insert_with(GroupChannel::new); - match self.channels.get_mut(&group_id) { - Some(group) => group.on_channel_success_for_hom_downtream(m.clone()), - None => unreachable!(), - } - } - /// Called when a new prev hash arrives. We loop through all group channels to update state - /// within each group - pub fn update_new_prev_hash(&mut self, m: &SetNewPrevHash) { - for group in self.channels.values_mut() { - group.update_new_prev_hash(m); - } - } - /// Called when a new extended job arrives. We loop through all group channels to update state - /// within group - pub fn on_new_extended_mining_job(&mut self, m: &NewExtendedMiningJob) { - for group in &mut self.channels.values_mut() { - let cloned = NewExtendedMiningJob { - channel_id: m.channel_id, - job_id: m.job_id, - min_ntime: m.min_ntime.clone().into_static(), - version: m.version, - version_rolling_allowed: m.version_rolling_allowed, - merkle_path: m.merkle_path.clone().into_static(), - coinbase_tx_prefix: m.coinbase_tx_prefix.clone().into_static(), - coinbase_tx_suffix: m.coinbase_tx_suffix.clone().into_static(), - }; - group.on_new_extended_mining_job(cloned); - } - } - /// Returns last valid job as a `NewMiningJob` - pub fn last_received_job_to_standard_job( - &mut self, - channel_id: u32, - group_id: u32, - ) -> Result, Error> { - match self.channels.get_mut(&group_id) { - Some(group) => group.last_received_job_to_standard_job(channel_id), - None => Err(Error::GroupIdNotFound), - } - } - - /// Get group channel ids - pub fn ids(&self) -> Vec { - self.channels.keys().copied().collect() - } -} - -#[derive(Debug, Clone)] -struct GroupChannel { - hom_downstreams: HashMap>, - future_jobs: Vec>, - last_prev_hash: Option>, - last_valid_job: Option>, - last_received_job: Option>, -} - -impl GroupChannel { - fn new() -> Self { - Self { - hom_downstreams: HashMap::with_hasher(BuildNoHashHasher::default()), - future_jobs: vec![], - last_prev_hash: None, - last_valid_job: None, - last_received_job: None, - } - } - // Called when a channel is successfully opened for header only mining(HOM) on standard - // channels. Here, we store the new channel, and update state for jobs and return relevant - // SV2 messages (NewMiningJob and SNPH) - fn on_channel_success_for_hom_downtream( - &mut self, - m: OpenStandardMiningChannelSuccess, - ) -> Result>, Error> { - let channel = StandardChannel { - channel_id: m.channel_id, - group_id: m.group_channel_id, - target: m.target.clone().into(), - extranonce: m.extranonce_prefix.clone().into(), - }; - let channel_id = m.channel_id; - let mut res = vec![]; - for extended_job in &self.future_jobs { - let standard_job = extended_to_standard_job( - extended_job, - &channel.extranonce.clone().to_vec(), - channel.channel_id, - None, - ) - .ok_or(Error::ImpossibleToCalculateMerkleRoot)?; - res.push(Mining::NewMiningJob(standard_job)); - } - - if let Some(last_valid_job) = &self.last_valid_job { - let mut standard_job = extended_to_standard_job( - last_valid_job, - &channel.extranonce.clone().to_vec(), - channel.channel_id, - None, - ) - .ok_or(Error::ImpossibleToCalculateMerkleRoot)?; - - if let Some(new_prev_hash) = &self.last_prev_hash { - let mut new_prev_hash = new_prev_hash.clone(); - standard_job.set_future(); - new_prev_hash.job_id = standard_job.job_id; - res.push(Mining::NewMiningJob(standard_job)); - res.push(Mining::SetNewPrevHash(new_prev_hash)) - } else { - res.push(Mining::NewMiningJob(standard_job)); - } - } else if let Some(new_prev_hash) = &self.last_prev_hash { - res.push(Mining::SetNewPrevHash(new_prev_hash.clone())) - } - - self.hom_downstreams.insert(channel_id, channel); - - Ok(res) - } - - // If a matching job is already in the future job queue, - // we set a new valid job, otherwise we clear the future jobs - // queue and stage a prev hash to be used when the job arrives - fn update_new_prev_hash(&mut self, m: &SetNewPrevHash) { - while let Some(job) = self.future_jobs.pop() { - if job.job_id == m.job_id { - self.last_valid_job = Some(job); - break; - } - } - self.future_jobs = vec![]; - let cloned = SetNewPrevHash { - channel_id: m.channel_id, - job_id: m.job_id, - prev_hash: m.prev_hash.clone().into_static(), - min_ntime: m.min_ntime, - nbits: m.nbits, - }; - self.last_prev_hash = Some(cloned.clone()); - } - - // Pushes new job to future_job queue if it is future, - // otherwise we set it as the valid job - fn on_new_extended_mining_job(&mut self, m: NewExtendedMiningJob<'static>) { - self.last_received_job = Some(m.clone()); - if m.is_future() { - self.future_jobs.push(m) - } else { - self.last_valid_job = Some(m) - } - } - - // Returns most recent job - fn last_received_job_to_standard_job( - &mut self, - channel_id: u32, - ) -> Result, Error> { - match &self.last_received_job { - Some(m) => { - let downstream = self - .hom_downstreams - .get(&channel_id) - .ok_or(Error::NotFoundChannelId)?; - extended_to_standard_job( - m, - &downstream.extranonce.clone().to_vec(), - downstream.channel_id, - None, - ) - .ok_or(Error::ImpossibleToCalculateMerkleRoot) - } - None => Err(Error::NoValidJob), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use codec_sv2::binary_sv2::{self, B064K}; - use std::convert::TryFrom; - - #[test] - fn group_channel_new_prev_hash_ordering_test() { - let mut group_channel = GroupChannel::new(); - let mut new_extended_mining_job = NewExtendedMiningJob { - channel_id: 1, - job_id: 0, - min_ntime: binary_sv2::Sv2Option::new(None), - version: 0, - version_rolling_allowed: false, - merkle_path: vec![].into(), - coinbase_tx_prefix: B064K::try_from(Vec::new()).unwrap(), - coinbase_tx_suffix: B064K::try_from(Vec::new()).unwrap(), - }; - - group_channel.on_new_extended_mining_job(new_extended_mining_job.clone()); - new_extended_mining_job.version = 1; - group_channel.on_new_extended_mining_job(new_extended_mining_job); - - // Make sure this returns the last job - the one where we updated the version. - group_channel.update_new_prev_hash(&SetNewPrevHash { - channel_id: 1, - job_id: 0, - prev_hash: [ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, - ] - .into(), - min_ntime: 989898, - nbits: 9, - }); - - assert_eq!(group_channel.last_valid_job.unwrap().version, 1); - } -} diff --git a/protocols/v2/roles-logic-sv2/src/common_properties.rs b/protocols/v2/roles-logic-sv2/src/common_properties.rs deleted file mode 100644 index 535a7fd292..0000000000 --- a/protocols/v2/roles-logic-sv2/src/common_properties.rs +++ /dev/null @@ -1,344 +0,0 @@ -//! # Common Properties for Stratum V2 (Sv2) Roles -//! -//! Defines common properties, traits, and utilities for implementing upstream and downstream -//! nodes. It provides abstractions for features like connection pairing, mining job distribution, -//! and channel management. These definitions form the foundation for consistent communication and -//! behavior across Sv2 roles/applications. - -use common_messages_sv2::{has_requires_std_job, Protocol, SetupConnection}; -use mining_sv2::{Extranonce, Target}; -use nohash_hasher::BuildNoHashHasher; -use std::collections::HashMap; - -/// Defines a mining downstream node at the most basic level. -#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] -pub struct CommonDownstreamData { - /// Header-only mining flag. - /// - /// Enables the processing of standard jobs only, leaving merkle root manipulation to the - /// upstream node. - /// - /// - `true`: The downstream node only processes standard jobs, relying on the upstream for - /// merkle root manipulation. - /// - `false`: The downstream node handles extended jobs and merkle root manipulation. - pub header_only: bool, - - /// Work selection flag. - /// - /// Enables the selection of which transactions or templates the node will work on. - /// - /// - `true`: The downstream node works on a custom block template, using the Job Declaration - /// Protocol. - /// - `false`: The downstream node strictly follows the work provided by the upstream, based on - /// pre-constructed templates from the upstream (e.g. Pool). - pub work_selection: bool, - - /// Version rolling flag. - /// - /// Enables rolling the block header version bits which allows for more unique hash generation - /// on the same mining job by expanding the nonce-space. Used when other fields (e.g. - /// `nonce` or `extranonce`) are fully exhausted. - /// - /// - `true`: The downstream supports version rolling and can modify specific bits in the - /// version field. This is useful for increasing hash rate efficiency by exploring a larger - /// solution space. - /// - `false`: The downstream does not support version rolling and relies on the upstream to - /// provide jobs with adjusted version fields. - pub version_rolling: bool, -} - -/// Encapsulates settings for pairing upstream and downstream nodes. -/// -/// Simplifies the [`SetupConnection`] configuration process by bundling the protocol, version -/// range, and flag settings. -#[derive(Debug, Copy, Clone)] -pub struct PairSettings { - /// Protocol the settings apply to. - pub protocol: Protocol, - - /// Minimum protocol version the node supports. - pub min_v: u16, - - /// Minimum protocol version the node supports. - pub max_v: u16, - - /// Flags indicating optional protocol features the node supports (e.g. header-only mining, - /// work selection, version-rolling, etc.). Each protocol field as its own - /// flags. - pub flags: u32, -} - -/// Properties defining behaviors common to all Sv2 upstream nodes. -pub trait IsUpstream { - /// Returns the protocol version used by the upstream node. - fn get_version(&self) -> u16; - - /// Returns the flags indicating the upstream node's protocol capabilities. - fn get_flags(&self) -> u32; - - /// Lists the protocols supported by the upstream node. - /// - /// Used to check if the upstream supports the protocol that the downstream wants to use. - fn get_supported_protocols(&self) -> Vec; - - /// Verifies if the upstream can pair with the given downstream settings. - fn is_pairable(&self, pair_settings: &PairSettings) -> bool { - let protocol = pair_settings.protocol; - let min_v = pair_settings.min_v; - let max_v = pair_settings.max_v; - let flags = pair_settings.flags; - - let check_version = self.get_version() >= min_v && self.get_version() <= max_v; - let check_flags = SetupConnection::check_flags(protocol, self.get_flags(), flags); - check_version && check_flags - } - - /// Returns the channel ID managed by the upstream node. - fn get_id(&self) -> u32; - - /// Provides a request ID mapper for viewing and managing upstream-downstream communication. - fn get_mapper(&mut self) -> Option<&mut RequestIdMapper>; -} - -/// The types of channels that can be opened with upstream nodes. -#[derive(Debug, Clone, Copy)] -pub enum UpstreamChannel { - /// A standard channel with a nominal hash rate. - /// - /// Typically used for mining devices with a direct connection to an upstream node (e.g. a pool - /// or proxy). The hashrate is specified as a `f32` value, representing the expected - /// computational capacity of the miner. - Standard(f32), - - /// A grouped channel for aggregated mining. - /// - /// Aggregates mining work for multiple standard channels under a single group channel, - /// enabling the upstream to manage work distribution and result aggregation for an entire - /// group of channels. - /// - /// Typically used by a mining proxy managing multiple downstream miners. - Group, - - /// An extended channel for additional features. - /// - /// Provides additional features or capabilities beyond standard and group channels, - /// accommodating features like custom job templates or experimental protocol extensions. - Extended, -} - -/// Properties of a standard mining channel. -/// -/// Standard channels are intended to be used by end mining devices with a nominal hashrate, where -/// each device operates on an independent channel to its upstream. -#[derive(Debug, Clone)] -pub struct StandardChannel { - /// Identifies a specific channel, unique to each mining connection. - /// - /// Dynamically assigned when a mining connection is established (as part of the negotiation - /// process) to avoid conflicts with other connections (e.g. other mining devices) managed by - /// the same upstream node. The identifier remains stable for the whole lifetime of the - /// connection. - /// - /// Used for broadcasting new jobs by [`mining_sv2::NewMiningJob`]. - pub channel_id: u32, - - /// Identifies a specific group in which the standard channel belongs. - pub group_id: u32, - - /// Initial difficulty target assigned to the mining. - pub target: Target, - - /// Additional nonce value used to differentiate shares within the same channel. - /// - /// Helps to avoid nonce collisions when multiple mining devices are working on the same job. - /// - /// The extranonce bytes are added to the coinbase to form a fully valid submission: - /// `full coinbase = coinbase_tx_prefix + extranonce_prefix + extranonce + coinbase_tx_suffix` - pub extranonce: Extranonce, -} - -/// Properties of a Sv2-compatible mining upstream node. -/// -/// This trait extends [`IsUpstream`] with additional functionality specific to mining, such as -/// hashrate management and channel updates. -pub trait IsMiningUpstream: IsUpstream { - /// Returns the total hashrate managed by the upstream node. - fn total_hash_rate(&self) -> u64; - - /// Adds hash rate to the upstream node. - fn add_hash_rate(&mut self, to_add: u64); - - /// Returns the list of open channels on the upstream node. - fn get_opened_channels(&mut self) -> &mut Vec; - - /// Updates the list of channels managed by the upstream node. - fn update_channels(&mut self, c: UpstreamChannel); - - /// Checks if the upstream node supports header-only mining. - fn is_header_only(&self) -> bool { - has_requires_std_job(self.get_flags()) - } -} - -/// Properties defining behaviors common to all Sv2 downstream nodes. -pub trait IsDownstream { - /// Returns the common properties of the downstream node (e.g. support for header-only mining, - /// work selection, and version rolling). - fn get_downstream_mining_data(&self) -> CommonDownstreamData; -} - -/// Properties of a SV2-compatible mining downstream node. -/// -/// This trait extends [`IsDownstream`] with additional functionality specific to mining, such as -/// header-only mining checks. -pub trait IsMiningDownstream: IsDownstream { - /// Checks if the downstream node supports header-only mining. - fn is_header_only(&self) -> bool { - self.get_downstream_mining_data().header_only - } -} - -// Implemented for the `NullDownstreamMiningSelector`. -impl IsUpstream for () { - fn get_version(&self) -> u16 { - unreachable!("Null upstream do not have a version"); - } - - fn get_flags(&self) -> u32 { - unreachable!("Null upstream do not have flags"); - } - - fn get_supported_protocols(&self) -> Vec { - unreachable!("Null upstream do not support any protocol"); - } - fn get_id(&self) -> u32 { - unreachable!("Null upstream do not have an ID"); - } - - fn get_mapper(&mut self) -> Option<&mut RequestIdMapper> { - unreachable!("Null upstream do not have a mapper") - } -} - -// Implemented for the `NullDownstreamMiningSelector`. -impl IsDownstream for () { - fn get_downstream_mining_data(&self) -> CommonDownstreamData { - unreachable!("Null downstream do not have mining data"); - } -} - -impl IsMiningUpstream for () { - fn total_hash_rate(&self) -> u64 { - unreachable!("Null selector do not have hash rate"); - } - - fn add_hash_rate(&mut self, _to_add: u64) { - unreachable!("Null selector can not add hash rate"); - } - fn get_opened_channels(&mut self) -> &mut Vec { - unreachable!("Null selector can not open channels"); - } - - fn update_channels(&mut self, _: UpstreamChannel) { - unreachable!("Null selector can not update channels"); - } -} - -impl IsMiningDownstream for () {} - -/// Maps request IDs between upstream and downstream nodes. -/// -/// Most commonly used by proxies, this struct facilitates communication between upstream and -/// downstream nodes by mapping request IDs. This ensures responses are routed correctly back from -/// the upstream to the originating downstream requester, even when request IDs are modified in -/// transit. -/// -/// ### Workflow -/// 1. **Request Mapping**: When a downstream node sends a request, `on_open_channel` assigns a new -/// unique upstream request ID and stores the mapping. -/// 2. **Response Mapping**: When the upstream responds, the proxy uses the map to translate the -/// upstream ID back to the original downstream ID. -/// 3. **Cleanup**: Once the responses are processed, the mapping is removed. -#[derive(Debug, Default, PartialEq, Eq)] -pub struct RequestIdMapper { - // A mapping between upstream-assigned request IDs and the original downstream IDs. - // - // In the hashmap, the key is the upstream request ID and the value is the corresponding - // downstream request ID. `BuildNoHashHasher` is an optimization to bypass the hashing step for - // integer keys. - request_ids_map: HashMap>, - - // A counter for assigning unique request IDs to upstream nodes, incrementing after every - // assignment. - next_id: u32, -} - -impl RequestIdMapper { - /// Creates a new [`RequestIdMapper`] instance. - pub fn new() -> Self { - Self { - request_ids_map: HashMap::with_hasher(BuildNoHashHasher::default()), - next_id: 0, - } - } - - /// Assigns a new upstream request ID for a request sent by a downstream node. - /// - /// Ensures every request forwarded to the upstream node has a unique ID while retaining - /// traceability to the original downstream request. - pub fn on_open_channel(&mut self, id: u32) -> u32 { - let new_id = self.next_id; // Assign new upstream ID - self.next_id += 1; // Increment next_id for future requests - - self.request_ids_map.insert(new_id, id); // Map new upstream ID to downstream ID - new_id - } - - /// Removes the mapping for a request ID once the response has been processed. - pub fn remove(&mut self, upstream_id: u32) -> Option { - self.request_ids_map.remove(&upstream_id) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn builds_request_id_mapper() { - let expect = RequestIdMapper { - request_ids_map: HashMap::with_hasher(BuildNoHashHasher::default()), - next_id: 0, - }; - let actual = RequestIdMapper::new(); - - assert_eq!(expect, actual); - } - - #[test] - fn updates_request_id_mapper_on_open_channel() { - let id = 0; - let mut expect = RequestIdMapper { - request_ids_map: HashMap::with_hasher(BuildNoHashHasher::default()), - next_id: id, - }; - let new_id = expect.next_id; - expect.next_id += 1; - expect.request_ids_map.insert(new_id, id); - - let mut actual = RequestIdMapper::new(); - actual.on_open_channel(0); - - assert_eq!(expect, actual); - } - - #[test] - fn removes_id_from_request_id_mapper() { - let mut request_id_mapper = RequestIdMapper::new(); - request_id_mapper.on_open_channel(0); - assert!(!request_id_mapper.request_ids_map.is_empty()); - - request_id_mapper.remove(0); - assert!(request_id_mapper.request_ids_map.is_empty()); - } -} diff --git a/protocols/v2/roles-logic-sv2/src/errors.rs b/protocols/v2/roles-logic-sv2/src/errors.rs index 93f3a4bb2a..2c7f20567c 100644 --- a/protocols/v2/roles-logic-sv2/src/errors.rs +++ b/protocols/v2/roles-logic-sv2/src/errors.rs @@ -5,7 +5,6 @@ use crate::{ channels::server::error::{ExtendedChannelError, GroupChannelError, StandardChannelError}, - common_properties::CommonDownstreamData, parsers::AnyMessage as AllMessages, utils::InputError, vardiff::error::VardiffError, @@ -37,8 +36,6 @@ pub enum Error { NoGroupIdOnExtendedChannel, /// No pairable upstream. Parameters are: (`min_v`, `max_v`, all flags supported) NoPairableUpstream((u16, u16, u32)), - /// No compatible upstream - NoCompatibleUpstream(CommonDownstreamData), /// Error if the hashmap `future_jobs` field in the `GroupChannelJobDispatcher` is empty. NoFutureJobs, /// No Downstream's connected @@ -186,9 +183,6 @@ impl Display for Error { NoPairableUpstream(a) => { write!(f, "No pairable upstream node: {a:?}") } - NoCompatibleUpstream(a) => { - write!(f, "No compatible upstream node: {a:?}") - } NoFutureJobs => write!(f, "GroupChannelJobDispatcher does not have any future jobs"), NoDownstreamsConnected => write!(f, "NoDownstreamsConnected"), PrevHashRequireNonExistentJobId(id) => { diff --git a/protocols/v2/roles-logic-sv2/src/handlers/mining.rs b/protocols/v2/roles-logic-sv2/src/handlers/mining.rs index 9be8ad5463..0488a7c1d5 100644 --- a/protocols/v2/roles-logic-sv2/src/handlers/mining.rs +++ b/protocols/v2/roles-logic-sv2/src/handlers/mining.rs @@ -39,8 +39,6 @@ use mining_sv2::{ UpdateChannel, UpdateChannelError, }; -use crate::common_properties::{IsMiningDownstream, IsMiningUpstream}; - use super::SendTo_; use crate::utils::Mutex; @@ -64,7 +62,7 @@ pub enum SupportedChannelTypes { /// /// This trait defines methods for parsing and routing downstream messages /// related to mining operations. -pub trait ParseMiningMessagesFromDownstream +pub trait ParseMiningMessagesFromDownstream where Self: Sized + D, { @@ -78,7 +76,7 @@ where payload: &mut [u8], ) -> Result, Error> where - Self: IsMiningDownstream + Sized, + Self: Sized, { match Self::handle_message_mining_deserialized( self_mutex, @@ -95,7 +93,7 @@ where message: Result, Error>, ) -> Result, Error> where - Self: IsMiningDownstream + Sized, + Self: Sized, { let (channel_type, is_work_selection_enabled) = self_mutex .safe_lock(|self_| (self_.get_channel_type(), self_.is_work_selection_enabled()))?; @@ -237,7 +235,7 @@ where /// /// This trait provides the functionality to handle and route various types of mining messages /// from the upstream based on the message type and payload. -pub trait ParseMiningMessagesFromUpstream +pub trait ParseMiningMessagesFromUpstream where Self: Sized + D, { diff --git a/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs b/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs index 597ed2e8bd..15489eef7f 100644 --- a/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs +++ b/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs @@ -6,17 +6,11 @@ //! jobs //! - determining if submitted shares correlate to valid jobs -use crate::{ - common_properties::StandardChannel, - utils::{merkle_root_from_path, Id, Mutex}, - Error, -}; +use crate::utils::merkle_root_from_path; use mining_sv2::{ - NewExtendedMiningJob, NewMiningJob, SetNewPrevHash, SubmitSharesError, SubmitSharesStandard, - Target, + NewExtendedMiningJob, NewMiningJob, SubmitSharesError, SubmitSharesStandard, Target, }; -use nohash_hasher::BuildNoHashHasher; -use std::{collections::HashMap, convert::TryInto, sync::Arc}; +use std::convert::TryInto; use bitcoin::hashes::{sha256d, Hash, HashEngine}; @@ -71,34 +65,6 @@ impl Header<'_> { } } -// helper struct to identify Standard Jobs being managed for downstream -#[derive(Debug)] -struct DownstreamJob { - #[allow(dead_code)] - merkle_root: Vec, - extended_job_id: u32, -} - -/// Used by proxies to keep track of standard jobs in the group channel -/// created with the sv2 server -#[derive(Debug)] -pub struct GroupChannelJobDispatcher { - //channels: Vec, - #[allow(dead_code)] - target: Target, - prev_hash: Vec, - // extended_job_id -> standard_job_id -> standard_job - future_jobs: - HashMap>, BuildNoHashHasher>, - // standard_job_id -> standard_job - jobs: HashMap>, - ids: Arc>, - // extended_id -> channel_id -> standard_id - extended_id_to_job_id: - HashMap>, BuildNoHashHasher>, - nbits: u32, -} - /// Used to signal if submitted shares correlate to valid jobs pub enum SendSharesResponse { /// ValidAndMeetUpstreamTarget((SubmitSharesStandard,SubmitSharesSuccess)), @@ -106,147 +72,10 @@ pub enum SendSharesResponse { Invalid(SubmitSharesError<'static>), } -impl GroupChannelJobDispatcher { - /// constructor - pub fn new(ids: Arc>) -> Self { - Self { - target: [0_u8; 32].into(), - prev_hash: Vec::new(), - future_jobs: HashMap::with_hasher(BuildNoHashHasher::default()), - jobs: HashMap::with_hasher(BuildNoHashHasher::default()), - ids, - nbits: 0, - extended_id_to_job_id: HashMap::with_hasher(BuildNoHashHasher::default()), - } - } - - /// When a downstream opens a connection with a proxy, the proxy uses this function to create a - /// new mining job from the last valid new extended mining job. - /// - /// When a proxy receives a new extended mining job from upstream it uses this function to - /// create the corresponding new mining job for each connected downstream. - #[allow(clippy::option_map_unit_fn)] - pub fn on_new_extended_mining_job( - &mut self, - extended: &NewExtendedMiningJob, - channel: &StandardChannel, - // should be changed to return a Result> - ) -> Option> { - if extended.is_future() { - self.future_jobs - .entry(extended.job_id) - .or_insert_with(|| HashMap::with_hasher(BuildNoHashHasher::default())); - self.extended_id_to_job_id - .entry(extended.job_id) - .or_insert_with(|| HashMap::with_hasher(BuildNoHashHasher::default())); - } - - // Is fine to unwrap a safe_lock result - let standard_job_id = self.ids.safe_lock(|ids| ids.next()).unwrap(); - - let extranonce: Vec = channel.extranonce.clone().into(); - let new_mining_job_message = extended_to_standard_job_for_group_channel( - extended, - &extranonce, - channel.channel_id, - standard_job_id, - )?; - let job = DownstreamJob { - merkle_root: new_mining_job_message.merkle_root.to_vec(), - extended_job_id: extended.job_id, - }; - if extended.is_future() { - self.future_jobs - .get_mut(&extended.job_id) - .map(|future_jobs| { - future_jobs.insert(standard_job_id, job); - }); - - let channel_id_to_standard_id = self - .extended_id_to_job_id - .get_mut(&extended.job_id) - // The key is always in the map cause we insert it above if not present - .unwrap(); - channel_id_to_standard_id.insert(channel.channel_id, standard_job_id); - } else { - self.jobs.insert(new_mining_job_message.job_id, job); - }; - Some(new_mining_job_message) - } - - /// Called when a SetNewPrevHash message is received. - /// This function will move all future jobs to current jobs, clear old jobs, - /// and update `self` to reference the latest prev_hash and nbits - /// associated with the latest job. - pub fn on_new_prev_hash( - &mut self, - message: &SetNewPrevHash, - ) -> Result>, Error> { - let jobs = self - .future_jobs - .get_mut(&message.job_id) - .ok_or(Error::PrevHashRequireNonExistentJobId(message.job_id))?; - std::mem::swap(&mut self.jobs, jobs); - self.prev_hash = message.prev_hash.to_vec(); - self.nbits = message.nbits; - self.future_jobs.clear(); - match self.extended_id_to_job_id.remove(&message.job_id) { - Some(map) => { - self.extended_id_to_job_id.clear(); - Ok(map) - } - None => { - self.extended_id_to_job_id.clear(); - Ok(HashMap::with_hasher(BuildNoHashHasher::default())) - } - } - } - - /// takes shares submitted by a group channel miner and determines if the shares correspond to a - /// valid job. - pub fn on_submit_shares(&self, shares: SubmitSharesStandard) -> SendSharesResponse { - let id = shares.job_id; - if let Some(job) = self.jobs.get(&id) { - let success = SubmitSharesStandard { - channel_id: shares.channel_id, - sequence_number: shares.sequence_number, - job_id: job.extended_job_id, - nonce: shares.nonce, - ntime: shares.ntime, - version: shares.version, - }; - SendSharesResponse::Valid(success) - } else { - let error = SubmitSharesError { - channel_id: shares.channel_id, - sequence_number: shares.sequence_number, - // Below unwrap never panic because an empty string will always fit - // in a `Inner` type - error_code: "".to_string().into_bytes().try_into().unwrap(), - }; - SendSharesResponse::Invalid(error) - } - } -} - #[cfg(test)] mod tests { use super::*; - use crate::{ - errors::Error, - job_creator::{ - tests::{new_pub_key, template_from_gen}, - JobsCreators, - }, - }; - use codec_sv2::binary_sv2::{u256_from_int, U256}; - use mining_sv2::Extranonce; - use quickcheck::{Arbitrary, Gen}; - use std::convert::TryFrom; - - use bitcoin::{Amount, ScriptBuf, TxOut}; - - const BLOCK_REWARD: u64 = 625_000_000_000; + use codec_sv2::binary_sv2::U256; #[test] fn test_block_hash() { @@ -292,302 +121,9 @@ mod tests { ); } - #[test] - fn test_group_channel_job_dispatcher() { - let out = TxOut { - value: Amount::from_sat(BLOCK_REWARD), - script_pubkey: ScriptBuf::new_p2pk(&new_pub_key()), - }; - let mut jobs_creators = JobsCreators::new(32); - let group_channel_id = 1; - //Create a template - let mut template = template_from_gen(&mut Gen::new(255)); - template.template_id %= u64::MAX; - template.future_template = true; - let extended_mining_job = jobs_creators - .on_new_template(&mut template, false, vec![out]) - .expect("Failed to create new job"); - - // create GroupChannelJobDispatcher - let ids = Arc::new(Mutex::new(Id::new())); - let mut group_channel_dispatcher = GroupChannelJobDispatcher::new(ids); - // create standard channel - let target = Target::from(U256::try_from(utils::extranonce_gen()).unwrap()); - let standard_channel_id = 2; - let extranonce = Extranonce::try_from(utils::extranonce_gen()) - .expect("Failed to convert bytes to extranonce"); - let standard_channel = StandardChannel { - channel_id: standard_channel_id, - group_id: group_channel_id, - target, - extranonce: extranonce.clone(), - }; - // call target function (on_new_extended_mining_job) - let new_mining_job = group_channel_dispatcher - .on_new_extended_mining_job(&extended_mining_job, &standard_channel) - .unwrap(); - - // on_new_extended_mining_job assertions - let (future_job_id, test_merkle_root) = assert_on_new_extended_mining_job( - &group_channel_dispatcher, - &new_mining_job, - &extended_mining_job, - extranonce.clone(), - standard_channel_id, - ); - // on_new_prev_hash assertions - if extended_mining_job.is_future() { - assert_on_new_prev_hash( - &mut group_channel_dispatcher, - standard_channel_id, - future_job_id, - test_merkle_root, - ) - } - assert_on_submit_shares( - &group_channel_dispatcher, - standard_channel_id, - future_job_id, - ); - } - - fn assert_on_new_extended_mining_job( - group_channel_job_dispatcher: &GroupChannelJobDispatcher, - new_mining_job: &NewMiningJob, - extended_mining_job: &NewExtendedMiningJob, - extranonce: Extranonce, - standard_channel_id: u32, - ) -> (u32, Vec) { - // compute test merkle path - let new_root = merkle_root_from_path( - extended_mining_job.coinbase_tx_prefix.inner_as_ref(), - extended_mining_job.coinbase_tx_suffix.inner_as_ref(), - extranonce.to_vec().as_slice(), - &extended_mining_job.merkle_path.inner_as_ref(), - ) - .unwrap(); - // Assertions - assert_eq!( - new_mining_job.channel_id, standard_channel_id, - "channel_id did not convert correctly" - ); - assert_eq!( - new_mining_job.job_id, extended_mining_job.job_id, - "job_id did not convert correctly" - ); - assert_eq!( - new_mining_job.version, extended_mining_job.version, - "version did not convert correctly" - ); - assert_eq!( - new_mining_job.min_ntime, extended_mining_job.min_ntime, - "future_job did not convert correctly" - ); - assert_eq!( - new_mining_job.merkle_root.to_vec(), - new_root, - "merkle_root did not convert correctly" - ); - let mut future_job_id: u32 = 0; - if new_mining_job.is_future() { - // assert job_id counter - let job_ids = group_channel_job_dispatcher - .extended_id_to_job_id - .get(&extended_mining_job.job_id) - .unwrap(); - let standard_job_id = job_ids.get(&standard_channel_id).unwrap(); - let standard_job_id_counter: u32 = group_channel_job_dispatcher - .ids - .safe_lock(|id| id.next()) - .unwrap(); - let prev_value = standard_job_id_counter - 1; - assert_eq!( - standard_job_id, &prev_value, - "Job Id counter does not match" - ); - // assert job was stored - let future_jobs = group_channel_job_dispatcher - .future_jobs - .get(&extended_mining_job.job_id) - .unwrap(); - let job = future_jobs.get(&prev_value).unwrap(); - assert_eq!( - job.extended_job_id, extended_mining_job.job_id, - "job_id not stored correctly in future_jobs" - ); - assert_eq!( - job.merkle_root, - new_mining_job.merkle_root.to_vec(), - "job merkle root not stored correctly in future jobs" - ); - future_job_id = prev_value; - } - (future_job_id, new_root) - } - - fn assert_on_new_prev_hash( - group_channel_job_dispatcher: &mut GroupChannelJobDispatcher, - standard_channel_id: u32, - future_job_id: u32, - test_merkle_root: Vec, - ) { - let mut prev_hash = Vec::new(); - prev_hash.resize_with(32, || u8::arbitrary(&mut Gen::new(1))); - let min_ntime: u32 = 1; - let nbits: u32 = 1; - let new_message = SetNewPrevHash { - channel_id: standard_channel_id, - job_id: future_job_id, - prev_hash: U256::try_from(prev_hash).unwrap(), - min_ntime, - nbits, - }; - - group_channel_job_dispatcher - .on_new_prev_hash(&new_message) - .expect("on_new_prev_hash failed to execute"); - - // assert future job was moved to current jobs - let new_current_job = group_channel_job_dispatcher - .jobs - .get(&future_job_id) - .unwrap(); - assert_eq!( - new_current_job.merkle_root, test_merkle_root, - "Future job not moved to current job correctly (merkle root)" - ); - assert_eq!( - new_current_job.extended_job_id, future_job_id, - "Future job not moved to current job correctly (job_id)" - ); - assert_eq!( - group_channel_job_dispatcher.nbits, new_message.nbits, - "nbits not updated for SetNewPrevHash" - ); - assert_eq!( - group_channel_job_dispatcher.prev_hash, - new_message.prev_hash.to_vec(), - "prev_hash not updated for SetNewPrevHash" - ); - assert!( - group_channel_job_dispatcher.future_jobs.is_empty(), - "Future jobs did not get cleared" - ) - } - fn assert_on_submit_shares( - group_channel_job_dispatcher: &GroupChannelJobDispatcher, - standard_channel_id: u32, - job_id: u32, - ) { - let shares = SubmitSharesStandard { - // Channel identification. - channel_id: standard_channel_id, - // Unique sequential identifier of the submit within the channel. - sequence_number: 0, - // Identifier of the job as provided by *NewMiningJob* or - // *NewExtendedMiningJob* message. - job_id, - // Nonce leading to the hash being submitted. - nonce: 1, - // The nTime field in the block header. This MUST be greater than or equal - // to the header_timestamp field in the latest SetNewPrevHash message - // and lower than or equal to that value plus the number of seconds since - // the receipt of that message. - ntime: 1, - // Full nVersion field. - version: 1, - }; - let mut faulty_shares = shares.clone(); - faulty_shares.job_id += 1; - - for (index, shares) in [shares, faulty_shares].iter().enumerate() { - match group_channel_job_dispatcher.on_submit_shares(shares.clone()) { - SendSharesResponse::Valid(resp) => { - assert_eq!( - index, 0, - "Only the first item in iterator should be a valid response" - ); - assert_eq!(resp.channel_id, standard_channel_id); - assert_eq!(resp.job_id, job_id); - assert_eq!(resp.sequence_number, shares.sequence_number); - assert_eq!(resp.nonce, shares.nonce); - assert_eq!(resp.ntime, shares.ntime); - assert_eq!(resp.version, shares.version); - } - SendSharesResponse::Invalid(err) => { - assert_eq!( - index, 1, - "Only the second item in iterator should be an invalid response" - ); - assert_eq!(err.channel_id, standard_channel_id); - assert_eq!(err.sequence_number, shares.sequence_number); - assert_eq!( - err.error_code, - "".to_string().into_bytes().try_into().unwrap() - ); - } - }; - } - } - - #[test] - fn builds_group_channel_job_dispatcher() { - let expect = GroupChannelJobDispatcher { - target: [0_u8; 32].into(), - prev_hash: Vec::new(), - future_jobs: HashMap::with_hasher(BuildNoHashHasher::default()), - jobs: HashMap::with_hasher(BuildNoHashHasher::default()), - ids: Arc::new(Mutex::new(Id::new())), - nbits: 0, - extended_id_to_job_id: HashMap::with_hasher(BuildNoHashHasher::default()), - }; - - let ids = Arc::new(Mutex::new(Id::new())); - let actual = GroupChannelJobDispatcher::new(ids); - - assert_eq!(expect.target, actual.target); - assert_eq!(expect.prev_hash, actual.prev_hash); - assert_eq!(expect.nbits, actual.nbits); - assert!(actual.future_jobs.is_empty()); - assert!(actual.jobs.is_empty()); - // check actual.ids, but idk how to properly test arc - // assert_eq!(expect.ids, actual.ids); - } - - #[ignore] - #[test] - fn updates_group_channel_job_dispatcher_on_new_prev_hash() -> Result<(), Error> { - let message = SetNewPrevHash { - channel_id: 0, - job_id: 0, - prev_hash: u256_from_int(45_u32), - min_ntime: 0, - nbits: 0, - }; - let ids = Arc::new(Mutex::new(Id::new())); - let mut dispatcher = GroupChannelJobDispatcher::new(ids); - - // fails on self.future_jobs unwrap in the first line of the on_new_prev_hash fn - let _actual = dispatcher.on_new_prev_hash(&message); - // let actual_prev_hash: U256<'static> = u256_from_int(tt); - let expect_prev_hash: Vec = dispatcher.prev_hash.to_vec(); - // assert_eq!(expect_prev_hash, dispatcher.prev_hash); - assert_eq!(expect_prev_hash, dispatcher.prev_hash); - - Ok(()) - } - pub mod utils { - use super::*; use std::fmt::Write; - pub fn extranonce_gen() -> Vec { - let mut u8_gen = Gen::new(1); - let mut extranonce: Vec = Vec::new(); - extranonce.resize_with(32, || u8::arbitrary(&mut u8_gen)); - extranonce - } - pub fn decode_hex(s: &str) -> Result, core::num::ParseIntError> { let s = match s.strip_prefix("0x") { Some(s) => s, diff --git a/protocols/v2/roles-logic-sv2/src/lib.rs b/protocols/v2/roles-logic-sv2/src/lib.rs index 75a95074b6..c550c91e57 100644 --- a/protocols/v2/roles-logic-sv2/src/lib.rs +++ b/protocols/v2/roles-logic-sv2/src/lib.rs @@ -19,7 +19,6 @@ //! - `prop_test`: Enables support for property testing in [`template_distribution_sv2`] crate. pub mod channel_logic; pub mod channels; -pub mod common_properties; pub mod errors; pub mod handlers; pub mod job_creator; diff --git a/roles/jd-client/src/lib/downstream.rs b/roles/jd-client/src/lib/downstream.rs index ccfe27b95b..f319318039 100644 --- a/roles/jd-client/src/lib/downstream.rs +++ b/roles/jd-client/src/lib/downstream.rs @@ -34,7 +34,6 @@ use stratum_common::roles_logic_sv2::{ channel_logic::channel_factory::{OnNewShare, PoolChannelFactory, Share}, codec_sv2, common_messages_sv2::{SetupConnection, SetupConnectionSuccess}, - common_properties::{CommonDownstreamData, IsDownstream, IsMiningDownstream}, errors::Error, handlers::{ common::{ParseCommonMessagesFromDownstream, SendTo as SendToCommon}, @@ -110,24 +109,17 @@ pub enum DownstreamMiningNodeStatus { Initializing(Option>>), /// The downstream node has completed the initial setup and is paired with an upstream pool. /// Holds common downstream data and a reference to the upstream. - Paired((CommonDownstreamData, Arc>)), + Paired(Arc>), /// A mining channel (specifically an Extended channel in this implementation) /// has been successfully opened for the downstream node. - /// Holds the `PoolChannelFactory` for this channel, common downstream data, - /// and a reference to the upstream. - ChannelOpened( - ( - PoolChannelFactory, - CommonDownstreamData, - Arc>, - ), - ), + /// Holds the `PoolChannelFactory` for this channel and a reference to the upstream. + ChannelOpened((PoolChannelFactory, Arc>)), /// The downstream node has completed initialization and is operating in solo mining mode. /// Holds common downstream data. - SoloMinerPaired(CommonDownstreamData), + SoloMinerPaired, /// The solo miner has opened a mining channel. /// Holds the `PoolChannelFactory` for this channel and common downstream data. - SoloMinerChannelOpend((PoolChannelFactory, CommonDownstreamData)), + SoloMinerChannelOpend(PoolChannelFactory), } impl DownstreamMiningNodeStatus { @@ -138,21 +130,21 @@ impl DownstreamMiningNodeStatus { DownstreamMiningNodeStatus::Initializing(_) => false, DownstreamMiningNodeStatus::Paired(_) => true, DownstreamMiningNodeStatus::ChannelOpened(_) => true, - DownstreamMiningNodeStatus::SoloMinerPaired(_) => true, + DownstreamMiningNodeStatus::SoloMinerPaired => true, DownstreamMiningNodeStatus::SoloMinerChannelOpend(_) => true, } } // Transitions the status from `Initializing` to either `Paired` (if an upstream exists) // or `SoloMinerPaired` (if in solo mining mode). - fn pair(&mut self, data: CommonDownstreamData) { + fn pair(&mut self) { match self { DownstreamMiningNodeStatus::Initializing(Some(up)) => { - let self_ = Self::Paired((data, up.clone())); + let self_ = Self::Paired(up.clone()); let _ = std::mem::replace(self, self_); } DownstreamMiningNodeStatus::Initializing(None) => { - let self_ = Self::SoloMinerPaired(data); + let self_ = Self::SoloMinerPaired; let _ = std::mem::replace(self, self_); } _ => panic!("Try to pair an already paired downstream"), @@ -165,14 +157,14 @@ impl DownstreamMiningNodeStatus { fn set_channel(&mut self, channel: PoolChannelFactory) -> bool { match self { DownstreamMiningNodeStatus::Initializing(_) => false, - DownstreamMiningNodeStatus::Paired((data, up)) => { - let self_ = Self::ChannelOpened((channel, *data, up.clone())); + DownstreamMiningNodeStatus::Paired(up) => { + let self_ = Self::ChannelOpened((channel, up.clone())); let _ = std::mem::replace(self, self_); true } DownstreamMiningNodeStatus::ChannelOpened(_) => false, - DownstreamMiningNodeStatus::SoloMinerPaired(data) => { - let self_ = Self::SoloMinerChannelOpend((channel, *data)); + DownstreamMiningNodeStatus::SoloMinerPaired => { + let self_ = Self::SoloMinerChannelOpend(channel); let _ = std::mem::replace(self, self_); true } @@ -186,9 +178,9 @@ impl DownstreamMiningNodeStatus { match self { DownstreamMiningNodeStatus::Initializing(_) => panic!(), DownstreamMiningNodeStatus::Paired(_) => panic!(), - DownstreamMiningNodeStatus::ChannelOpened((channel, _, _)) => channel, - DownstreamMiningNodeStatus::SoloMinerPaired(_) => panic!(), - DownstreamMiningNodeStatus::SoloMinerChannelOpend((channel, _)) => channel, + DownstreamMiningNodeStatus::ChannelOpened((channel, _)) => channel, + DownstreamMiningNodeStatus::SoloMinerPaired => panic!(), + DownstreamMiningNodeStatus::SoloMinerChannelOpend(channel) => channel, } } @@ -198,7 +190,7 @@ impl DownstreamMiningNodeStatus { DownstreamMiningNodeStatus::Initializing(_) => false, DownstreamMiningNodeStatus::Paired(_) => false, DownstreamMiningNodeStatus::ChannelOpened(_) => true, - DownstreamMiningNodeStatus::SoloMinerPaired(_) => false, + DownstreamMiningNodeStatus::SoloMinerPaired => false, DownstreamMiningNodeStatus::SoloMinerChannelOpend(_) => true, } } @@ -208,10 +200,10 @@ impl DownstreamMiningNodeStatus { fn get_upstream(&mut self) -> Option>> { match self { DownstreamMiningNodeStatus::Initializing(Some(up)) => Some(up.clone()), - DownstreamMiningNodeStatus::Paired((_, up)) => Some(up.clone()), - DownstreamMiningNodeStatus::ChannelOpened((_, _, up)) => Some(up.clone()), + DownstreamMiningNodeStatus::Paired(up) => Some(up.clone()), + DownstreamMiningNodeStatus::ChannelOpened((_, up)) => Some(up.clone()), DownstreamMiningNodeStatus::Initializing(None) => None, - DownstreamMiningNodeStatus::SoloMinerPaired(_) => None, + DownstreamMiningNodeStatus::SoloMinerPaired => None, DownstreamMiningNodeStatus::SoloMinerChannelOpend(_) => None, } } @@ -221,7 +213,7 @@ impl DownstreamMiningNodeStatus { matches!( self, DownstreamMiningNodeStatus::Initializing(None) - | DownstreamMiningNodeStatus::SoloMinerPaired(_) + | DownstreamMiningNodeStatus::SoloMinerPaired | DownstreamMiningNodeStatus::SoloMinerChannelOpend(_) ) } @@ -931,12 +923,7 @@ impl ParseCommonMessagesFromDownstream for DownstreamMiningNode { // require extended channels flags: 0b0000_0000_0000_0010, }; - let data = CommonDownstreamData { - header_only: false, - work_selection: false, - version_rolling: true, - }; - self.status.pair(data); + self.status.pair(); Ok(SendToCommon::Respond(response.into())) } } @@ -1108,20 +1095,3 @@ pub async fn listen_for_downstream_mining( info!("Downstream mining listener has shut down."); } - -impl IsDownstream for DownstreamMiningNode { - // Retrieves the `CommonDownstreamData` from the current status of the downstream node. - fn get_downstream_mining_data(&self) -> CommonDownstreamData { - match self.status { - DownstreamMiningNodeStatus::Initializing(_) => panic!(), - DownstreamMiningNodeStatus::Paired((data, _)) => data, - DownstreamMiningNodeStatus::ChannelOpened((_, data, _)) => data, - DownstreamMiningNodeStatus::SoloMinerPaired(data) => data, - DownstreamMiningNodeStatus::SoloMinerChannelOpend((_, data)) => data, - } - } -} - -/// Implementation of the `IsMiningDownstream` trait for `DownstreamMiningNode`. Marker trait, -/// should we remove this? -impl IsMiningDownstream for DownstreamMiningNode {} diff --git a/roles/jd-client/src/lib/upstream_sv2/upstream.rs b/roles/jd-client/src/lib/upstream_sv2/upstream.rs index e8234e1743..0ad1429f75 100644 --- a/roles/jd-client/src/lib/upstream_sv2/upstream.rs +++ b/roles/jd-client/src/lib/upstream_sv2/upstream.rs @@ -54,7 +54,6 @@ use stratum_common::{ framing_sv2, HandshakeRole, Initiator, }, common_messages_sv2::{Protocol, Reconnect, SetupConnection}, - common_properties::{IsMiningUpstream, IsUpstream}, handlers::{ common::{ParseCommonMessagesFromUpstream, SendTo as SendToCommon}, mining::{ParseMiningMessagesFromUpstream, SendTo, SupportedChannelTypes}, @@ -548,50 +547,6 @@ impl Upstream { } } -// not really used.. -impl IsUpstream for Upstream { - fn get_version(&self) -> u16 { - todo!() - } - - fn get_flags(&self) -> u32 { - todo!() - } - - fn get_supported_protocols(&self) -> Vec { - todo!() - } - - fn get_id(&self) -> u32 { - todo!() - } - - fn get_mapper(&mut self) -> Option<&mut roles_logic_sv2::common_properties::RequestIdMapper> { - todo!() - } -} - -// Not really used... -impl IsMiningUpstream for Upstream { - fn total_hash_rate(&self) -> u64 { - todo!() - } - - fn add_hash_rate(&mut self, _to_add: u64) { - todo!() - } - - fn get_opened_channels( - &mut self, - ) -> &mut Vec { - todo!() - } - - fn update_channels(&mut self, _c: roles_logic_sv2::common_properties::UpstreamChannel) { - todo!() - } -} - impl ParseCommonMessagesFromUpstream for Upstream { // Handles a `SetupConnectionSuccess` message received from the upstream pool. // diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index 027ea0c24d..586d9ea535 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -83,7 +83,7 @@ impl ParseMiningMessagesFromDownstream<()> for Downstream { pool_coinbase_outputs[0].value = Amount::from_sat(last_future_template.coinbase_tx_value_remaining); - if !self.downstream_data.header_only && self.group_channel.is_none() { + if !self.requires_standard_jobs && self.group_channel.is_none() { // we only create one group channel for all standard channels let group_channel_id = self.channel_id_factory.next(); diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 0c78545ed2..90a5e119cd 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -49,7 +49,6 @@ use stratum_common::{ codec_sv2::{ self, binary_sv2::U256, HandshakeRole, Responder, StandardEitherFrame, StandardSv2Frame, }, - common_properties::{CommonDownstreamData, IsDownstream, IsMiningDownstream}, errors::Error, handlers::mining::{ParseMiningMessagesFromDownstream, SendTo}, mining_sv2::{ @@ -115,8 +114,8 @@ pub struct Downstream { receiver: Receiver, // Channel sender for outgoing SV2 frames to the network connection task. sender: Sender, - // Common data negotiated during the connection setup (e.g., protocol version, flags). - downstream_data: CommonDownstreamData, + // Whether the downstream requires standard jobs. + requires_standard_jobs: bool, // Sender channel to forward valid `SubmitSolution` messages received from this // downstream miner to the main [`Pool`] task, which then sends them upstream. solution_sender: Sender>, @@ -187,7 +186,7 @@ impl Downstream { ) -> PoolResult>> { // Handle the SV2 SetupConnection message exchange. let setup_connection = Arc::new(Mutex::new(SetupConnectionHandler::new())); - let downstream_data = + let requires_standard_jobs = SetupConnectionHandler::setup(setup_connection, &mut receiver, &mut sender, address) .await?; @@ -239,7 +238,7 @@ impl Downstream { id, receiver, sender: sender.clone(), - downstream_data, + requires_standard_jobs, solution_sender, channel_id_factory, extended_channels: HashMap::new(), @@ -429,17 +428,6 @@ pub fn verify_token( is_verified } -impl IsDownstream for Downstream { - // Returns the `CommonDownstreamData` negotiated during connection setup. - fn get_downstream_mining_data(&self) -> CommonDownstreamData { - self.downstream_data - } -} - -// Marker trait implementation indicating this struct represents a mining downstream. Do we really -// need this? -impl IsMiningDownstream for Downstream {} - impl Pool { /// Binds to the configured listen address and starts accepting incoming TCP connections. /// @@ -542,7 +530,7 @@ impl Pool { .await?; // Extract the assigned ID after successful creation. - let (_, channel_id) = downstream.safe_lock(|d| (d.downstream_data.header_only, d.id))?; + let channel_id = downstream.safe_lock(|d| d.id)?; // Add the new downstream to the central map. self_.safe_lock(|p| { diff --git a/roles/pool/src/lib/mining_pool/setup_connection.rs b/roles/pool/src/lib/mining_pool/setup_connection.rs index 1682bb299c..a7e66fed55 100644 --- a/roles/pool/src/lib/mining_pool/setup_connection.rs +++ b/roles/pool/src/lib/mining_pool/setup_connection.rs @@ -12,11 +12,7 @@ use async_channel::{Receiver, Sender}; use std::{convert::TryInto, net::SocketAddr, sync::Arc}; use stratum_common::roles_logic_sv2::{ self, - common_messages_sv2::{ - has_requires_std_job, has_version_rolling, has_work_selection, SetupConnection, - SetupConnectionSuccess, - }, - common_properties::CommonDownstreamData, + common_messages_sv2::{has_requires_std_job, SetupConnection, SetupConnectionSuccess}, errors::Error, handlers::common::ParseCommonMessagesFromDownstream, parsers::{AnyMessage, CommonMessages}, @@ -48,7 +44,7 @@ impl SetupConnectionHandler { receiver: &mut Receiver, sender: &mut Sender, address: SocketAddr, - ) -> PoolResult { + ) -> PoolResult { // read stdFrame from receiver let mut incoming: StdFrame = match receiver.recv().await { @@ -91,12 +87,8 @@ impl SetupConnectionHandler { match message { CommonMessages::SetupConnectionSuccess(m) => { - debug!("Sent back SetupConnectionSuccess: {}", m); - Ok(CommonDownstreamData { - header_only: has_requires_std_job(m.flags), - work_selection: has_work_selection(m.flags), - version_rolling: has_version_rolling(m.flags), - }) + debug!("Sent back SetupConnectionSuccess: {:?}", m); + Ok(has_requires_std_job(m.flags)) } _ => panic!(), } diff --git a/roles/translator/src/lib/downstream_sv1/downstream.rs b/roles/translator/src/lib/downstream_sv1/downstream.rs index 9563768bd9..ac2819c893 100644 --- a/roles/translator/src/lib/downstream_sv1/downstream.rs +++ b/roles/translator/src/lib/downstream_sv1/downstream.rs @@ -37,8 +37,6 @@ use tokio::{ use super::{kill, DownstreamMessages, SubmitShareWithChannelId, SUBSCRIBE_TIMEOUT_SECS}; use stratum_common::roles_logic_sv2::{ - self, - common_properties::{IsDownstream, IsMiningDownstream}, mining_sv2::Target, utils::{hash_rate_to_target, Mutex}, vardiff::Vardiff, @@ -116,8 +114,6 @@ impl Downstream { difficulty_mgmt: DownstreamDifficultyConfig, upstream_difficulty_config: Arc>, ) -> Self { - use roles_logic_sv2::utils::hash_rate_to_target; - let hashrate = difficulty_mgmt.min_individual_miner_hashrate; let target = hash_rate_to_target(hashrate.into(), difficulty_mgmt.shares_per_minute.into()) .unwrap() @@ -711,21 +707,9 @@ impl IsServer<'static> for Downstream { } } -// Can we remove this? -impl IsMiningDownstream for Downstream {} -// Can we remove this? -impl IsDownstream for Downstream { - fn get_downstream_mining_data( - &self, - ) -> roles_logic_sv2::common_properties::CommonDownstreamData { - todo!() - } -} - #[cfg(test)] mod tests { - use roles_logic_sv2::mining_sv2::Target; - use stratum_common::roles_logic_sv2::codec_sv2::binary_sv2::U256; + use stratum_common::roles_logic_sv2::{codec_sv2::binary_sv2::U256, mining_sv2::Target}; use super::*; diff --git a/roles/translator/src/lib/proxy/bridge.rs b/roles/translator/src/lib/proxy/bridge.rs index 65e6f11cf4..f45dbd8b29 100644 --- a/roles/translator/src/lib/proxy/bridge.rs +++ b/roles/translator/src/lib/proxy/bridge.rs @@ -588,10 +588,6 @@ pub struct OpenSv1Downstream { mod test { use super::*; use async_channel::bounded; - use stratum_common::roles_logic_sv2::{ - bitcoin::{absolute::LockTime, consensus, transaction::Version}, - codec_sv2::binary_sv2, - }; pub mod test_utils { use super::*; @@ -654,87 +650,4 @@ mod test { } } } - - #[test] - fn test_version_bits_insert() { - use stratum_common::roles_logic_sv2::bitcoin::{ - self, blockdata::witness::Witness, hashes::Hash, - }; - - let extranonces = ExtendedExtranonce::new(0..6, 6..8, 8..16, None) - .expect("Failed to create ExtendedExtranonce with valid ranges"); - let (bridge, _) = test_utils::create_bridge(extranonces); - bridge - .safe_lock(|bridge| { - let channel_id = 1; - let out_id = bitcoin::hashes::sha256d::Hash::from_slice(&[ - 0_u8, 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, - ]) - .unwrap(); - let p_out = bitcoin::OutPoint { - txid: bitcoin::Txid::from_raw_hash(out_id), - vout: 0xffff_ffff, - }; - let in_ = bitcoin::TxIn { - previous_output: p_out, - script_sig: vec![89_u8; 16].into(), - sequence: bitcoin::Sequence(0), - witness: Witness::from(vec![] as Vec>), - }; - let tx = bitcoin::Transaction { - version: Version::ONE, - lock_time: LockTime::from_consensus(0), - input: vec![in_], - output: vec![], - }; - let tx = consensus::serialize(&tx); - let _down = bridge - .channel_factory - .add_standard_channel(0, 10_000_000_000.0, true, 1) - .unwrap(); - let prev_hash = SetNewPrevHash { - channel_id, - job_id: 0, - prev_hash: [ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, - ] - .into(), - min_ntime: 989898, - nbits: 9, - }; - bridge.channel_factory.on_new_prev_hash(prev_hash).unwrap(); - let now = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs() as u32; - let new_mining_job = NewExtendedMiningJob { - channel_id, - job_id: 0, - min_ntime: binary_sv2::Sv2Option::new(Some(now)), - version: 0b0000_0000_0000_0000, - version_rolling_allowed: false, - merkle_path: vec![].into(), - coinbase_tx_prefix: tx[0..42].to_vec().try_into().unwrap(), - coinbase_tx_suffix: tx[58..].to_vec().try_into().unwrap(), - }; - bridge - .channel_factory - .on_new_extended_mining_job(new_mining_job.clone()) - .unwrap(); - - // pass sv1_submit into Bridge::translate_submit - let sv1_submit = test_utils::create_sv1_submit(0); - let sv2_message = bridge - .translate_submit(channel_id, sv1_submit, None) - .unwrap(); - // assert sv2 message equals sv1 with version bits added - assert_eq!( - new_mining_job.version, sv2_message.version, - "Version bits were not inserted for non version rolling sv1 message" - ); - }) - .unwrap(); - } } diff --git a/roles/translator/src/lib/upstream_sv2/upstream.rs b/roles/translator/src/lib/upstream_sv2/upstream.rs index 543578ef37..13ff779674 100644 --- a/roles/translator/src/lib/upstream_sv2/upstream.rs +++ b/roles/translator/src/lib/upstream_sv2/upstream.rs @@ -40,7 +40,6 @@ use stratum_common::{ self, codec_sv2::{self, binary_sv2::u256_from_int, framing_sv2, HandshakeRole, Initiator}, common_messages_sv2::{Protocol, SetupConnection}, - common_properties::{IsMiningUpstream, IsUpstream}, handlers::{ common::{ParseCommonMessagesFromUpstream, SendTo as SendToCommon}, mining::{ParseMiningMessagesFromUpstream, SendTo}, @@ -604,50 +603,6 @@ impl Upstream { } } -// Can be removed? -impl IsUpstream for Upstream { - fn get_version(&self) -> u16 { - todo!() - } - - fn get_flags(&self) -> u32 { - todo!() - } - - fn get_supported_protocols(&self) -> Vec { - todo!() - } - - fn get_id(&self) -> u32 { - todo!() - } - - fn get_mapper(&mut self) -> Option<&mut roles_logic_sv2::common_properties::RequestIdMapper> { - todo!() - } -} - -// Can be removed? -impl IsMiningUpstream for Upstream { - fn total_hash_rate(&self) -> u64 { - todo!() - } - - fn add_hash_rate(&mut self, _to_add: u64) { - todo!() - } - - fn get_opened_channels( - &mut self, - ) -> &mut Vec { - todo!() - } - - fn update_channels(&mut self, _c: roles_logic_sv2::common_properties::UpstreamChannel) { - todo!() - } -} - impl ParseCommonMessagesFromUpstream for Upstream { // Handles the SV2 `SetupConnectionSuccess` message received from the upstream. // From 3341c2e9177f299b5099af9919bf5d60356409aa Mon Sep 17 00:00:00 2001 From: plebhash Date: Wed, 25 Jun 2025 18:10:32 -0300 Subject: [PATCH 070/338] deprecate roles_logic_sv2::job_dispatcher --- .../v2/roles-logic-sv2/src/job_dispatcher.rs | 146 ------------------ protocols/v2/roles-logic-sv2/src/lib.rs | 1 - 2 files changed, 147 deletions(-) delete mode 100644 protocols/v2/roles-logic-sv2/src/job_dispatcher.rs diff --git a/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs b/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs deleted file mode 100644 index 15489eef7f..0000000000 --- a/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs +++ /dev/null @@ -1,146 +0,0 @@ -//! Job Dispatcher -//! -//! This module contains relevant logic to maintain group channels in proxy roles such as: -//! - converting extended jobs to standard jobs -//! - handling updates to jobs when new templates and prev hashes arrive, as well as cleaning up old -//! jobs -//! - determining if submitted shares correlate to valid jobs - -use crate::utils::merkle_root_from_path; -use mining_sv2::{ - NewExtendedMiningJob, NewMiningJob, SubmitSharesError, SubmitSharesStandard, Target, -}; -use std::convert::TryInto; - -use bitcoin::hashes::{sha256d, Hash, HashEngine}; - -/// Used to convert an extended mining job to a standard mining job. The `extranonce` field must -/// be exactly 32 bytes. -pub fn extended_to_standard_job_for_group_channel<'a>( - extended: &NewExtendedMiningJob, - extranonce: &[u8], - channel_id: u32, - job_id: u32, -) -> Option> { - let merkle_root = merkle_root_from_path( - extended.coinbase_tx_prefix.inner_as_ref(), - extended.coinbase_tx_suffix.inner_as_ref(), - extranonce, - &extended.merkle_path.inner_as_ref(), - ); - - Some(NewMiningJob { - channel_id, - job_id, - min_ntime: extended.min_ntime.clone().into_static(), - version: extended.version, - merkle_root: merkle_root?.try_into().ok()?, - }) -} - -// helper struct to easily calculate block hashes from headers -#[allow(dead_code)] -struct Header<'a> { - version: u32, - prev_hash: &'a [u8], - merkle_root: &'a [u8], - timestamp: u32, - nbits: u32, - nonce: u32, -} - -impl Header<'_> { - // calculates the sha256 blockhash of the header - #[allow(dead_code)] - pub fn hash(&self) -> Target { - let mut engine = sha256d::Hash::engine(); - engine.input(&self.version.to_le_bytes()); - engine.input(self.prev_hash); - engine.input(self.merkle_root); - engine.input(&self.timestamp.to_be_bytes()); - engine.input(&self.nbits.to_be_bytes()); - engine.input(&self.nonce.to_be_bytes()); - let hashed: [u8; 32] = *sha256d::Hash::from_engine(engine).as_ref(); - hashed.into() - } -} - -/// Used to signal if submitted shares correlate to valid jobs -pub enum SendSharesResponse { - /// ValidAndMeetUpstreamTarget((SubmitSharesStandard,SubmitSharesSuccess)), - Valid(SubmitSharesStandard), - Invalid(SubmitSharesError<'static>), -} - -#[cfg(test)] -mod tests { - use super::*; - use codec_sv2::binary_sv2::U256; - - #[test] - fn test_block_hash() { - let le_version = "0x32950000".strip_prefix("0x").unwrap(); - let be_prev_hash = "0x00000000000000000004e962c1a0fc6a201d937bf08ffe4b1221e956615c7cd9"; - let be_merkle_root = "0x897dff6755a7c255455f1b2a2c8ad44ad1b6c23ef00fbf501d0dde7e42cd8c71"; - let le_timestamp = "0x637B9A4C".strip_prefix("0x").unwrap(); - let le_nbits = "0x17079e15".strip_prefix("0x").unwrap(); - let le_nonce = "0x102aa10".strip_prefix("0x").unwrap(); - - let le_version = u32::from_str_radix(le_version, 16).expect("Failed converting hex to u32"); - let mut be_prev_hash = - utils::decode_hex(be_prev_hash).expect("Failed converting hex to bytes"); - let mut be_merkle_root = - utils::decode_hex(be_merkle_root).expect("Failed converting hex to bytes"); - let le_timestamp: u32 = - u32::from_str_radix(le_timestamp, 16).expect("Failed converting hex to u32"); - let le_nbits = u32::from_str_radix(le_nbits, 16).expect("Failed converting hex to u32"); - let le_nonce = u32::from_str_radix(le_nonce, 16).expect("Failed converting hex to u32"); - be_prev_hash.reverse(); - be_merkle_root.reverse(); - let le_prev_hash = be_prev_hash.as_slice(); - let le_merkle_root = be_merkle_root.as_slice(); - - let block_header: Header = Header { - version: le_version, - prev_hash: le_prev_hash, - merkle_root: le_merkle_root, - timestamp: le_timestamp.to_be(), - nbits: le_nbits.to_be(), - nonce: le_nonce.to_be(), - }; - - let target = U256::from(block_header.hash()); - let mut actual_block_hash = - utils::decode_hex("00000000000000000000199349a95526c4f83959f0ef06697048a297f25e7fac") - .expect("Failed converting hex to bytes"); - actual_block_hash.reverse(); - assert_eq!( - target.to_vec(), - actual_block_hash, - "Computed block hash does not equal the actaul block hash" - ); - } - - pub mod utils { - use std::fmt::Write; - - pub fn decode_hex(s: &str) -> Result, core::num::ParseIntError> { - let s = match s.strip_prefix("0x") { - Some(s) => s, - None => s, - }; - (0..s.len()) - .step_by(2) - .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) - .collect() - } - - pub fn _encode_hex(bytes: &[u8]) -> String { - let mut s = String::with_capacity(bytes.len() * 2); - for &b in bytes { - write!(&mut s, "{b:02x}").unwrap(); - } - s - } - } -} diff --git a/protocols/v2/roles-logic-sv2/src/lib.rs b/protocols/v2/roles-logic-sv2/src/lib.rs index c550c91e57..865b73b92f 100644 --- a/protocols/v2/roles-logic-sv2/src/lib.rs +++ b/protocols/v2/roles-logic-sv2/src/lib.rs @@ -22,7 +22,6 @@ pub mod channels; pub mod errors; pub mod handlers; pub mod job_creator; -pub mod job_dispatcher; pub mod parsers; pub mod utils; pub mod vardiff; From b6a30e7ea5afc5072b6f9d9d0a689de938647686 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 8 Jul 2025 23:49:23 +0530 Subject: [PATCH 071/338] use cursor in deserialize_template_outputs --- protocols/v2/roles-logic-sv2/src/utils.rs | 39 ++++++----------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/utils.rs b/protocols/v2/roles-logic-sv2/src/utils.rs index 654e935dc4..498d5ebad6 100644 --- a/protocols/v2/roles-logic-sv2/src/utils.rs +++ b/protocols/v2/roles-logic-sv2/src/utils.rs @@ -22,6 +22,7 @@ use std::{ cmp::max, convert::TryInto, fmt::Write, + io::Cursor, ops::Div, sync::{Mutex as Mutex_, MutexGuard, PoisonError}, }; @@ -67,36 +68,14 @@ pub fn deserialize_template_outputs( serialized_outputs: Vec, coinbase_tx_outputs_count: u32, ) -> Result, Error> { - let mut deserialized_outputs: Vec = vec![]; - - // The serialized outputs are in Bitcoin consensus format - // We need to parse them one by one, keeping track of cursor position - let mut cursor = 0; - let mut txouts = &serialized_outputs[cursor..]; - - // Iteratively decode each TxOut until we can't decode any more - while let Ok(out) = TxOut::consensus_decode(&mut txouts) { - // Calculate the size of this TxOut based on its script_pubkey length - // 8 bytes for value + variable bytes for script_pubkey length - // For small scripts (0-252 bytes): 1 byte length prefix - // For medium scripts (253-1000000 bytes): 3 byte length prefix (1 marker + 2 byte - // length) - let len = match out.script_pubkey.len() { - a @ 0..=252 => 8 + 1 + a, // 8 (value) + 1 (compact size) + script_len - a @ 253..=1000000 => 8 + 3 + a, // 8 (value) + 3 (compact size) + script_len - _ => break, // Unreasonably large script, likely an error - }; - - // Move the cursor forward by the size of this TxOut - cursor += len; - deserialized_outputs.push(out); - } - - if deserialized_outputs.len() != coinbase_tx_outputs_count as usize { - return Err(Error::FailedToDeserializeCoinbaseOutputs); - } - - Ok(deserialized_outputs) + let mut cursor = Cursor::new(serialized_outputs); + + (0..coinbase_tx_outputs_count) + .map(|_| { + TxOut::consensus_decode(&mut cursor) + .map_err(|_| Error::FailedToDeserializeCoinbaseOutputs) + }) + .collect() } /// Custom synchronization primitive for managing shared mutable state. From 29766b3afd175542773d0c2cfdb24a942a09f234 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Fri, 4 Jul 2025 12:14:13 -0400 Subject: [PATCH 072/338] add logging initialization to config_helpers --- roles/Cargo.lock | 70 ++++++++++++++++++- roles/roles-utils/config-helpers/Cargo.toml | 2 + roles/roles-utils/config-helpers/src/lib.rs | 2 + .../roles-utils/config-helpers/src/logging.rs | 41 +++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 roles/roles-utils/config-helpers/src/logging.rs diff --git a/roles/Cargo.lock b/roles/Cargo.lock index 9c62a8b739..a2ea619dbf 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -75,6 +75,15 @@ dependencies = [ "zerocopy 0.7.35", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -688,6 +697,8 @@ dependencies = [ "miniscript", "roles_logic_sv2", "serde", + "tracing", + "tracing-subscriber", ] [[package]] @@ -1562,6 +1573,15 @@ dependencies = [ "value-bag", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.7.4" @@ -2100,6 +2120,50 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "ring" version = "0.17.14" @@ -2663,10 +2727,14 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] diff --git a/roles/roles-utils/config-helpers/Cargo.toml b/roles/roles-utils/config-helpers/Cargo.toml index 3b6858a2ec..27e057a160 100644 --- a/roles/roles-utils/config-helpers/Cargo.toml +++ b/roles/roles-utils/config-helpers/Cargo.toml @@ -15,3 +15,5 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] serde = { version = "1.0.89", features = ["derive","alloc"], default-features = false } miniscript = { version = "12.3.2", default-features = false, features = [ "no-std" ] } roles_logic_sv2 = { path = "../../../protocols/v2/roles-logic-sv2" } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tracing = { version = "0.1" } diff --git a/roles/roles-utils/config-helpers/src/lib.rs b/roles/roles-utils/config-helpers/src/lib.rs index e15fec1b5f..79c7dc6e5a 100644 --- a/roles/roles-utils/config-helpers/src/lib.rs +++ b/roles/roles-utils/config-helpers/src/lib.rs @@ -3,3 +3,5 @@ pub use coinbase_output::{CoinbaseOutput, Error as CoinbaseOutputError}; mod toml; pub use toml::duration_from_toml; + +pub mod logging; diff --git a/roles/roles-utils/config-helpers/src/logging.rs b/roles/roles-utils/config-helpers/src/logging.rs new file mode 100644 index 0000000000..4e7673a10f --- /dev/null +++ b/roles/roles-utils/config-helpers/src/logging.rs @@ -0,0 +1,41 @@ +use std::{fs::OpenOptions, io, path::Path, str::FromStr}; +use tracing::level_filters::LevelFilter; +use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; + +/// Initialize logging to stdout and optionally to a file. +/// +/// If `log_file` is Some, logs will be written to both stdout and the file. +/// If `log_level` is not provided or is invalid, it defaults to "info". +pub fn init_logging(log_file: Option<&Path>) { + let rust_log = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string()); + let log_level_filter = LevelFilter::from_str(&rust_log).unwrap_or(LevelFilter::INFO); + let env_filter = EnvFilter::new(log_level_filter.to_string()); + + let subscriber: Box = match log_file { + Some(path) => { + // Log to both file and stdout + let path = path.to_owned(); + let file_layer = fmt::layer().with_writer(move || { + OpenOptions::new() + .create(true) + .append(true) + .open(&path) + .expect("Failed to open log file") + }); + let stdout_layer = fmt::layer().with_writer(io::stdout); + Box::new( + Registry::default() + .with(env_filter) + .with(stdout_layer) + .with(file_layer), + ) + } + None => { + // Log only to stdout + let stdout_layer = fmt::layer().with_writer(io::stdout); + Box::new(Registry::default().with(env_filter).with(stdout_layer)) + } + }; + + tracing::subscriber::set_global_default(subscriber).expect("Failed to set global subscriber"); +} From 2b44bef35507134d75477487640945268c9deb75 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 3 Jul 2025 14:57:28 -0400 Subject: [PATCH 073/338] add logging initialization and CLI argument enhancements for Pool --- roles/Cargo.lock | 3 +- roles/pool/Cargo.toml | 1 - .../pool-config-hosted-tp-example.toml | 9 ++- .../pool-config-local-tp-example.toml | 6 ++ roles/pool/src/args.rs | 11 +++- roles/pool/src/lib/config.rs | 15 +++++ roles/pool/src/main.rs | 3 +- test/integration-tests/Cargo.lock | 66 +++++++++++++++++++ 8 files changed, 107 insertions(+), 7 deletions(-) diff --git a/roles/Cargo.lock b/roles/Cargo.lock index a2ea619dbf..9255f217bc 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "addr2line" @@ -1989,7 +1989,6 @@ dependencies = [ "stratum-common", "tokio", "tracing", - "tracing-subscriber", ] [[package]] diff --git a/roles/pool/Cargo.toml b/roles/pool/Cargo.toml index 378a14fbba..d47e6a276e 100644 --- a/roles/pool/Cargo.toml +++ b/roles/pool/Cargo.toml @@ -26,7 +26,6 @@ secp256k1 = { version = "0.28.2", default-features = false, features = ["alloc", tokio = { version = "1.44.1", features = ["full"] } ext-config = { version = "0.14.0", features = ["toml"], package = "config" } tracing = { version = "0.1" } -tracing-subscriber = "0.3" async-recursion = "1.0.0" error_handling = { path = "../../utils/error-handling" } nohash-hasher = "0.2.0" diff --git a/roles/pool/config-examples/pool-config-hosted-tp-example.toml b/roles/pool/config-examples/pool-config-hosted-tp-example.toml index 06f3afafab..8e639a6894 100644 --- a/roles/pool/config-examples/pool-config-hosted-tp-example.toml +++ b/roles/pool/config-examples/pool-config-hosted-tp-example.toml @@ -2,7 +2,7 @@ authority_public_key = "9auqWEzQDVyd2oe1JVGFLMLHZtCo2FFqZwtKA5gd9xbuEu7PH72" authority_secret_key = "mkDLTBBRxdBv998612qipDYoTK3YUrqLe8uWw7gu3iXbSrn2n" cert_validity_sec = 3600 -test_only_listen_adress_plain = "0.0.0.0:34250" +test_only_listen_adress_plain = "0.0.0.0:34250" listen_address = "0.0.0.0:34254" # List of coinbase outputs used to build the coinbase tx @@ -20,6 +20,11 @@ coinbase_outputs = [ # Pool signature (string to be included in coinbase tx) pool_signature = "Stratum V2 SRI Pool" +# Enable this option to set a predefined log file path. +# When enabled, logs will always be written to this file. +# The CLI option --log-file (or -f) will override this setting if provided. +# log_file = "./pool.log" + # Template Provider config # Local TP (this is pointing to localhost so you must run a TP locally for this configuration to work) #tp_address = "127.0.0.1:8442" @@ -27,4 +32,4 @@ pool_signature = "Stratum V2 SRI Pool" tp_address = "75.119.150.111:8442" tp_authority_public_key = "9bwHCYnjhbHm4AS3pWg9MtAH83mzWohoJJJDELYBqZhDNqszDLc" shares_per_minute = 1.0 -share_batch_size = 10 \ No newline at end of file +share_batch_size = 10 diff --git a/roles/pool/config-examples/pool-config-local-tp-example.toml b/roles/pool/config-examples/pool-config-local-tp-example.toml index 54660d0b42..fe05f1d588 100644 --- a/roles/pool/config-examples/pool-config-local-tp-example.toml +++ b/roles/pool/config-examples/pool-config-local-tp-example.toml @@ -20,6 +20,12 @@ coinbase_outputs = [ # Pool signature (string to be included in coinbase tx) pool_signature = "Stratum V2 SRI Pool" +# Enable this option to set a predefined log file path. +# When enabled, logs will always be written to this file. +# The CLI option --log-file (or -f) will override this setting if provided. +# log_file = "./pool.log" + + # Template Provider config # Local TP (this is pointing to localhost so you must run a TP locally for this configuration to work) tp_address = "127.0.0.1:8442" diff --git a/roles/pool/src/args.rs b/roles/pool/src/args.rs index 3816f5a25d..c2c2186b34 100644 --- a/roles/pool/src/args.rs +++ b/roles/pool/src/args.rs @@ -18,16 +18,25 @@ pub struct Args { default_value = "pool-config.toml" )] pub config_path: PathBuf, + #[arg( + short = 'f', + long = "log-file", + help = "Path to the log file. If not set, logs will only be written to stdout." + )] + pub log_file: Option, } /// Parses CLI arguments and loads the PoolConfig from the specified file. pub fn process_cli_args() -> PoolConfig { let args = Args::parse(); let config_path = args.config_path.to_str().expect("Invalid config path"); - let config: PoolConfig = Config::builder() + let mut config: PoolConfig = Config::builder() .add_source(File::new(config_path, FileFormat::Toml)) .build() .and_then(|settings| settings.try_deserialize::()) .expect("Failed to load or deserialize config"); + + config.set_log_dir(args.log_file); + config } diff --git a/roles/pool/src/lib/config.rs b/roles/pool/src/lib/config.rs index 452c1e9a9c..c8a1b9dd44 100644 --- a/roles/pool/src/lib/config.rs +++ b/roles/pool/src/lib/config.rs @@ -8,6 +8,8 @@ //! - Managing [`TemplateProviderConfig`], [`AuthorityConfig`], [`CoinbaseOutput`], and //! [`ConnectionConfig`] //! - Validating and converting coinbase outputs +use std::path::{Path, PathBuf}; + use config_helpers::CoinbaseOutput; use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; @@ -24,6 +26,7 @@ pub struct PoolConfig { pool_signature: String, shares_per_minute: f32, share_batch_size: usize, + log_file: Option, } impl PoolConfig { @@ -47,6 +50,7 @@ impl PoolConfig { pool_signature: pool_connection.signature, shares_per_minute, share_batch_size, + log_file: None, } } @@ -109,6 +113,17 @@ impl PoolConfig { pub fn set_tp_address(&mut self, tp_address: String) { self.tp_address = tp_address; } + + /// Sets the log directory. + pub fn set_log_dir(&mut self, log_dir: Option) { + if let Some(dir) = log_dir { + self.log_file = Some(dir); + } + } + /// Returns the log directory. + pub fn log_dir(&self) -> Option<&Path> { + self.log_file.as_deref() + } } /// Configuration for connecting to a Template Provider. diff --git a/roles/pool/src/main.rs b/roles/pool/src/main.rs index 3b380be56d..124e0c498f 100644 --- a/roles/pool/src/main.rs +++ b/roles/pool/src/main.rs @@ -11,12 +11,13 @@ use tracing::{error, info}; mod args; use args::process_cli_args; +use config_helpers::logging::init_logging; /// Initializes logging, parses arguments, loads configuration, and starts the Pool runtime. #[tokio::main] async fn main() { - tracing_subscriber::fmt::init(); let config = process_cli_args(); + init_logging(config.log_dir()); let _ = PoolSv2::new(config).start().await; select! { interrupt_signal = tokio::signal::ctrl_c() => { diff --git a/test/integration-tests/Cargo.lock b/test/integration-tests/Cargo.lock index 104ac721d9..89f4d21d47 100644 --- a/test/integration-tests/Cargo.lock +++ b/test/integration-tests/Cargo.lock @@ -75,6 +75,15 @@ dependencies = [ "zerocopy 0.7.35", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -1367,6 +1376,15 @@ version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.7.4" @@ -1864,6 +1882,50 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "ring" version = "0.17.8" @@ -2435,10 +2497,14 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] From cf883043faf0de628ec2b32a01a2ab64dfb58dd6 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 3 Jul 2025 15:21:55 -0400 Subject: [PATCH 074/338] add logging initialization and CLI argument enhancements for Translator Proxy --- roles/Cargo.lock | 2 +- roles/translator/Cargo.toml | 3 ++- .../tproxy-config-hosted-pool-example.toml | 5 +++++ .../tproxy-config-local-jdc-example.toml | 5 +++++ .../tproxy-config-local-pool-example.toml | 5 +++++ roles/translator/src/args.rs | 11 ++++++++++- roles/translator/src/lib/config.rs | 17 +++++++++++++++++ roles/translator/src/main.rs | 6 +++--- 8 files changed, 48 insertions(+), 6 deletions(-) diff --git a/roles/Cargo.lock b/roles/Cargo.lock index 9255f217bc..656f50eddd 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -2747,6 +2747,7 @@ dependencies = [ "buffer_sv2", "clap", "config", + "config-helpers", "error_handling", "futures", "key-utils", @@ -2761,7 +2762,6 @@ dependencies = [ "tokio", "tokio-util", "tracing", - "tracing-subscriber", ] [[package]] diff --git a/roles/translator/Cargo.toml b/roles/translator/Cargo.toml index 714ddafaf4..a1a0094f7c 100644 --- a/roles/translator/Cargo.toml +++ b/roles/translator/Cargo.toml @@ -31,7 +31,6 @@ futures = "0.3.25" tokio = { version = "1.44.1", features = ["full"] } ext-config = { version = "0.14.0", features = ["toml"], package = "config" } tracing = { version = "0.1" } -tracing-subscriber = { version = "0.3" } v1 = { path = "../../protocols/v1", package="sv1_api" } error_handling = { path = "../../utils/error-handling" } key-utils = { path = "../../utils/key-utils" } @@ -39,6 +38,8 @@ tokio-util = { version = "0.7.10", features = ["codec"] } rand = "0.8.4" primitive-types = "0.13.1" clap = { version = "4.5.39", features = ["derive"] } +config-helpers = { path = "../roles-utils/config-helpers" } + [dev-dependencies] sha2 = "0.10.6" diff --git a/roles/translator/config-examples/tproxy-config-hosted-pool-example.toml b/roles/translator/config-examples/tproxy-config-hosted-pool-example.toml index ec706471c9..aa616fe832 100644 --- a/roles/translator/config-examples/tproxy-config-hosted-pool-example.toml +++ b/roles/translator/config-examples/tproxy-config-hosted-pool-example.toml @@ -22,6 +22,11 @@ min_supported_version = 2 # Min value: 2 min_extranonce2_size = 4 +# Enable this option to set a predefined log file path. +# When enabled, logs will always be written to this file. +# The CLI option --log-file (or -f) will override this setting if provided. +# log_file = "./tproxy.log" + # Difficulty params [downstream_difficulty_config] # hashes/s of the weakest miner that will be connecting (e.g.: 10 Th/s = 10_000_000_000_000.0) diff --git a/roles/translator/config-examples/tproxy-config-local-jdc-example.toml b/roles/translator/config-examples/tproxy-config-local-jdc-example.toml index 62a5a5ac68..aa53dd40f3 100644 --- a/roles/translator/config-examples/tproxy-config-local-jdc-example.toml +++ b/roles/translator/config-examples/tproxy-config-local-jdc-example.toml @@ -22,6 +22,11 @@ min_supported_version = 2 # Min value: 2 min_extranonce2_size = 4 +# Enable this option to set a predefined log file path. +# When enabled, logs will always be written to this file. +# The CLI option --log-file (or -f) will override this setting if provided. +# log_file = "./tproxy.log" + # Difficulty params [downstream_difficulty_config] # hashes/s of the weakest miner that will be connecting (e.g.: 10 Th/s = 10_000_000_000_000.0) diff --git a/roles/translator/config-examples/tproxy-config-local-pool-example.toml b/roles/translator/config-examples/tproxy-config-local-pool-example.toml index 22c3dc1775..bc9e552277 100644 --- a/roles/translator/config-examples/tproxy-config-local-pool-example.toml +++ b/roles/translator/config-examples/tproxy-config-local-pool-example.toml @@ -22,6 +22,11 @@ min_supported_version = 2 # Min value: 2 min_extranonce2_size = 4 +# Enable this option to set a predefined log file path. +# When enabled, logs will always be written to this file. +# The CLI option --log-file (or -f) will override this setting if provided. +# log_file = "./tproxy.log" + # Difficulty params [downstream_difficulty_config] # hashes/s of the weakest miner that will be connecting (e.g.: 10 Th/s = 10_000_000_000_000.0) diff --git a/roles/translator/src/args.rs b/roles/translator/src/args.rs index 91df433085..2baa9ff600 100644 --- a/roles/translator/src/args.rs +++ b/roles/translator/src/args.rs @@ -22,6 +22,12 @@ pub struct Args { default_value = "proxy-config.toml" )] pub config_path: PathBuf, + #[arg( + short = 'f', + long = "log-file", + help = "Path to the log file. If not set, logs will only be written to stdout." + )] + pub log_file: Option, } /// Process CLI args, if any. @@ -41,6 +47,9 @@ pub fn process_cli_args<'a>() -> ProxyResult<'a, TranslatorConfig> { .build()?; // Deserialize settings into TranslatorConfig - let config = settings.try_deserialize::()?; + let mut config = settings.try_deserialize::()?; + + config.set_log_dir(args.log_file); + Ok(config) } diff --git a/roles/translator/src/lib/config.rs b/roles/translator/src/lib/config.rs index 91c0f54f41..85f5b522d2 100644 --- a/roles/translator/src/lib/config.rs +++ b/roles/translator/src/lib/config.rs @@ -11,6 +11,8 @@ //! - Supported protocol versions //! - Downstream difficulty adjustment parameters ([`DownstreamDifficultyConfig`]) //! - Upstream difficulty adjustment parameters ([`UpstreamDifficultyConfig`]) +use std::path::{Path, PathBuf}; + use key_utils::Secp256k1PublicKey; use serde::Deserialize; @@ -37,7 +39,21 @@ pub struct TranslatorConfig { pub downstream_difficulty_config: DownstreamDifficultyConfig, /// Configuration settings for managing difficulty on the upstream connection. pub upstream_difficulty_config: UpstreamDifficultyConfig, + /// The path to the log file for the Translator. + log_file: Option, +} + +impl TranslatorConfig { + pub fn set_log_dir(&mut self, log_dir: Option) { + if let Some(dir) = log_dir { + self.log_file = Some(dir); + } + } + pub fn log_dir(&self) -> Option<&Path> { + self.log_file.as_deref() + } } + /// Configuration settings specific to the upstream connection. pub struct UpstreamConfig { /// The address of the upstream server. @@ -109,6 +125,7 @@ impl TranslatorConfig { min_extranonce2_size, downstream_difficulty_config: downstream.difficulty_config, upstream_difficulty_config: upstream.difficulty_config, + log_file: None, } } } diff --git a/roles/translator/src/main.rs b/roles/translator/src/main.rs index 0e4ecb6a2b..38d4139720 100644 --- a/roles/translator/src/main.rs +++ b/roles/translator/src/main.rs @@ -1,4 +1,5 @@ mod args; + pub use translator_sv2::{ config, downstream_sv1, error, proxy, status, upstream_sv2, TranslatorSv2, }; @@ -6,19 +7,18 @@ pub use translator_sv2::{ use tracing::info; use crate::args::process_cli_args; - +use config_helpers::logging::init_logging; /// Entrypoint for the Translator binary. /// /// Loads the configuration from TOML and initializes the main runtime /// defined in `translator_sv2::TranslatorSv2`. Errors during startup are logged. #[tokio::main] async fn main() { - tracing_subscriber::fmt::init(); - let proxy_config = match process_cli_args() { Ok(p) => p, Err(e) => panic!("failed to load config: {e}"), }; + init_logging(proxy_config.log_dir()); info!("Proxy Config: {:?}", &proxy_config); TranslatorSv2::new(proxy_config).start().await; From 170cdbcd1759ad3177dacb045842d8cd79eeff0f Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 3 Jul 2025 15:30:29 -0400 Subject: [PATCH 075/338] add logging initialization and CLI argument enhancements for Job Declarator Server --- roles/Cargo.lock | 1 - roles/jd-server/Cargo.toml | 1 - .../jds-config-hosted-example.toml | 5 +++++ .../jds-config-local-example.toml | 5 +++++ roles/jd-server/src/args.rs | 13 ++++++++++++- roles/jd-server/src/lib/config.rs | 16 +++++++++++++++- roles/jd-server/src/main.rs | 8 ++++---- 7 files changed, 41 insertions(+), 8 deletions(-) diff --git a/roles/Cargo.lock b/roles/Cargo.lock index 656f50eddd..ff3cc8a6dd 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -1462,7 +1462,6 @@ dependencies = [ "stratum-common", "tokio", "tracing", - "tracing-subscriber", ] [[package]] diff --git a/roles/jd-server/Cargo.toml b/roles/jd-server/Cargo.toml index 224888710f..7af32024d0 100644 --- a/roles/jd-server/Cargo.toml +++ b/roles/jd-server/Cargo.toml @@ -24,7 +24,6 @@ rand = "0.8.4" tokio = { version = "1.44.1", features = ["full"] } ext-config = { version = "0.14.0", features = ["toml"], package = "config" } tracing = { version = "0.1" } -tracing-subscriber = "0.3" error_handling = { path = "../../utils/error-handling" } nohash-hasher = "0.2.0" serde_json = { version = "1.0", default-features = false, features = ["alloc","raw_value"] } diff --git a/roles/jd-server/config-examples/jds-config-hosted-example.toml b/roles/jd-server/config-examples/jds-config-hosted-example.toml index 7d5afbbf75..d9bd59531b 100644 --- a/roles/jd-server/config-examples/jds-config-hosted-example.toml +++ b/roles/jd-server/config-examples/jds-config-hosted-example.toml @@ -18,6 +18,11 @@ coinbase_outputs = [ #{ output_script_type = "P2TR", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, ] +# Enable this option to set a predefined log file path. +# When enabled, logs will always be written to this file. +# The CLI option --log-file (or -f) will override this setting if provided. +# log_file = "./jd-server.log" + # SRI Pool JD config listen_jd_address = "0.0.0.0:34264" # RPC config for mempool (it can be also the same TP if correctly configured) diff --git a/roles/jd-server/config-examples/jds-config-local-example.toml b/roles/jd-server/config-examples/jds-config-local-example.toml index 91cada6369..b23e0b4a48 100644 --- a/roles/jd-server/config-examples/jds-config-local-example.toml +++ b/roles/jd-server/config-examples/jds-config-local-example.toml @@ -18,6 +18,11 @@ coinbase_outputs = [ #{ output_script_type = "P2TR", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, ] +# Enable this option to set a predefined log file path. +# When enabled, logs will always be written to this file. +# The CLI option --log-file (or -f) will override this setting if provided. +# log_file = "./jd-server.log" + # SRI Pool JD config listen_jd_address = "127.0.0.1:34264" # RPC config for mempool (it can be also the same TP if correctly configured) diff --git a/roles/jd-server/src/args.rs b/roles/jd-server/src/args.rs index 9b265670c6..e0e0d26400 100644 --- a/roles/jd-server/src/args.rs +++ b/roles/jd-server/src/args.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use clap::Parser; use ext_config::{Config, File, FileFormat}; use jd_server::{ @@ -23,6 +25,12 @@ pub struct Args { default_value = "jds-config.toml" )] pub config_path: std::path::PathBuf, + #[arg( + short = 'f', + long = "log-file", + help = "Path to the log file. If not set, logs will only be written to stdout." + )] + pub log_file: Option, } /// Process CLI args and load configuration. @@ -46,11 +54,14 @@ pub fn process_cli_args() -> Result { })?; // Deserialize settings into JobDeclaratorServerConfig - let config = settings + let mut config = settings .try_deserialize::() .map_err(|e| { error!("Failed to deserialize config: {}", e); JdsError::BadCliArgs })?; + + config.set_log_file(args.log_file); + Ok(config) } diff --git a/roles/jd-server/src/lib/config.rs b/roles/jd-server/src/lib/config.rs index 2b9466be23..fef6bc7f86 100644 --- a/roles/jd-server/src/lib/config.rs +++ b/roles/jd-server/src/lib/config.rs @@ -14,7 +14,11 @@ use config_helpers::CoinbaseOutput; use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; use serde::Deserialize; -use std::{convert::TryInto, time::Duration}; +use std::{ + convert::TryInto, + path::{Path, PathBuf}, + time::Duration, +}; use stratum_common::roles_logic_sv2::bitcoin::{Amount, TxOut}; #[derive(Debug, serde::Deserialize, Clone)] @@ -32,6 +36,7 @@ pub struct JobDeclaratorServerConfig { core_rpc_pass: String, #[serde(deserialize_with = "config_helpers::duration_from_toml")] mempool_update_interval: Duration, + log_file: Option, } impl JobDeclaratorServerConfig { @@ -57,6 +62,7 @@ impl JobDeclaratorServerConfig { core_rpc_user: core_rpc.user, core_rpc_pass: core_rpc.pass, mempool_update_interval, + log_file: None, } } @@ -143,6 +149,14 @@ impl JobDeclaratorServerConfig { _ => Ok(result), } } + pub fn log_file(&self) -> Option<&Path> { + self.log_file.as_deref() + } + pub fn set_log_file(&mut self, log_file: Option) { + if let Some(path) = log_file { + self.log_file = Some(path); + } + } } fn default_true() -> bool { diff --git a/roles/jd-server/src/main.rs b/roles/jd-server/src/main.rs index 77d874bd0b..88cc8ce6da 100644 --- a/roles/jd-server/src/main.rs +++ b/roles/jd-server/src/main.rs @@ -6,7 +6,8 @@ //! The actual task orchestration and shutdown logic are managed in `lib/mod.rs`. mod args; use args::process_cli_args; -use jd_server::{config::JobDeclaratorServerConfig, JobDeclaratorServer}; +use config_helpers::logging::init_logging; +use jd_server::JobDeclaratorServer; use tracing::error; /// Entrypoint for the Job Declarator Server binary. @@ -15,14 +16,13 @@ use tracing::error; /// defined in `jd_server::JobDeclaratorServer`. Errors during startup are logged. #[tokio::main] async fn main() { - tracing_subscriber::fmt::init(); - let config: JobDeclaratorServerConfig = match process_cli_args() { + let config = match process_cli_args() { Ok(cfg) => cfg, Err(e) => { error!("Failed to process CLI arguments: {}", e); return; } }; - + init_logging(config.log_file()); let _ = JobDeclaratorServer::new(config).start().await; } From 7c0dbe66ee18d26f6c1a7405f16215ef826650c3 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 3 Jul 2025 15:36:28 -0400 Subject: [PATCH 076/338] add logging initialization and CLI argument enhancements for Job Declarator Client --- roles/Cargo.lock | 1 - roles/jd-client/Cargo.toml | 1 - .../jdc-config-hosted-example.toml | 5 +++++ .../jdc-config-local-example.toml | 5 +++++ roles/jd-client/src/args.rs | 11 ++++++++++- roles/jd-client/src/lib/config.rs | 18 +++++++++++++++++- roles/jd-client/src/main.rs | 9 +++++---- 7 files changed, 42 insertions(+), 8 deletions(-) diff --git a/roles/Cargo.lock b/roles/Cargo.lock index ff3cc8a6dd..00e33d82fe 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -1438,7 +1438,6 @@ dependencies = [ "stratum-common", "tokio", "tracing", - "tracing-subscriber", ] [[package]] diff --git a/roles/jd-client/Cargo.toml b/roles/jd-client/Cargo.toml index 0afc5117ef..1becd0ba39 100644 --- a/roles/jd-client/Cargo.toml +++ b/roles/jd-client/Cargo.toml @@ -26,7 +26,6 @@ futures = "0.3.25" tokio = { version = "1.44.1", features = ["full"] } ext-config = { version = "0.14.0", features = ["toml"], package = "config" } tracing = { version = "0.1" } -tracing-subscriber = { version = "0.3" } error_handling = { path = "../../utils/error-handling" } nohash-hasher = "0.2.0" key-utils = { path = "../../utils/key-utils" } diff --git a/roles/jd-client/config-examples/jdc-config-hosted-example.toml b/roles/jd-client/config-examples/jdc-config-hosted-example.toml index bf80c2c236..473db3b27c 100644 --- a/roles/jd-client/config-examples/jdc-config-hosted-example.toml +++ b/roles/jd-client/config-examples/jdc-config-hosted-example.toml @@ -39,6 +39,11 @@ coinbase_outputs = [ #{ output_script_type = "P2TR", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, ] +# Enable this option to set a predefined log file path. +# When enabled, logs will always be written to this file. +# The CLI option --log-file (or -f) will override this setting if provided. +# log_file = "./jd-client.log" + [timeout] unit = "secs" value = 1 diff --git a/roles/jd-client/config-examples/jdc-config-local-example.toml b/roles/jd-client/config-examples/jdc-config-local-example.toml index c8c77ad014..238dd94a42 100644 --- a/roles/jd-client/config-examples/jdc-config-local-example.toml +++ b/roles/jd-client/config-examples/jdc-config-local-example.toml @@ -38,6 +38,11 @@ coinbase_outputs = [ #{ output_script_type = "P2TR", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, ] +# Enable this option to set a predefined log file path. +# When enabled, logs will always be written to this file. +# The CLI option --log-file (or -f) will override this setting if provided. +# log_file = "./jd-client.log" + [timeout] unit = "secs" value = 1 diff --git a/roles/jd-client/src/args.rs b/roles/jd-client/src/args.rs index e59a29cf0e..04b4cf1c73 100644 --- a/roles/jd-client/src/args.rs +++ b/roles/jd-client/src/args.rs @@ -22,6 +22,12 @@ pub struct Args { default_value = "jdc-config.toml" )] pub config_path: PathBuf, + #[arg( + short = 'f', + long = "log-file", + help = "Path to the log file. If not set, logs will only be written to stdout." + )] + pub log_file: Option, } /// Process CLI args and load configuration. @@ -41,6 +47,9 @@ pub fn process_cli_args<'a>() -> ProxyResult<'a, JobDeclaratorClientConfig> { .build()?; // Deserialize settings into JobDeclaratorClientConfig - let config = settings.try_deserialize::()?; + let mut config = settings.try_deserialize::()?; + + config.set_log_file(args.log_file); + Ok(config) } diff --git a/roles/jd-client/src/lib/config.rs b/roles/jd-client/src/lib/config.rs index 357becfc3c..f8e9356221 100644 --- a/roles/jd-client/src/lib/config.rs +++ b/roles/jd-client/src/lib/config.rs @@ -9,7 +9,11 @@ use config_helpers::CoinbaseOutput; use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; use serde::Deserialize; -use std::{net::SocketAddr, time::Duration}; +use std::{ + net::SocketAddr, + path::{Path, PathBuf}, + time::Duration, +}; use stratum_common::roles_logic_sv2::bitcoin::{Amount, TxOut}; /// Represents the configuration of a Job Declarator Client (JDC). @@ -55,6 +59,8 @@ pub struct JobDeclaratorClientConfig { coinbase_outputs: Vec, /// A signature string identifying this JDC instance. jdc_signature: String, + /// The path to the log file where JDC will write logs. + log_file: Option, } impl JobDeclaratorClientConfig { @@ -84,6 +90,7 @@ impl JobDeclaratorClientConfig { timeout, coinbase_outputs: protocol_config.coinbase_outputs, jdc_signature, + log_file: None, } } @@ -163,6 +170,15 @@ impl JobDeclaratorClientConfig { _ => Ok(result), } } + + pub fn log_file(&self) -> Option<&Path> { + self.log_file.as_deref() + } + pub fn set_log_file(&mut self, log_file: Option) { + if let Some(log_file) = log_file { + self.log_file = Some(log_file); + } + } } /// Represents pool specific encryption keys. diff --git a/roles/jd-client/src/main.rs b/roles/jd-client/src/main.rs index da2af17cac..d8e8907486 100644 --- a/roles/jd-client/src/main.rs +++ b/roles/jd-client/src/main.rs @@ -7,7 +7,7 @@ mod args; use args::process_cli_args; - +use config_helpers::logging::init_logging; use jd_client::JobDeclaratorClient; use tracing::error; @@ -61,8 +61,7 @@ use tracing::error; /// defined in `jd_client::JobDeclaratorClient`. Errors during startup are logged. #[tokio::main] async fn main() { - tracing_subscriber::fmt::init(); - let proxy_config = match process_cli_args() { + let jdc_config = match process_cli_args() { Ok(p) => p, Err(e) => { error!("Job Declarator Client Config error: {}", e); @@ -70,6 +69,8 @@ async fn main() { } }; - let jdc = JobDeclaratorClient::new(proxy_config); + init_logging(jdc_config.log_file()); + + let jdc = JobDeclaratorClient::new(jdc_config); jdc.start().await; } From 69fb38a8d29fbd9affaedc65b2878ee151079c71 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Thu, 3 Jul 2025 14:59:15 -0400 Subject: [PATCH 077/338] add '*.log' to '.gitignore' file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6720e2bde7..a9ea2b9204 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ cobertura.xml /test/integration-tests/template-provider **/template-provider stratum-message-generator +*.log \ No newline at end of file From e3f6399e6f389028a23d60f12559b321e8f264c9 Mon Sep 17 00:00:00 2001 From: Lucas Balieiro Date: Fri, 4 Jul 2025 12:42:00 -0400 Subject: [PATCH 078/338] update integration tests lock file --- test/integration-tests/Cargo.lock | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/integration-tests/Cargo.lock b/test/integration-tests/Cargo.lock index 89f4d21d47..b33456549b 100644 --- a/test/integration-tests/Cargo.lock +++ b/test/integration-tests/Cargo.lock @@ -575,6 +575,8 @@ dependencies = [ "miniscript", "roles_logic_sv2", "serde", + "tracing", + "tracing-subscriber", ] [[package]] @@ -1263,7 +1265,6 @@ dependencies = [ "stratum-common", "tokio", "tracing", - "tracing-subscriber", ] [[package]] @@ -1287,7 +1288,6 @@ dependencies = [ "stratum-common", "tokio", "tracing", - "tracing-subscriber", ] [[package]] @@ -1757,7 +1757,6 @@ dependencies = [ "stratum-common", "tokio", "tracing", - "tracing-subscriber", ] [[package]] @@ -2518,6 +2517,7 @@ dependencies = [ "buffer_sv2", "clap", "config", + "config-helpers", "error_handling", "futures", "key-utils", @@ -2531,7 +2531,6 @@ dependencies = [ "tokio", "tokio-util", "tracing", - "tracing-subscriber", ] [[package]] From 3956aa9a32f7b6807d6383871c53716cae2808ed Mon Sep 17 00:00:00 2001 From: GitGab19 Date: Wed, 9 Jul 2025 14:50:46 +0200 Subject: [PATCH 079/338] fix unnecessary version bumps done by mistake on https://github.com/stratum-mining/stratum/pull/1728 --- common/Cargo.lock | 4 ++-- protocols/v2/framing-sv2/Cargo.toml | 2 +- protocols/v2/subprotocols/common-messages/Cargo.toml | 2 +- roles/Cargo.lock | 4 ++-- test/integration-tests/Cargo.lock | 4 ++-- utils/Cargo.lock | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/common/Cargo.lock b/common/Cargo.lock index 8b9906f2ad..2b6acb5efc 100644 --- a/common/Cargo.lock +++ b/common/Cargo.lock @@ -301,7 +301,7 @@ dependencies = [ [[package]] name = "common_messages_sv2" -version = "5.1.0" +version = "5.0.0" dependencies = [ "binary_sv2", ] @@ -409,7 +409,7 @@ dependencies = [ [[package]] name = "framing_sv2" -version = "5.1.0" +version = "5.0.0" dependencies = [ "binary_sv2", "buffer_sv2", diff --git a/protocols/v2/framing-sv2/Cargo.toml b/protocols/v2/framing-sv2/Cargo.toml index 479b798401..e8617e0e22 100644 --- a/protocols/v2/framing-sv2/Cargo.toml +++ b/protocols/v2/framing-sv2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framing_sv2" -version = "5.1.0" +version = "5.0.0" authors = ["The Stratum V2 Developers"] edition = "2018" readme = "README.md" diff --git a/protocols/v2/subprotocols/common-messages/Cargo.toml b/protocols/v2/subprotocols/common-messages/Cargo.toml index 7fd13b3b7b..69a80b40ba 100644 --- a/protocols/v2/subprotocols/common-messages/Cargo.toml +++ b/protocols/v2/subprotocols/common-messages/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "common_messages_sv2" -version = "5.1.0" +version = "5.0.0" authors = ["The Stratum V2 Developers"] edition = "2018" readme = "README.md" diff --git a/roles/Cargo.lock b/roles/Cargo.lock index 00e33d82fe..204f8358cd 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -657,7 +657,7 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "common_messages_sv2" -version = "5.1.0" +version = "5.0.0" dependencies = [ "binary_sv2", ] @@ -978,7 +978,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "framing_sv2" -version = "5.1.0" +version = "5.0.0" dependencies = [ "binary_sv2", "buffer_sv2", diff --git a/test/integration-tests/Cargo.lock b/test/integration-tests/Cargo.lock index b33456549b..56b6582d7a 100644 --- a/test/integration-tests/Cargo.lock +++ b/test/integration-tests/Cargo.lock @@ -535,7 +535,7 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "common_messages_sv2" -version = "5.1.0" +version = "5.0.0" dependencies = [ "binary_sv2", ] @@ -835,7 +835,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "framing_sv2" -version = "5.1.0" +version = "5.0.0" dependencies = [ "binary_sv2", "buffer_sv2", diff --git a/utils/Cargo.lock b/utils/Cargo.lock index 42862c4f2e..419e2830c5 100644 --- a/utils/Cargo.lock +++ b/utils/Cargo.lock @@ -323,7 +323,7 @@ dependencies = [ [[package]] name = "common_messages_sv2" -version = "5.1.0" +version = "5.0.0" dependencies = [ "binary_sv2", ] @@ -511,7 +511,7 @@ dependencies = [ [[package]] name = "framing_sv2" -version = "5.1.0" +version = "5.0.0" dependencies = [ "binary_sv2", "buffer_sv2", From 50d96a03a09916a455134758509aceed87e6f567 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 5 Jul 2025 17:05:38 +0000 Subject: [PATCH 080/338] update miniscript dep to 12.3.4 There are a couple minor bugfixes in this patch release that we want to have for stratum. --- roles/Cargo.lock | 4 ++-- roles/roles-utils/config-helpers/Cargo.toml | 2 +- test/integration-tests/Cargo.lock | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/roles/Cargo.lock b/roles/Cargo.lock index 204f8358cd..f47ca40894 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -1637,9 +1637,9 @@ dependencies = [ [[package]] name = "miniscript" -version = "12.3.2" +version = "12.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0760e92feaf4ee26bd2e616f557de64712bf1e75f3b1b218dfb475c0a84c7943" +checksum = "a1eeb3bbebc87062b99fbb8c9067d30dab85469f4cf12248a2667777cc86b282" dependencies = [ "bech32", "bitcoin", diff --git a/roles/roles-utils/config-helpers/Cargo.toml b/roles/roles-utils/config-helpers/Cargo.toml index 27e057a160..37c2cf4708 100644 --- a/roles/roles-utils/config-helpers/Cargo.toml +++ b/roles/roles-utils/config-helpers/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] [dependencies] serde = { version = "1.0.89", features = ["derive","alloc"], default-features = false } -miniscript = { version = "12.3.2", default-features = false, features = [ "no-std" ] } +miniscript = { version = "12.3.4", default-features = false, features = [ "no-std" ] } roles_logic_sv2 = { path = "../../../protocols/v2/roles-logic-sv2" } tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing = { version = "0.1" } diff --git a/test/integration-tests/Cargo.lock b/test/integration-tests/Cargo.lock index 56b6582d7a..70dbaa8c9f 100644 --- a/test/integration-tests/Cargo.lock +++ b/test/integration-tests/Cargo.lock @@ -1442,9 +1442,9 @@ dependencies = [ [[package]] name = "miniscript" -version = "12.3.2" +version = "12.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0760e92feaf4ee26bd2e616f557de64712bf1e75f3b1b218dfb475c0a84c7943" +checksum = "a1eeb3bbebc87062b99fbb8c9067d30dab85469f4cf12248a2667777cc86b282" dependencies = [ "bech32", "bitcoin", From 82404073b2e72f3eaf9ed0d25cdcac5dc6bd3790 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 22 May 2025 17:05:00 +0000 Subject: [PATCH 081/338] coinbase_output: make internals private We already have a `CoinbaseOutput::new` method for directly constructing values. Use it rather than exposing the internals of the type. --- roles/jd-server/src/lib/config.rs | 18 ++++++++---------- .../src/coinbase_output/errors.rs | 9 +++++++++ .../config-helpers/src/coinbase_output/mod.rs | 4 ++-- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/roles/jd-server/src/lib/config.rs b/roles/jd-server/src/lib/config.rs index fef6bc7f86..93c5232464 100644 --- a/roles/jd-server/src/lib/config.rs +++ b/roles/jd-server/src/lib/config.rs @@ -223,11 +223,10 @@ mod tests { let config = load_config("config-examples/jds-config-hosted-example.toml"); let outputs = config.get_txout().expect("Failed to get coinbase output"); - let expected_output = CoinbaseOutput { - output_script_type: "P2WPKH".to_string(), - output_script_value: - "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), - }; + let expected_output = CoinbaseOutput::new( + "P2WPKH".to_string(), + "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), + ); let expected_script: ScriptBuf = expected_output.try_into().unwrap(); let expected_transaction_output = TxOut { value: Amount::from_sat(0), @@ -296,11 +295,10 @@ mod tests { fn get_txout_supported_output_script_types() { let config = load_config("config-examples/jds-config-hosted-example.toml"); let outputs = config.get_txout().expect("Failed to get coinbase output"); - let expected_output = CoinbaseOutput { - output_script_type: "P2WPKH".to_string(), - output_script_value: - "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), - }; + let expected_output = CoinbaseOutput::new( + "P2WPKH".to_string(), + "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), + ); let expected_script: ScriptBuf = expected_output.try_into().unwrap(); let expected_transaction_output = TxOut { value: Amount::from_sat(0), diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs b/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs index 19e9c6e0bd..976b750f24 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs @@ -9,6 +9,8 @@ pub enum Error { InvalidOutputScript, /// Unknown script type in config UnknownOutputScriptType, + /// Error from the `miniscript` crate. + Miniscript(miniscript::Error), } impl fmt::Display for Error { @@ -18,6 +20,13 @@ impl fmt::Display for Error { EmptyCoinbaseOutputs => write!(f, "Empty coinbase outputs in config"), UnknownOutputScriptType => write!(f, "Unknown script type in config"), InvalidOutputScript => write!(f, "Invalid output_script_value for your script type. It must be a valid public key/script"), + Miniscript(ref e) => write!(f, "Miniscript: {e}"), } } } + +impl From for Error { + fn from(e: miniscript::Error) -> Self { + Error::Miniscript(e) + } +} diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs index 2d0a0a4567..5f41d3843a 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs @@ -23,7 +23,7 @@ pub struct CoinbaseOutput { /// - `"P2WPKH"`: Pay-to-Witness-Public-Key-Hash /// - `"P2WSH"`: Pay-to-Witness-Script-Hash /// - `"P2TR"`: Pay-to-Taproot - pub output_script_type: String, + output_script_type: String, /// Value associated with the script, typically a public key or script hash. /// @@ -34,7 +34,7 @@ pub struct CoinbaseOutput { /// - For `"P2SH"`: A script hash. /// - For `"P2WSH"`: A witness script hash. /// - For `"P2TR"`: An x-only public key. - pub output_script_value: String, + output_script_value: String, } impl CoinbaseOutput { From 31d650176243d4b246c1edcf5fc7e3b09cd09dba Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 25 May 2025 20:05:25 +0000 Subject: [PATCH 082/338] roles/jd-server: refactor unit tests In the next commit we will lose the CoinbaseOutput::new function, which is used in several unit tests. We replace it with a new test helper function which substitutes strings into an actual configuration TOML, which is more flexible and is a better test. This removes one redundant test but adds a couple new ones. The tests no longer directly test the CoinbaseOutput::try_into method that yields a ScriptBuf; we will remove that in the next commits in favor of "you cannot construct a CoinbaseOutput that doesn't produce a ScriptBuf". --- roles/jd-server/src/lib/config.rs | 125 ++++++++++++++++-------------- 1 file changed, 65 insertions(+), 60 deletions(-) diff --git a/roles/jd-server/src/lib/config.rs b/roles/jd-server/src/lib/config.rs index 93c5232464..ad1f329167 100644 --- a/roles/jd-server/src/lib/config.rs +++ b/roles/jd-server/src/lib/config.rs @@ -185,13 +185,34 @@ impl CoreRpc { #[cfg(test)] mod tests { use super::super::JobDeclaratorServer; - use config_helpers::CoinbaseOutput; - use ext_config::{Config, File, FileFormat}; - use std::{convert::TryInto, path::PathBuf}; - use stratum_common::roles_logic_sv2::bitcoin::{Amount, ScriptBuf, TxOut}; + use ext_config::{Config, ConfigError, File, FileFormat}; + use std::path::PathBuf; + use stratum_common::roles_logic_sv2::bitcoin::{self, Amount, ScriptBuf, TxOut}; use crate::config::JobDeclaratorServerConfig; + const COINBASE_CONFIG_TEMPLATE: &'static str = r#" + full_template_mode_required = true + authority_public_key = "9auqWEzQDVyd2oe1JVGFLMLHZtCo2FFqZwtKA5gd9xbuEu7PH72" + authority_secret_key = "mkDLTBBRxdBv998612qipDYoTK3YUrqLe8uWw7gu3iXbSrn2n" + cert_validity_sec = 3600 + + coinbase_outputs = %COINBASE_OUTPUTS% + + listen_jd_address = "127.0.0.1:34264" + core_rpc_url = "http://127.0.0.1" + core_rpc_port = 48332 + core_rpc_user = "username" + core_rpc_pass = "password" + [mempool_update_interval] + unit = "secs" + value = 1 + "#; + const TEST_PK_HEX: &'static str = + "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075"; + const TEST_INVALID_PK_HEX: &'static str = + "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7ffffff"; + fn load_config(path: &str) -> JobDeclaratorServerConfig { let config_path = PathBuf::from(path); assert!( @@ -210,6 +231,16 @@ mod tests { settings.try_deserialize().expect("Failed to parse config") } + fn load_coinbase_config_str(path: &str) -> Result { + let s = COINBASE_CONFIG_TEMPLATE.replace("%COINBASE_OUTPUTS%", path); + let settings = Config::builder() + .add_source(File::from_str(&s, FileFormat::Toml)) + .build() + .expect("Failed to build config"); + + settings.try_deserialize() + } + #[tokio::test] async fn test_offline_rpc_url() { let mut config = load_config("config-examples/jds-config-hosted-example.toml"); @@ -220,14 +251,20 @@ mod tests { #[test] fn test_get_txout_non_empty() { - let config = load_config("config-examples/jds-config-hosted-example.toml"); + let pk = TEST_PK_HEX + .parse::() + .expect("Failed to parse public key"); + let config = load_coinbase_config_str(&format!( + "[ {{ output_script_type = \"P2WPKH\", output_script_value = \"{pk}\" }} ]" + )) + .expect("Failed to parse config"); let outputs = config.get_txout().expect("Failed to get coinbase output"); - let expected_output = CoinbaseOutput::new( - "P2WPKH".to_string(), - "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), - ); - let expected_script: ScriptBuf = expected_output.try_into().unwrap(); + let expected_script = ScriptBuf::from_hex(&format!( + "0014{}", + pk.wpubkey_hash().expect("compressed key") + )) + .expect("hex"); let expected_transaction_output = TxOut { value: Amount::from_sat(0), script_pubkey: expected_script, @@ -238,10 +275,9 @@ mod tests { #[test] fn test_get_txout_empty() { - let mut config = load_config("config-examples/jds-config-hosted-example.toml"); - config.set_coinbase_outputs(Vec::new()); + let config = load_coinbase_config_str("[]").expect("can parse config with empty list"); - let result = &config.get_txout(); + let result = config.get_txout(); assert!( matches!( result, @@ -252,22 +288,12 @@ mod tests { } #[test] - fn test_try_from_valid_input() { - let input = config_helpers::CoinbaseOutput::new( - "P2PKH".to_string(), - "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), - ); - let result: Result = input.try_into(); - assert!(result.is_ok()); - } - - #[test] - fn test_try_from_invalid_input() { - let input = config_helpers::CoinbaseOutput::new( - "INVALID".to_string(), - "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), - ); - let result: Result = input.try_into(); + fn test_get_txout_invalid_script_type() { + let config = load_coinbase_config_str(&format!( + "[ {{ output_script_type = \"INVALID\", output_script_value = \"{TEST_PK_HEX}\" }} ]" + )) + .expect("Failed to parse config"); + let result = config.get_txout(); assert!(matches!( result, Err(config_helpers::CoinbaseOutputError::UnknownOutputScriptType) @@ -275,36 +301,15 @@ mod tests { } #[test] - fn get_txout_invalid_output_script_type() { - let mut config = load_config("config-examples/jds-config-hosted-example.toml"); - config.set_coinbase_outputs(vec![config_helpers::CoinbaseOutput::new( - "INVALID".to_string(), - "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), - )]); - let outputs = config.get_txout(); - assert!( - matches!( - outputs, - Err(config_helpers::CoinbaseOutputError::UnknownOutputScriptType) - ), - "Expected an error for unknown output script type" - ); - } - - #[test] - fn get_txout_supported_output_script_types() { - let config = load_config("config-examples/jds-config-hosted-example.toml"); - let outputs = config.get_txout().expect("Failed to get coinbase output"); - let expected_output = CoinbaseOutput::new( - "P2WPKH".to_string(), - "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), - ); - let expected_script: ScriptBuf = expected_output.try_into().unwrap(); - let expected_transaction_output = TxOut { - value: Amount::from_sat(0), - script_pubkey: expected_script, - }; - - assert_eq!(outputs[0], expected_transaction_output); + fn test_get_txout_invalid_value() { + let config = load_coinbase_config_str(&format!( + "[ {{ output_script_type = \"P2WPKH\", output_script_value = \"{TEST_INVALID_PK_HEX}\" }} ]" + )) + .expect("Failed to parse config"); + let result = config.get_txout(); + assert!(matches!( + result, + Err(config_helpers::CoinbaseOutputError::InvalidOutputScript) + )); } } From 3bf8b318c0dbcb58198eeb754de0c662239c3d46 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 22 May 2025 17:36:13 +0000 Subject: [PATCH 083/338] roles/roles-utils/config-helpers: make invalid CoinbaseOutputs unconstructible A few tests in roles/jd-server/src/lib/config.rs change from "cannot use the config" to "cannot parse the config" which for the user will result in a much faster failure. Unfortunately there isn't a nice way to match on ConfigErrors that I found, so I asserted equality of the error messages. I hope that's okay, even though they make the tests a bit fragile. I retained the CoinbaseOutput::new function for now; it is used in the integration tests. But it is not used anywhere else and once we have a descriptor-based constructor we will eliminate it. --- roles/jd-client/src/lib/config.rs | 2 +- roles/jd-server/src/lib/config.rs | 28 ++--- roles/pool/src/lib/mining_pool/mod.rs | 2 +- roles/pool/src/lib/mod.rs | 29 ----- .../config-helpers/src/coinbase_output/mod.rs | 110 ++---------------- .../src/coinbase_output/serde_types.rs | 100 ++++++++++++++++ test/integration-tests/lib/mod.rs | 9 +- 7 files changed, 130 insertions(+), 150 deletions(-) create mode 100644 roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs diff --git a/roles/jd-client/src/lib/config.rs b/roles/jd-client/src/lib/config.rs index f8e9356221..697fd44dee 100644 --- a/roles/jd-client/src/lib/config.rs +++ b/roles/jd-client/src/lib/config.rs @@ -159,7 +159,7 @@ impl JobDeclaratorClientConfig { pub fn get_txout(&self) -> Result, config_helpers::CoinbaseOutputError> { let mut result = Vec::new(); for coinbase_output_pool in &self.coinbase_outputs { - let output_script = coinbase_output_pool.clone().try_into()?; + let output_script = coinbase_output_pool.script_pubkey().to_owned(); result.push(TxOut { value: Amount::from_sat(0), script_pubkey: output_script, diff --git a/roles/jd-server/src/lib/config.rs b/roles/jd-server/src/lib/config.rs index ad1f329167..a6bb955ca8 100644 --- a/roles/jd-server/src/lib/config.rs +++ b/roles/jd-server/src/lib/config.rs @@ -15,7 +15,6 @@ use config_helpers::CoinbaseOutput; use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; use serde::Deserialize; use std::{ - convert::TryInto, path::{Path, PathBuf}, time::Duration, }; @@ -138,7 +137,7 @@ impl JobDeclaratorServerConfig { pub fn get_txout(&self) -> Result, config_helpers::CoinbaseOutputError> { let mut result = Vec::new(); for coinbase_output_pool in &self.coinbase_outputs { - let output_script = coinbase_output_pool.clone().try_into()?; + let output_script = coinbase_output_pool.script_pubkey().to_owned(); result.push(TxOut { value: Amount::from_sat(0), script_pubkey: output_script, @@ -258,8 +257,8 @@ mod tests { "[ {{ output_script_type = \"P2WPKH\", output_script_value = \"{pk}\" }} ]" )) .expect("Failed to parse config"); - let outputs = config.get_txout().expect("Failed to get coinbase output"); + let outputs = config.get_txout().expect("Failed to get coinbase output"); let expected_script = ScriptBuf::from_hex(&format!( "0014{}", pk.wpubkey_hash().expect("compressed key") @@ -289,27 +288,22 @@ mod tests { #[test] fn test_get_txout_invalid_script_type() { - let config = load_coinbase_config_str(&format!( + let error = load_coinbase_config_str(&format!( "[ {{ output_script_type = \"INVALID\", output_script_value = \"{TEST_PK_HEX}\" }} ]" )) - .expect("Failed to parse config"); - let result = config.get_txout(); - assert!(matches!( - result, - Err(config_helpers::CoinbaseOutputError::UnknownOutputScriptType) - )); + .expect_err("Cannot parse config with bad script type"); + assert_eq!(error.to_string(), "Unknown script type in config",); } #[test] fn test_get_txout_invalid_value() { - let config = load_coinbase_config_str(&format!( + let error = load_coinbase_config_str(&format!( "[ {{ output_script_type = \"P2WPKH\", output_script_value = \"{TEST_INVALID_PK_HEX}\" }} ]" )) - .expect("Failed to parse config"); - let result = config.get_txout(); - assert!(matches!( - result, - Err(config_helpers::CoinbaseOutputError::InvalidOutputScript) - )); + .expect_err("Cannot parse config with bad pubkeys"); + assert_eq!( + error.to_string(), + "Invalid output_script_value for your script type. It must be a valid public key/script", + ); } } diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 90a5e119cd..5250a75c92 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -88,7 +88,7 @@ pub type EitherFrame = StandardEitherFrame; pub fn get_coinbase_output(config: &PoolConfig) -> Result, CoinbaseOutputError> { let mut result = Vec::new(); for coinbase_output_pool in config.coinbase_outputs() { - let output_script: ScriptBuf = coinbase_output_pool.clone().try_into()?; + let output_script: ScriptBuf = coinbase_output_pool.script_pubkey().to_owned(); result.push(TxOut { value: Amount::from_sat(0), script_pubkey: output_script, diff --git a/roles/pool/src/lib/mod.rs b/roles/pool/src/lib/mod.rs index b44f6640a4..81786f529d 100644 --- a/roles/pool/src/lib/mod.rs +++ b/roles/pool/src/lib/mod.rs @@ -210,35 +210,6 @@ mod tests { use super::*; use ext_config::{Config, File, FileFormat}; - #[tokio::test] - async fn pool_bad_coinbase_output() { - let invalid_coinbase_output = vec![config_helpers::CoinbaseOutput::new( - "P2PK".to_string(), - "wrong".to_string(), - )]; - let config_path = "config-examples/pool-config-hosted-tp-example.toml"; - let mut config: PoolConfig = match Config::builder() - .add_source(File::new(config_path, FileFormat::Toml)) - .build() - { - Ok(settings) => match settings.try_deserialize::() { - Ok(c) => c, - Err(e) => { - error!("Failed to deserialize config: {}", e); - return; - } - }, - Err(e) => { - error!("Failed to build config: {}", e); - return; - } - }; - config.set_coinbase_outputs(invalid_coinbase_output); - let pool = PoolSv2::new(config); - let result = pool.start().await; - assert!(result.is_err()); - } - #[tokio::test] async fn shutdown_pool() { let template_provider = integration_tests_sv2::start_template_provider(None); diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs index 5f41d3843a..6ac2a6be94 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs @@ -1,11 +1,9 @@ mod errors; +mod serde_types; use core::convert::TryFrom; -use miniscript::bitcoin::{ - secp256k1::{All, Secp256k1}, - PublicKey, ScriptBuf, ScriptHash, WScriptHash, XOnlyPublicKey, -}; +use miniscript::bitcoin::{Script, ScriptBuf}; pub use errors::Error; @@ -13,108 +11,22 @@ pub use errors::Error; /// /// Typically used for parsing coinbase outputs defined in SRI role configuration files. #[derive(Debug, serde::Deserialize, Clone)] +#[serde(try_from = "serde_types::SerdeCoinbaseOutput")] pub struct CoinbaseOutput { - /// Specifies type of the script used in the output. - /// - /// Supported values include: - /// - `"P2PK"`: Pay-to-Public-Key - /// - `"P2PKH"`: Pay-to-Public-Key-Hash - /// - `"P2SH"`: Pay-to-Script-Hash - /// - `"P2WPKH"`: Pay-to-Witness-Public-Key-Hash - /// - `"P2WSH"`: Pay-to-Witness-Script-Hash - /// - `"P2TR"`: Pay-to-Taproot - output_script_type: String, - - /// Value associated with the script, typically a public key or script hash. - /// - /// This field's interpretation depends on the `output_script_type`: - /// - For `"P2PK"`: The raw public key. - /// - For `"P2PKH"`: A public key hash. - /// - For `"P2WPKH"`: A witness public key hash. - /// - For `"P2SH"`: A script hash. - /// - For `"P2WSH"`: A witness script hash. - /// - For `"P2TR"`: An x-only public key. - output_script_value: String, + script_pubkey: ScriptBuf, } impl CoinbaseOutput { - /// Creates a new [`CoinbaseOutput`]. - pub fn new(output_script_type: String, output_script_value: String) -> Self { - Self { + /// Creates a new [`CoinbaseOutput`] from a script type and value. + pub fn new(output_script_type: String, output_script_value: String) -> Result { + Self::try_from(serde_types::SerdeCoinbaseOutput { output_script_type, output_script_value, - } + }) } -} - -impl TryFrom for ScriptBuf { - type Error = Error; - fn try_from(value: CoinbaseOutput) -> Result { - match value.output_script_type.as_str() { - "TEST" => { - let pub_key_hash = value - .output_script_value - .parse::() - .map_err(|_| Error::InvalidOutputScript)? - .pubkey_hash(); - Ok(ScriptBuf::new_p2pkh(&pub_key_hash)) - } - "P2PK" => { - let pub_key = value - .output_script_value - .parse::() - .map_err(|_| Error::InvalidOutputScript)?; - Ok(ScriptBuf::new_p2pk(&pub_key)) - } - "P2PKH" => { - let pub_key_hash = value - .output_script_value - .parse::() - .map_err(|_| Error::InvalidOutputScript)? - .pubkey_hash(); - Ok(ScriptBuf::new_p2pkh(&pub_key_hash)) - } - "P2WPKH" => { - let w_pub_key_hash = value - .output_script_value - .parse::() - .map_err(|_| Error::InvalidOutputScript)? - .wpubkey_hash() - .unwrap(); - Ok(ScriptBuf::new_p2wpkh(&w_pub_key_hash)) - } - "P2SH" => { - let script_hashed = value - .output_script_value - .parse::() - .map_err(|_| Error::InvalidOutputScript)?; - Ok(ScriptBuf::new_p2sh(&script_hashed)) - } - "P2WSH" => { - let w_script_hashed = value - .output_script_value - .parse::() - .map_err(|_| Error::InvalidOutputScript)?; - Ok(ScriptBuf::new_p2wsh(&w_script_hashed)) - } - "P2TR" => { - // From the bip - // - // Conceptually, every Taproot output corresponds to a combination of - // a single public key condition (the internal key), - // and zero or more general conditions encoded in scripts organized in a tree. - let pub_key = value - .output_script_value - .parse::() - .map_err(|_| Error::InvalidOutputScript)?; - Ok(ScriptBuf::new_p2tr::( - &Secp256k1::::new(), - pub_key, - None, - )) - } - _ => Err(Error::UnknownOutputScriptType), - } + /// The `scriptPubKey` associated with the coinbase output + pub fn script_pubkey(&self) -> &Script { + &self.script_pubkey } } diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs b/roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs new file mode 100644 index 0000000000..07f6e96ff1 --- /dev/null +++ b/roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs @@ -0,0 +1,100 @@ +use core::convert::TryFrom; +use miniscript::bitcoin::{ + secp256k1::{All, Secp256k1}, + PublicKey, ScriptBuf, ScriptHash, WScriptHash, XOnlyPublicKey, +}; + +use super::Error; + +#[derive(serde::Deserialize)] +pub(super) struct SerdeCoinbaseOutput { + /// Specifies type of the script used in the output. + /// + /// Supported values include: + /// - `"P2PK"`: Pay-to-Public-Key + /// - `"P2PKH"`: Pay-to-Public-Key-Hash + /// - `"P2SH"`: Pay-to-Script-Hash + /// - `"P2WPKH"`: Pay-to-Witness-Public-Key-Hash + /// - `"P2WSH"`: Pay-to-Witness-Script-Hash + /// - `"P2TR"`: Pay-to-Taproot + pub(super) output_script_type: String, + + /// Value associated with the script, typically a public key or script hash. + /// + /// This field's interpretation depends on the `output_script_type`: + /// - For `"P2PK"`: The raw public key. + /// - For `"P2PKH"`: A public key hash. + /// - For `"P2WPKH"`: A witness public key hash. + /// - For `"P2SH"`: A script hash. + /// - For `"P2WSH"`: A witness script hash. + /// - For `"P2TR"`: An x-only public key. + pub(super) output_script_value: String, +} + +impl TryFrom for super::CoinbaseOutput { + type Error = super::Error; + fn try_from(value: SerdeCoinbaseOutput) -> Result { + let script_pubkey = match value.output_script_type.as_str() { + "TEST" => { + let pub_key_hash = value + .output_script_value + .parse::() + .map_err(|_| Error::InvalidOutputScript)? + .pubkey_hash(); + ScriptBuf::new_p2pkh(&pub_key_hash) + } + "P2PK" => { + let pub_key = value + .output_script_value + .parse::() + .map_err(|_| Error::InvalidOutputScript)?; + ScriptBuf::new_p2pk(&pub_key) + } + "P2PKH" => { + let pub_key_hash = value + .output_script_value + .parse::() + .map_err(|_| Error::InvalidOutputScript)? + .pubkey_hash(); + ScriptBuf::new_p2pkh(&pub_key_hash) + } + "P2WPKH" => { + let w_pub_key_hash = value + .output_script_value + .parse::() + .map_err(|_| Error::InvalidOutputScript)? + .wpubkey_hash() + .unwrap(); + ScriptBuf::new_p2wpkh(&w_pub_key_hash) + } + "P2SH" => { + let script_hashed = value + .output_script_value + .parse::() + .map_err(|_| Error::InvalidOutputScript)?; + ScriptBuf::new_p2sh(&script_hashed) + } + "P2WSH" => { + let w_script_hashed = value + .output_script_value + .parse::() + .map_err(|_| Error::InvalidOutputScript)?; + ScriptBuf::new_p2wsh(&w_script_hashed) + } + "P2TR" => { + // From the bip + // + // Conceptually, every Taproot output corresponds to a combination of + // a single public key condition (the internal key), + // and zero or more general conditions encoded in scripts organized in a tree. + let pub_key = value + .output_script_value + .parse::() + .map_err(|_| Error::InvalidOutputScript)?; + ScriptBuf::new_p2tr::(&Secp256k1::::new(), pub_key, None) + } + _ => return Err(Error::UnknownOutputScriptType), + }; + Ok(Self { script_pubkey }) + } +} diff --git a/test/integration-tests/lib/mod.rs b/test/integration-tests/lib/mod.rs index 13fd69463d..a5c516a0c0 100644 --- a/test/integration-tests/lib/mod.rs +++ b/test/integration-tests/lib/mod.rs @@ -70,7 +70,8 @@ pub async fn start_pool(template_provider_address: Option) -> (PoolS let coinbase_outputs = vec![CoinbaseOutput::new( "P2WPKH".to_string(), "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), - )]; + ) + .unwrap()]; let pool_signature = "Stratum V2 SRI Pool".to_string(); let tp_address = if let Some(tp_add) = template_provider_address { tp_add.to_string() @@ -129,7 +130,8 @@ pub fn start_jdc( let coinbase_outputs = vec![CoinbaseOutput::new( "P2WPKH".to_string(), "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), - )]; + ) + .unwrap()]; let authority_pubkey = Secp256k1PublicKey::try_from( "9auqWEzQDVyd2oe1JVGFLMLHZtCo2FFqZwtKA5gd9xbuEu7PH72".to_string(), ) @@ -183,7 +185,8 @@ pub fn start_jds(tp_rpc_connection: &ConnectParams) -> (JobDeclaratorServer, Soc let coinbase_outputs = vec![CoinbaseOutput::new( "P2WPKH".to_string(), "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), - )]; + ) + .unwrap()]; if let Ok(Some(CookieValues { user, password })) = tp_rpc_connection.get_cookie_values() { let ip = tp_rpc_connection.rpc_socket.ip().to_string(); let url = jd_server::Uri::builder() From 902504ced3451d7ad1d273834b25af70b032bc76 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 22 May 2025 17:05:00 +0000 Subject: [PATCH 084/338] roles/roles-utils/config-helpers: add support for ordinary Descriptor types This does not include raw() or addr(); we will add those in the next commit, and then update the example configs and tests. --- .../config-helpers/src/coinbase_output/mod.rs | 15 ++++++- .../src/coinbase_output/serde_types.rs | 40 +++++++++++++++++-- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs index 6ac2a6be94..e1b56b215a 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs @@ -3,7 +3,10 @@ mod serde_types; use core::convert::TryFrom; -use miniscript::bitcoin::{Script, ScriptBuf}; +use miniscript::{ + bitcoin::{Script, ScriptBuf}, + DefiniteDescriptorKey, Descriptor, +}; pub use errors::Error; @@ -19,12 +22,20 @@ pub struct CoinbaseOutput { impl CoinbaseOutput { /// Creates a new [`CoinbaseOutput`] from a script type and value. pub fn new(output_script_type: String, output_script_value: String) -> Result { - Self::try_from(serde_types::SerdeCoinbaseOutput { + Self::try_from(serde_types::LegacyCoinbaseOutput { output_script_type, output_script_value, }) } + /// Creates a new [`CoinbaseOutput`] from a descriptor string. + pub fn from_descriptor(s: &str) -> Result { + let desc = s.parse::>()?; + Ok(Self { + script_pubkey: desc.script_pubkey(), + }) + } + /// The `scriptPubKey` associated with the coinbase output pub fn script_pubkey(&self) -> &Script { &self.script_pubkey diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs b/roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs index 07f6e96ff1..fa329ee51a 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs @@ -7,14 +7,15 @@ use miniscript::bitcoin::{ use super::Error; #[derive(serde::Deserialize)] -pub(super) struct SerdeCoinbaseOutput { +pub(super) struct LegacyCoinbaseOutput { /// Specifies type of the script used in the output. /// /// Supported values include: /// - `"P2PK"`: Pay-to-Public-Key /// - `"P2PKH"`: Pay-to-Public-Key-Hash /// - `"P2SH"`: Pay-to-Script-Hash - /// - `"P2WPKH"`: Pay-to-Witness-Public-Key-Hash + /// - `"P2WPKH"`: Pay-to-Witness-Public-Key-Hash:w + /// - `"P2WSH"`: Pay-to-Witness-Script-Hash /// - `"P2TR"`: Pay-to-Taproot pub(super) output_script_type: String, @@ -31,9 +32,9 @@ pub(super) struct SerdeCoinbaseOutput { pub(super) output_script_value: String, } -impl TryFrom for super::CoinbaseOutput { +impl TryFrom for super::CoinbaseOutput { type Error = super::Error; - fn try_from(value: SerdeCoinbaseOutput) -> Result { + fn try_from(value: LegacyCoinbaseOutput) -> Result { let script_pubkey = match value.output_script_type.as_str() { "TEST" => { let pub_key_hash = value @@ -98,3 +99,34 @@ impl TryFrom for super::CoinbaseOutput { Ok(Self { script_pubkey }) } } + +/// A coinbase output script as it appears in a configuration file. +/// +/// Private to avoid exposing the enum constructors. +#[derive(serde::Deserialize)] +#[serde(untagged)] // decode as whichever variant makes sense for the input +enum SerdeCoinbaseOutputInner { + Legacy(LegacyCoinbaseOutput), + Descriptor(String), +} + +/// A structure representing a coinbase output script as it appears in a +/// configuration file. +/// +/// Can only be constructed via serde, and supports no operations except conversion +/// to a [`super::CoinbaseOutput`] via [`TryFrom`]. +#[derive(serde::Deserialize)] +#[serde(transparent)] +pub struct SerdeCoinbaseOutput { + inner: SerdeCoinbaseOutputInner, +} + +impl TryFrom for super::CoinbaseOutput { + type Error = super::Error; + fn try_from(value: SerdeCoinbaseOutput) -> Result { + match value.inner { + SerdeCoinbaseOutputInner::Legacy(legacy) => Self::try_from(legacy), + SerdeCoinbaseOutputInner::Descriptor(ref s) => Self::from_descriptor(s), + } + } +} From 855770f0a06e3ea53784c704cb5bc735940e6691 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 26 May 2025 00:32:55 +0000 Subject: [PATCH 085/338] roles/roles-utils/config-helpers: drop CoinbaseOutput::new function This was only used for tests, and represents an old-style constructor. Replace it with CoinbaseOutput::from_descriptor which is more general. The original method was only used in the integration tests which are easy to change. --- .../config-helpers/src/coinbase_output/mod.rs | 10 ---------- test/integration-tests/lib/mod.rs | 15 ++++++--------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs index e1b56b215a..f77a1271ff 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs @@ -1,8 +1,6 @@ mod errors; mod serde_types; -use core::convert::TryFrom; - use miniscript::{ bitcoin::{Script, ScriptBuf}, DefiniteDescriptorKey, Descriptor, @@ -20,14 +18,6 @@ pub struct CoinbaseOutput { } impl CoinbaseOutput { - /// Creates a new [`CoinbaseOutput`] from a script type and value. - pub fn new(output_script_type: String, output_script_value: String) -> Result { - Self::try_from(serde_types::LegacyCoinbaseOutput { - output_script_type, - output_script_value, - }) - } - /// Creates a new [`CoinbaseOutput`] from a descriptor string. pub fn from_descriptor(s: &str) -> Result { let desc = s.parse::>()?; diff --git a/test/integration-tests/lib/mod.rs b/test/integration-tests/lib/mod.rs index a5c516a0c0..61b0aa9e77 100644 --- a/test/integration-tests/lib/mod.rs +++ b/test/integration-tests/lib/mod.rs @@ -67,9 +67,8 @@ pub async fn start_pool(template_provider_address: Option) -> (PoolS ) .expect("failed"); let cert_validity_sec = 3600; - let coinbase_outputs = vec![CoinbaseOutput::new( - "P2WPKH".to_string(), - "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), + let coinbase_outputs = vec![CoinbaseOutput::from_descriptor( + "wpkh(036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075)", ) .unwrap()]; let pool_signature = "Stratum V2 SRI Pool".to_string(); @@ -127,9 +126,8 @@ pub fn start_jdc( "mkDLTBBRxdBv998612qipDYoTK3YUrqLe8uWw7gu3iXbSrn2n".to_string(), ) .unwrap(); - let coinbase_outputs = vec![CoinbaseOutput::new( - "P2WPKH".to_string(), - "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), + let coinbase_outputs = vec![CoinbaseOutput::from_descriptor( + "wpkh(036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075)", ) .unwrap()]; let authority_pubkey = Secp256k1PublicKey::try_from( @@ -182,9 +180,8 @@ pub fn start_jds(tp_rpc_connection: &ConnectParams) -> (JobDeclaratorServer, Soc .unwrap(); let listen_jd_address = get_available_address(); let cert_validity_sec = 3600; - let coinbase_outputs = vec![CoinbaseOutput::new( - "P2WPKH".to_string(), - "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(), + let coinbase_outputs = vec![CoinbaseOutput::from_descriptor( + "wpkh(036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075)", ) .unwrap()]; if let Ok(Some(CookieValues { user, password })) = tp_rpc_connection.get_cookie_values() { From efdf4310ee5de120ea3a745f91ba3f6f90a75e36 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 22 May 2025 19:31:40 +0000 Subject: [PATCH 086/338] roles/roles-utils/config-helpers: add support for addr descriptors in coinbase output This also adds an accessor `ok_for_mainnet` to `CoinbaseOutput` but it does not use it anywhere. In a later PR we should hook this up and refuse to start on mainnet if we are given a testnet address. It is currently out of scope because it appears we have no way of determining whether we are on mainnet or testnet (other than suggesting it in error messages in case the user has an empty mempool). --- .../src/coinbase_output/errors.rs | 20 ++++++ .../config-helpers/src/coinbase_output/mod.rs | 71 +++++++++++++++++-- .../src/coinbase_output/serde_types.rs | 6 +- 3 files changed, 90 insertions(+), 7 deletions(-) diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs b/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs index 976b750f24..ee6106edf0 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs @@ -1,8 +1,18 @@ use core::fmt; +use miniscript::bitcoin::address; + /// Error enum #[derive(Debug)] pub enum Error { + /// Error parsing a Bitcoin address + Address(address::ParseError), + // TODO rust-miniscript 13 will have functions to do these checks for us so we don't + // need to pollute our own error enum with this fiddly stuff + /// addr() descriptor did not have exactly 1 child + AddrDescriptorNChildren(usize), + /// addr() descriptor child did not have 0 children + AddrDescriptorGrandchild, /// Empty coinbase outputs in config EmptyCoinbaseOutputs, /// Invalid `output_script_value` for script type. It must be a valid public key/script @@ -17,6 +27,10 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Error::*; match self { + Address(ref e) => write!(f, "Bitcoin address: {e}"), + AddrDescriptorNChildren(0) => write!(f, "Found addr() descriptor with no address"), + AddrDescriptorNChildren(n) => write!(f, "Found addr() descriptor with {n} children; must be exactly one valid address"), + AddrDescriptorGrandchild => write!(f, "Found descriptor of the form addr(X(y)); X must be a valid address and have no subexpression"), EmptyCoinbaseOutputs => write!(f, "Empty coinbase outputs in config"), UnknownOutputScriptType => write!(f, "Unknown script type in config"), InvalidOutputScript => write!(f, "Invalid output_script_value for your script type. It must be a valid public key/script"), @@ -25,6 +39,12 @@ impl fmt::Display for Error { } } +impl From for Error { + fn from(e: address::ParseError) -> Self { + Error::Address(e) + } +} + impl From for Error { fn from(e: miniscript::Error) -> Self { Error::Miniscript(e) diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs index f77a1271ff..5ef1669f18 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs @@ -2,7 +2,7 @@ mod errors; mod serde_types; use miniscript::{ - bitcoin::{Script, ScriptBuf}, + bitcoin::{address::NetworkUnchecked, Address, Network, Script, ScriptBuf}, DefiniteDescriptorKey, Descriptor, }; @@ -15,15 +15,74 @@ pub use errors::Error; #[serde(try_from = "serde_types::SerdeCoinbaseOutput")] pub struct CoinbaseOutput { script_pubkey: ScriptBuf, + ok_for_mainnet: bool, } impl CoinbaseOutput { /// Creates a new [`CoinbaseOutput`] from a descriptor string. - pub fn from_descriptor(s: &str) -> Result { - let desc = s.parse::>()?; - Ok(Self { - script_pubkey: desc.script_pubkey(), - }) + pub fn from_descriptor(mut s: &str) -> Result { + // Taproot descriptors cannot be parsed with `expression::Tree::from_str` and + // need special handling. So we special-case them early and just pass to + // rust-miniscript. In Miniscript 13 we will not need to do this. + if s.starts_with("tr") { + let desc = s.parse::>()?; + return Ok(Self { + script_pubkey: desc.script_pubkey(), + // Descriptors don't have a way to specify a network, so we assume + // they are OK to be used on mainnet. + ok_for_mainnet: true, + }); + } + + // Manually verify the checksum. FIXME in Miniscript 13 we will not need + // to do this, since `expression::Tree::from_str` will do the checksum + // validation for us. (And yield a much less horrible error type.) + if let Some((desc_str, checksum_str)) = s.rsplit_once('#') { + let expected_sum = miniscript::descriptor::checksum::desc_checksum(desc_str)?; + if checksum_str != expected_sum { + return Err(miniscript::Error::BadDescriptor(format!( + "Invalid checksum '{checksum_str}', expected '{expected_sum}'" + )) + .into()); + } + s = desc_str; + } + + let tree = miniscript::expression::Tree::from_str(s)?; + match tree.name { + "addr" => { + if tree.args.len() != 1 { + return Err(Error::AddrDescriptorNChildren(tree.args.len())); + } + if !tree.args[0].args.is_empty() { + return Err(Error::AddrDescriptorGrandchild); + } + + let addr = tree.args[0].name.parse::>()?; + Ok(Self { + script_pubkey: addr.assume_checked_ref().script_pubkey(), + ok_for_mainnet: addr.is_valid_for_network(Network::Bitcoin), + }) + } + _ => { + let desc = s.parse::>()?; + Ok(Self { + script_pubkey: desc.script_pubkey(), + // Descriptors don't have a way to specify a network, so we assume + // they are OK to be used on mainnet. + ok_for_mainnet: true, + }) + } + } + } + + /// Whether this coinbase output is okay for use on mainnet. + /// + /// This is a "best effort" check and currently only returns false if the user + /// provides an addr() descriptor in which they specified a testnet or regtest + /// address. + pub fn ok_for_mainnet(&self) -> bool { + self.ok_for_mainnet } /// The `scriptPubKey` associated with the coinbase output diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs b/roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs index fa329ee51a..0e10b74af9 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/serde_types.rs @@ -96,7 +96,11 @@ impl TryFrom for super::CoinbaseOutput { } _ => return Err(Error::UnknownOutputScriptType), }; - Ok(Self { script_pubkey }) + Ok(Self { + script_pubkey, + // legacy encoding gives no way to specify testnet or mainnet + ok_for_mainnet: true, + }) } } From 78c0e01b1bbfe1615d5ac9c510ea0c1e3688d7f3 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 22 May 2025 19:38:25 +0000 Subject: [PATCH 087/338] roles/role-utils/config-helpers: add support for raw descriptors in coinbase output --- .../src/coinbase_output/errors.rs | 20 ++++++++++++++-- .../config-helpers/src/coinbase_output/mod.rs | 23 ++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs b/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs index ee6106edf0..727c124e21 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs @@ -1,6 +1,6 @@ use core::fmt; -use miniscript::bitcoin::address; +use miniscript::bitcoin::{address, hex}; /// Error enum #[derive(Debug)] @@ -11,8 +11,14 @@ pub enum Error { // need to pollute our own error enum with this fiddly stuff /// addr() descriptor did not have exactly 1 child AddrDescriptorNChildren(usize), - /// addr() descriptor child did not have 0 children + /// raw() descriptor child did not have 0 children AddrDescriptorGrandchild, + /// raw() descriptor did not have exactly 1 child + RawDescriptorNChildren(usize), + /// addr() descriptor child did not have 0 children + RawDescriptorGrandchild, + /// Error parsing a raw descriptor as hex. + Hex(hex::HexToBytesError), /// Empty coinbase outputs in config EmptyCoinbaseOutputs, /// Invalid `output_script_value` for script type. It must be a valid public key/script @@ -31,6 +37,10 @@ impl fmt::Display for Error { AddrDescriptorNChildren(0) => write!(f, "Found addr() descriptor with no address"), AddrDescriptorNChildren(n) => write!(f, "Found addr() descriptor with {n} children; must be exactly one valid address"), AddrDescriptorGrandchild => write!(f, "Found descriptor of the form addr(X(y)); X must be a valid address and have no subexpression"), + RawDescriptorNChildren(0) => write!(f, "Found raw() descriptor with no hex-encoded script"), + RawDescriptorNChildren(n) => write!(f, "Found raw() descriptor with {n} children; must be exactly one hex-encoded script"), + RawDescriptorGrandchild => write!(f, "Found descriptor of the form raw(X(y)); X must be a hex-encoded script and have no subexpression"), + Hex(ref e) => write!(f, "Decoding hex-formatted script: {e}"), EmptyCoinbaseOutputs => write!(f, "Empty coinbase outputs in config"), UnknownOutputScriptType => write!(f, "Unknown script type in config"), InvalidOutputScript => write!(f, "Invalid output_script_value for your script type. It must be a valid public key/script"), @@ -45,6 +55,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: hex::HexToBytesError) -> Self { + Error::Hex(e) + } +} + impl From for Error { fn from(e: miniscript::Error) -> Self { Error::Miniscript(e) diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs index 5ef1669f18..f76e3ea0b7 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs @@ -2,7 +2,7 @@ mod errors; mod serde_types; use miniscript::{ - bitcoin::{address::NetworkUnchecked, Address, Network, Script, ScriptBuf}, + bitcoin::{address::NetworkUnchecked, hex::FromHex as _, Address, Network, Script, ScriptBuf}, DefiniteDescriptorKey, Descriptor, }; @@ -51,6 +51,9 @@ impl CoinbaseOutput { let tree = miniscript::expression::Tree::from_str(s)?; match tree.name { "addr" => { + // In rust-miniscript 13 these can be replaced with a call to + // TreeIterItem::verify_toplevel which will these checks for us + // in a uniform way. if tree.args.len() != 1 { return Err(Error::AddrDescriptorNChildren(tree.args.len())); } @@ -64,6 +67,24 @@ impl CoinbaseOutput { ok_for_mainnet: addr.is_valid_for_network(Network::Bitcoin), }) } + "raw" => { + // In rust-miniscript 13 these can be replaced with a call to + // TreeIterItem::verify_toplevel which will these checks for us + // in a uniform way. + if tree.args.len() != 1 { + return Err(Error::RawDescriptorNChildren(tree.args.len())); + } + if !tree.args[0].args.is_empty() { + return Err(Error::RawDescriptorGrandchild); + } + + let bytes = Vec::::from_hex(tree.args[0].name)?; + Ok(Self { + script_pubkey: ScriptBuf::from(bytes), + // Users of hex scriptpubkeys are on their own. + ok_for_mainnet: true, + }) + } _ => { let desc = s.parse::>()?; Ok(Self { From 95045324227173dcc8105af36fc8b9378aee12c2 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 29 Jun 2025 22:26:15 +0000 Subject: [PATCH 088/338] roles: enforce "at least one coinbase output is present" at config parsing time Saves us a bunch of run-time asserts/unwraps/expects. The existing example configuration files say "Right now only one output is supported, so comment all the ones you don't need !". But this condition is not actually enforced anywhere. It's just that when push comes to shove, we only actually use the first entry of the vector. So maybe this commit should enforce that exactly one entry is present? Right now it only enforces that *at least* one entry is present, which matches the existing behavior. The configuration structs all have `new` constructors which I believe are only public so that they can be used in the integration tests. Therefore I simply made them panic if you try to use them to construct a config object with a bad number of coinbase outputs. --- roles/jd-client/src/lib/config.rs | 28 +++++---- roles/jd-client/src/lib/mod.rs | 4 +- roles/jd-server/src/lib/config.rs | 57 +++++++++++-------- roles/jd-server/src/lib/job_declarator/mod.rs | 12 +--- roles/pool/src/lib/config.rs | 9 +++ roles/pool/src/lib/mining_pool/mod.rs | 27 ++++----- roles/pool/src/lib/mod.rs | 2 +- .../src/coinbase_output/errors.rs | 3 - roles/roles-utils/config-helpers/src/lib.rs | 49 +++++++++++++++- 9 files changed, 122 insertions(+), 69 deletions(-) diff --git a/roles/jd-client/src/lib/config.rs b/roles/jd-client/src/lib/config.rs index 697fd44dee..74ae323a76 100644 --- a/roles/jd-client/src/lib/config.rs +++ b/roles/jd-client/src/lib/config.rs @@ -56,6 +56,7 @@ pub struct JobDeclaratorClientConfig { timeout: Duration, /// A list of coinbase outputs to be included in the block templates. /// This is only used during solo-mining. + #[serde(deserialize_with = "config_helpers::deserialize_vec_exactly_1")] coinbase_outputs: Vec, /// A signature string identifying this JDC instance. jdc_signature: String, @@ -65,6 +66,10 @@ pub struct JobDeclaratorClientConfig { impl JobDeclaratorClientConfig { /// Creates a new instance of [`JobDeclaratorClientConfig`]. + /// + /// # Panics + /// + /// Panics if `protocol_config.coinbase_outputs` is empty. #[allow(clippy::too_many_arguments)] pub fn new( listening_address: SocketAddr, @@ -76,6 +81,10 @@ impl JobDeclaratorClientConfig { timeout: Duration, jdc_signature: String, ) -> Self { + assert!( + !protocol_config.coinbase_outputs.is_empty(), + "set of coinbase outputs must be nonempty" + ); Self { listening_address, max_supported_version: protocol_config.max_supported_version, @@ -156,19 +165,14 @@ impl JobDeclaratorClientConfig { &self.jdc_signature } - pub fn get_txout(&self) -> Result, config_helpers::CoinbaseOutputError> { - let mut result = Vec::new(); - for coinbase_output_pool in &self.coinbase_outputs { - let output_script = coinbase_output_pool.script_pubkey().to_owned(); - result.push(TxOut { + pub fn get_txout(&self) -> Vec { + self.coinbase_outputs + .iter() + .map(|out| TxOut { value: Amount::from_sat(0), - script_pubkey: output_script, - }); - } - match result.is_empty() { - true => Err(config_helpers::CoinbaseOutputError::EmptyCoinbaseOutputs), - _ => Ok(result), - } + script_pubkey: out.script_pubkey().to_owned(), + }) + .collect() } pub fn log_file(&self) -> Option<&Path> { diff --git a/roles/jd-client/src/lib/mod.rs b/roles/jd-client/src/lib/mod.rs index 81a44e49fb..d9003d8eb0 100644 --- a/roles/jd-client/src/lib/mod.rs +++ b/roles/jd-client/src/lib/mod.rs @@ -233,7 +233,7 @@ impl JobDeclaratorClient { task_collector: Arc>>, shutdown: Arc, ) { - let miner_tx_out = config.get_txout().expect("Failed to get txout"); + let miner_tx_out = config.get_txout(); // Spawn the downstream listener task. In solo mode, `upstream` and `jd` are `None`. let downstream_handle = tokio::spawn(downstream::listen_for_downstream_mining( @@ -245,7 +245,7 @@ impl JobDeclaratorClient { config.cert_validity_sec(), task_collector.clone(), tx_status.clone(), - miner_tx_out.clone(), + miner_tx_out, None, config.clone(), shutdown, diff --git a/roles/jd-server/src/lib/config.rs b/roles/jd-server/src/lib/config.rs index a6bb955ca8..c0226c3fd9 100644 --- a/roles/jd-server/src/lib/config.rs +++ b/roles/jd-server/src/lib/config.rs @@ -28,6 +28,7 @@ pub struct JobDeclaratorServerConfig { authority_public_key: Secp256k1PublicKey, authority_secret_key: Secp256k1SecretKey, cert_validity_sec: u64, + #[serde(deserialize_with = "config_helpers::deserialize_vec_exactly_1")] coinbase_outputs: Vec, core_rpc_url: String, core_rpc_port: u16, @@ -40,6 +41,10 @@ pub struct JobDeclaratorServerConfig { impl JobDeclaratorServerConfig { /// Creates a new instance of [`JobDeclaratorServerConfig`]. + /// + /// # Panics + /// + /// Panics if `coinbase_outputs` is empty. pub fn new( listen_jd_address: String, authority_public_key: Secp256k1PublicKey, @@ -49,6 +54,10 @@ impl JobDeclaratorServerConfig { core_rpc: CoreRpc, mempool_update_interval: Duration, ) -> Self { + assert!( + !coinbase_outputs.is_empty(), + "cannot have empty coinbase outputs array" + ); Self { full_template_mode_required: true, listen_jd_address, @@ -134,19 +143,14 @@ impl JobDeclaratorServerConfig { self.coinbase_outputs = outputs; } - pub fn get_txout(&self) -> Result, config_helpers::CoinbaseOutputError> { - let mut result = Vec::new(); - for coinbase_output_pool in &self.coinbase_outputs { - let output_script = coinbase_output_pool.script_pubkey().to_owned(); - result.push(TxOut { + pub fn get_txout(&self) -> Vec { + self.coinbase_outputs + .iter() + .map(|out| TxOut { value: Amount::from_sat(0), - script_pubkey: output_script, - }); - } - match result.is_empty() { - true => Err(config_helpers::CoinbaseOutputError::EmptyCoinbaseOutputs), - _ => Ok(result), - } + script_pubkey: out.script_pubkey().to_owned(), + }) + .collect() } pub fn log_file(&self) -> Option<&Path> { self.log_file.as_deref() @@ -258,7 +262,7 @@ mod tests { )) .expect("Failed to parse config"); - let outputs = config.get_txout().expect("Failed to get coinbase output"); + let outputs = config.get_txout(); let expected_script = ScriptBuf::from_hex(&format!( "0014{}", pk.wpubkey_hash().expect("compressed key") @@ -274,36 +278,41 @@ mod tests { #[test] fn test_get_txout_empty() { - let config = load_coinbase_config_str("[]").expect("can parse config with empty list"); - - let result = config.get_txout(); - assert!( - matches!( - result, - Err(config_helpers::CoinbaseOutputError::EmptyCoinbaseOutputs) - ), - "Expected an error for empty coinbase outputs" + let error = + load_coinbase_config_str("[]").expect_err("cannot parse config with empty list"); + assert_eq!( + error.to_string(), + "invalid length 0, expected a list with exactly one coinbase output, or a single descriptor string", ); } #[test] fn test_get_txout_invalid_script_type() { + // This error message was introduced in https://github.com/stratum-mining/stratum/pull/1720 + // as part of a change to allow Vec or non-Vec coinbase output lists to be parsed. We + // have https://github.com/stratum-mining/stratum/issues/1793 to track improving it. let error = load_coinbase_config_str(&format!( "[ {{ output_script_type = \"INVALID\", output_script_value = \"{TEST_PK_HEX}\" }} ]" )) .expect_err("Cannot parse config with bad script type"); - assert_eq!(error.to_string(), "Unknown script type in config",); + assert_eq!( + error.to_string(), + "could not parse descriptor string (or old-style list format)", + ); } #[test] fn test_get_txout_invalid_value() { + // This error message was introduced in https://github.com/stratum-mining/stratum/pull/1720 + // as part of a change to allow Vec or non-Vec coinbase output lists to be parsed. We + // have https://github.com/stratum-mining/stratum/issues/1793 to track improving it. let error = load_coinbase_config_str(&format!( "[ {{ output_script_type = \"P2WPKH\", output_script_value = \"{TEST_INVALID_PK_HEX}\" }} ]" )) .expect_err("Cannot parse config with bad pubkeys"); assert_eq!( error.to_string(), - "Invalid output_script_value for your script type. It must be a valid public key/script", + "could not parse descriptor string (or old-style list format)", ); } } diff --git a/roles/jd-server/src/lib/job_declarator/mod.rs b/roles/jd-server/src/lib/job_declarator/mod.rs index ec9d61cad9..a317f625d7 100644 --- a/roles/jd-server/src/lib/job_declarator/mod.rs +++ b/roles/jd-server/src/lib/job_declarator/mod.rs @@ -31,10 +31,7 @@ use stratum_common::{ network_helpers_sv2::noise_connection::Connection, roles_logic_sv2::{ self, - bitcoin::{ - consensus::{encode::serialize, Encodable}, - Block, Transaction, Txid, - }, + bitcoin::{consensus::encode::serialize, Block, Transaction, Txid}, codec_sv2::{ binary_sv2, binary_sv2::{B0255, U256}, @@ -121,7 +118,6 @@ impl JobDeclaratorDownstream { mempool: Arc>, sender_add_txs_to_mempool: Sender, ) -> Self { - let mut coinbase_output = vec![]; // TODO: use next variables let token_to_job_map = HashMap::with_hasher(BuildNoHashHasher::default()); let tokens = Id::new(); @@ -129,11 +125,7 @@ impl JobDeclaratorDownstream { known_transactions: vec![], unknown_transactions: vec![], }; - config - .get_txout() - .expect("Invalid coinbase output in config") - .consensus_encode(&mut coinbase_output) - .expect("Invalid coinbase output in config"); + let coinbase_output = serialize(&config.get_txout()); Self { full_template_mode_required, diff --git a/roles/pool/src/lib/config.rs b/roles/pool/src/lib/config.rs index c8a1b9dd44..9b2e7a40f1 100644 --- a/roles/pool/src/lib/config.rs +++ b/roles/pool/src/lib/config.rs @@ -22,6 +22,7 @@ pub struct PoolConfig { authority_public_key: Secp256k1PublicKey, authority_secret_key: Secp256k1SecretKey, cert_validity_sec: u64, + #[serde(deserialize_with = "config_helpers::deserialize_vec_exactly_1")] coinbase_outputs: Vec, pool_signature: String, shares_per_minute: f32, @@ -31,6 +32,10 @@ pub struct PoolConfig { impl PoolConfig { /// Creates a new instance of the [`PoolConfig`]. + /// + /// # Panics + /// + /// Panics if `coinbase_outputs` is empty. pub fn new( pool_connection: ConnectionConfig, template_provider: TemplateProviderConfig, @@ -39,6 +44,10 @@ impl PoolConfig { shares_per_minute: f32, share_batch_size: usize, ) -> Self { + assert!( + !coinbase_outputs.is_empty(), + "set of coinbase outputs must be nonempty" + ); Self { listen_address: pool_connection.listen_address, tp_address: template_provider.address, diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 5250a75c92..62bc019935 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -26,7 +26,6 @@ use super::{ status, }; use async_channel::{Receiver, Sender}; -use config_helpers::CoinbaseOutputError; use error_handling::handle_result; use key_utils::SignatureService; use nohash_hasher::BuildNoHashHasher; @@ -42,7 +41,7 @@ use stratum_common::{ network_helpers_sv2::noise_connection::Connection, roles_logic_sv2::{ self, - bitcoin::{Amount, ScriptBuf, TxOut}, + bitcoin::{Amount, TxOut}, channels::server::{ extended::ExtendedChannel, group::GroupChannel, standard::StandardChannel, }, @@ -85,19 +84,15 @@ pub type EitherFrame = StandardEitherFrame; /// It iterates through the configured outputs, attempts to convert them into the /// internal `CoinbaseOutput_` representation and then into `bitcoin::ScriptBuf`. /// Sets the value to 0 sats as per SV2 pool requirements (actual value determined later) -pub fn get_coinbase_output(config: &PoolConfig) -> Result, CoinbaseOutputError> { - let mut result = Vec::new(); - for coinbase_output_pool in config.coinbase_outputs() { - let output_script: ScriptBuf = coinbase_output_pool.script_pubkey().to_owned(); - result.push(TxOut { +pub fn get_coinbase_output(config: &PoolConfig) -> Vec { + config + .coinbase_outputs() + .iter() + .map(|out| TxOut { value: Amount::from_sat(0), - script_pubkey: output_script, - }); - } - match result.is_empty() { - true => Err(CoinbaseOutputError::EmptyCoinbaseOutputs), - _ => Ok(result), - } + script_pubkey: out.script_pubkey().to_owned(), + }) + .collect() } /// Represents a single connection to a downstream miner. @@ -1026,7 +1021,7 @@ impl Pool { config, recv_stop_signal, shares_per_minute, - pool_coinbase_outputs.expect("Invalid coinbase output in config"), + pool_coinbase_outputs, ) .await { @@ -1310,7 +1305,7 @@ mod test { let _coinbase_tx_value_remaining: u64 = 625000000; let _coinbase_tx_outputs_count = 0; let coinbase_tx_locktime = 0; - let coinbase_tx_outputs: Vec = super::get_coinbase_output(&config).unwrap(); + let coinbase_tx_outputs: Vec = super::get_coinbase_output(&config); // extranonce len set to max_extranonce_size in `ChannelFactory::new_extended_channel()` let extranonce_len = 32; diff --git a/roles/pool/src/lib/mod.rs b/roles/pool/src/lib/mod.rs index 81786f529d..e2562930f6 100644 --- a/roles/pool/src/lib/mod.rs +++ b/roles/pool/src/lib/mod.rs @@ -71,7 +71,7 @@ impl PoolSv2 { let (s_message_recv_signal, r_message_recv_signal) = bounded(10); // Prepare coinbase output information required by TemplateRx. - let coinbase_output_result = get_coinbase_output(&config)?; + let coinbase_output_result = get_coinbase_output(&config); let coinbase_output_len = coinbase_output_result .iter() .map(|output| output.size() as u32) diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs b/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs index 727c124e21..e3c1492b30 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/errors.rs @@ -19,8 +19,6 @@ pub enum Error { RawDescriptorGrandchild, /// Error parsing a raw descriptor as hex. Hex(hex::HexToBytesError), - /// Empty coinbase outputs in config - EmptyCoinbaseOutputs, /// Invalid `output_script_value` for script type. It must be a valid public key/script InvalidOutputScript, /// Unknown script type in config @@ -41,7 +39,6 @@ impl fmt::Display for Error { RawDescriptorNChildren(n) => write!(f, "Found raw() descriptor with {n} children; must be exactly one hex-encoded script"), RawDescriptorGrandchild => write!(f, "Found descriptor of the form raw(X(y)); X must be a hex-encoded script and have no subexpression"), Hex(ref e) => write!(f, "Decoding hex-formatted script: {e}"), - EmptyCoinbaseOutputs => write!(f, "Empty coinbase outputs in config"), UnknownOutputScriptType => write!(f, "Unknown script type in config"), InvalidOutputScript => write!(f, "Invalid output_script_value for your script type. It must be a valid public key/script"), Miniscript(ref e) => write!(f, "Miniscript: {e}"), diff --git a/roles/roles-utils/config-helpers/src/lib.rs b/roles/roles-utils/config-helpers/src/lib.rs index 79c7dc6e5a..9909a9d54f 100644 --- a/roles/roles-utils/config-helpers/src/lib.rs +++ b/roles/roles-utils/config-helpers/src/lib.rs @@ -1,7 +1,54 @@ +use serde::{Deserialize, Deserializer}; + mod coinbase_output; pub use coinbase_output::{CoinbaseOutput, Error as CoinbaseOutputError}; +pub mod logging; + mod toml; pub use toml::duration_from_toml; -pub mod logging; +/// Deserialize an object, allowing it to be encoded either directly or as +/// a singleton vector. +/// +/// Returns a singleton vector in either case. +/// +/// This function was created as part of https://github.com/stratum-mining/stratum/pull/1720 +/// as a first step in transitioning the `coinbase_outputs` field of various configuration +/// files away from a vector toward a single object. As part of a larger refactoring, it +/// should be changed to return a single [`CoinbaseOutput`] directly. +pub fn deserialize_vec_exactly_1<'de, D>(d: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + use serde::de::Error as _; + + // Serde will attempt `Single` first, then `Vec` if that fails. + #[derive(Deserialize)] + #[serde(untagged)] + enum Helper { + Single(CoinbaseOutput), + Vec(Vec), + } + + match Helper::deserialize(d) { + Err(_) => { + // The errors yielded by serde are meaningless to the user and + // expose the name of the private `Helper` type. Override them + // with something equally useless but less confusing. + Err(D::Error::custom( + "could not parse descriptor string (or old-style list format)", + )) + } + Ok(Helper::Single(ret)) => Ok(vec![ret]), + Ok(Helper::Vec(ret)) => { + if ret.len() != 1 { + return Err(D::Error::invalid_length( + ret.len(), + &"a list with exactly one coinbase output, or a single descriptor string", + )); + } + Ok(ret) + } + } +} From 28b46df43a0908ff277fe9fa9610a33f321d9a62 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 29 Jun 2025 22:33:20 +0000 Subject: [PATCH 089/338] roles/roles-utils/config-helpers: add unit tests for coinbase parsing --- .../config-helpers/src/coinbase_output/mod.rs | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) diff --git a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs index f76e3ea0b7..a4f72b9ba5 100644 --- a/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs +++ b/roles/roles-utils/config-helpers/src/coinbase_output/mod.rs @@ -111,3 +111,263 @@ impl CoinbaseOutput { &self.script_pubkey } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fixed_vector_addr() { + // Valid + assert_eq!( + CoinbaseOutput::from_descriptor("addr(1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2)#wdnlkpe8") + .unwrap() + .script_pubkey() + .to_hex_string(), + "76a91477bff20c60e522dfaa3350c39b030a5d004e839a88ac", + ); + assert_eq!( + CoinbaseOutput::from_descriptor("addr(3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy)#rsjl0crt") + .unwrap() + .script_pubkey() + .to_hex_string(), + "a914b472a266d0bd89c13706a4132ccfb16f7c3b9fcb87", + ); + assert_eq!( + CoinbaseOutput::from_descriptor( + "addr(bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4)#uyjndxcw" + ) + .unwrap() + .script_pubkey() + .to_hex_string(), + "0014751e76e8199196d454941c45d1b3a323f1433bd6", + ); + assert_eq!( + CoinbaseOutput::from_descriptor( + "addr(bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3)#8kzm8txf" + ) + .unwrap() + .script_pubkey() + .to_hex_string(), + "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + ); + // no checksum is ok + assert_eq!( + CoinbaseOutput::from_descriptor("addr(1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2)") + .unwrap() + .script_pubkey() + .to_hex_string(), + "76a91477bff20c60e522dfaa3350c39b030a5d004e839a88ac", + ); + assert_eq!( + CoinbaseOutput::from_descriptor("addr(1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2,)") + .unwrap_err() + .to_string(), + "Found addr() descriptor with 2 children; must be exactly one valid address", + ); + + // Invalid + // But empty checksum is not (in Miniscript 13 these error messages will be cleaner) + assert_eq!( + CoinbaseOutput::from_descriptor("addr(1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2)#") + .unwrap_err() + .to_string(), + "Miniscript: Invalid descriptor: Invalid checksum '', expected 'wdnlkpe8'", + ); + assert_eq!( + CoinbaseOutput::from_descriptor("addr(1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2)#wdnlkpe7") + .unwrap_err() + .to_string(), + "Miniscript: Invalid descriptor: Invalid checksum 'wdnlkpe7', expected 'wdnlkpe8'", + ); + // Bad base58ck checksum even though the descriptor checksum is OK. Note that rust-bitcoin + // 0.32 interprets bad bech32 checksums as "base58 errors" because it doessn't know + // what encoding an invalid string is supposed to have. See https://github.com/rust-bitcoin/rust-bitcoin/issues/3044 + assert_eq!( + CoinbaseOutput::from_descriptor("addr(1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN3)#5v55uzec") + .unwrap_err() + .to_string(), + "Bitcoin address: base58 error", + ); + assert_eq!( + CoinbaseOutput::from_descriptor( + "addr(bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t3)#wfr7lfxf" + ) + .unwrap_err() + .to_string(), + "Bitcoin address: base58 error", + ); + // Flagrantly bad stuff -- should probably PR these upstream to rust-miniscript. + assert_eq!( + CoinbaseOutput::from_descriptor("addr()") + .unwrap_err() + .to_string(), + "Bitcoin address: base58 error", + ); + assert_eq!( + CoinbaseOutput::from_descriptor("addr(It's a mad mad world!?! 🙃)") + .unwrap_err() + .to_string(), + "Miniscript: unprintable character 0xf0", + ); + // This error is just wrong lol. Fixed in Miniscript 13. + assert_eq!( + CoinbaseOutput::from_descriptor("addr(It's a mad mad world!?! 🙃)#abcdefg") + .unwrap_err() + .to_string(), + "Miniscript: Invalid descriptor: Invalid character in checksum: '🙃'", + ); + assert_eq!( + CoinbaseOutput::from_descriptor("addr(It's a mad mad world!?!)#hmeprl29") + .unwrap_err() + .to_string(), + "Bitcoin address: base58 error", + ); + assert_eq!( + CoinbaseOutput::from_descriptor("addr(It's a mad mad world!?!)#🙃🙃🙃🙃🙃🙃") + .unwrap_err() + .to_string(), + "Miniscript: Invalid descriptor: Invalid checksum '🙃🙃🙃🙃🙃🙃', expected 'hmeprl29'", + ); + } + + #[test] + fn fixed_vector_combo() { + // We do not support combo descriptors. Nobody should. + assert_eq!( + CoinbaseOutput::from_descriptor( + "combo(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)" + ) + .unwrap_err() + .to_string(), + "Miniscript: unexpected «combo(1 args) while parsing Miniscript»" + ); + } + + #[test] + fn fixed_vector_musig() { + // We do not support musig descriptors. One day. + assert_eq!( + CoinbaseOutput::from_descriptor("musig(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)").unwrap_err().to_string(), + "Miniscript: unexpected «musig(2 args) while parsing Miniscript»" + ); + assert_eq!( + CoinbaseOutput::from_descriptor("tr(musig(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))").unwrap_err().to_string(), + "Miniscript: expected )", + ); + } + + #[test] + fn fixed_vector_raw() { + // Empty raw descriptors are OK; correspond to the empty script. + assert_eq!( + CoinbaseOutput::from_descriptor("raw()") + .unwrap() + .script_pubkey() + .to_hex_string(), + "", + ); + assert_eq!( + CoinbaseOutput::from_descriptor("raw(deadbeef)") + .unwrap() + .script_pubkey() + .to_hex_string(), + "deadbeef", + ); + assert_eq!( + CoinbaseOutput::from_descriptor("raw(DEADBEEF)") + .unwrap() + .script_pubkey() + .to_hex_string(), + "deadbeef", + ); + // Should we allow this? We do, so I guess we should test it and make sure we don't stop.. + assert_eq!( + CoinbaseOutput::from_descriptor("raw(DEADbeef)") + .unwrap() + .script_pubkey() + .to_hex_string(), + "deadbeef", + ); + assert_eq!( + CoinbaseOutput::from_descriptor("raw(0)") + .unwrap_err() + .to_string(), + "Decoding hex-formatted script: odd length, failed to create bytes from hex", + ); + assert_eq!( + CoinbaseOutput::from_descriptor("raw(0,1)") + .unwrap_err() + .to_string(), + "Found raw() descriptor with 2 children; must be exactly one hex-encoded script", + ); + } + + #[test] + fn fixed_vector_miniscript() { + assert_eq!( + CoinbaseOutput::from_descriptor("sh(wsh(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)))#qpcmf2lu").unwrap().script_pubkey().to_hex_string(), + "a9141cb55de50b72c67709ab16307d69557e6bb1a98787", + ); + assert_eq!( + CoinbaseOutput::from_descriptor( + "tr(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)" + ) + .unwrap() + .script_pubkey() + .to_hex_string(), + "5120da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21", + ); + assert_eq!( + CoinbaseOutput::from_descriptor("tr(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,{pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),{multi_a(2,026a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4,0231ecbfac95d972f0b8f81ec6e01e9c621d91a4b48d5f9d12d7e95febe9f34d64),multi_a(2,026a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4,0231ecbfac95d972f0b8f81ec6e01e9c621d91a4b48d5f9d12d7e95febe9f34d64)}})") + .unwrap() + .script_pubkey() + .to_hex_string(), + "5120493bdae0d225af5cb88c4cb2a1e1e89e391153ba7699c91ebee2fd082ed1636c", + ); + } + + #[test] + fn fixed_vector_keys() { + // xpub + assert_eq!( + CoinbaseOutput::from_descriptor("pkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8)").unwrap().script_pubkey().to_hex_string(), + "76a9143442193e1bb70916e914552172cd4e2dbc9df81188ac", + ); + // xpub with non-hardened path + assert_eq!( + CoinbaseOutput::from_descriptor("pkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/1/2/3)").unwrap().script_pubkey().to_hex_string(), + "76a914f2d2e1401c88353c2298d1a928d4ed827ff46ff688ac", + ); + // xpub with hardened path (not allowed) + assert_eq!( + CoinbaseOutput::from_descriptor("pkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/1'/2/3)").unwrap_err().to_string(), + "Miniscript: unexpected «cannot parse multi-path keys, keys with a wildcard or keys with hardened derivation steps as a DerivedDescriptorKey»", + ); + // no wildcards allowed (at least for now; gmax thinks it would be cool if we would + // instantiate it with the blockheight or something, but need to work out UX) + assert_eq!( + CoinbaseOutput::from_descriptor("pkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/*)").unwrap_err().to_string(), + "Miniscript: unexpected «cannot parse multi-path keys, keys with a wildcard or keys with hardened derivation steps as a DerivedDescriptorKey»", + ); + // No multipath descriptors allowed; this is not a wallet with change + assert_eq!( + CoinbaseOutput::from_descriptor("pkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/<0;1>)").unwrap_err().to_string(), + "Miniscript: unexpected «cannot parse multi-path keys, keys with a wildcard or keys with hardened derivation steps as a DerivedDescriptorKey»", + ); + // Private keys are not allowed, or xprvs. + assert_eq!( + CoinbaseOutput::from_descriptor( + "pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)" + ) + .unwrap_err() + .to_string(), + "Miniscript: unexpected «Key too short (<66 char), doesn't match any format»", + ); + // This is a confusing error message which should be fixed in Miniscript 13. + assert_eq!( + CoinbaseOutput::from_descriptor("pkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi)").unwrap_err().to_string(), + "Miniscript: unexpected «Public keys must be 64/66/130 characters in size»", + ); + } +} From e4e28b4971f710e2502227a8d6ecdc31b8d6b7f3 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 7 Jul 2025 20:58:45 +0000 Subject: [PATCH 090/338] roles/*: allow using "coinbase_output" in place of "coinbase_outputs" in config --- roles/jd-client/src/lib/config.rs | 1 + roles/jd-server/src/lib/config.rs | 1 + roles/pool/src/lib/config.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/roles/jd-client/src/lib/config.rs b/roles/jd-client/src/lib/config.rs index 74ae323a76..4c5b57e24f 100644 --- a/roles/jd-client/src/lib/config.rs +++ b/roles/jd-client/src/lib/config.rs @@ -56,6 +56,7 @@ pub struct JobDeclaratorClientConfig { timeout: Duration, /// A list of coinbase outputs to be included in the block templates. /// This is only used during solo-mining. + #[serde(alias = "coinbase_output")] // only one is allowed, so don't make the user type the plural #[serde(deserialize_with = "config_helpers::deserialize_vec_exactly_1")] coinbase_outputs: Vec, /// A signature string identifying this JDC instance. diff --git a/roles/jd-server/src/lib/config.rs b/roles/jd-server/src/lib/config.rs index c0226c3fd9..14ab249afb 100644 --- a/roles/jd-server/src/lib/config.rs +++ b/roles/jd-server/src/lib/config.rs @@ -28,6 +28,7 @@ pub struct JobDeclaratorServerConfig { authority_public_key: Secp256k1PublicKey, authority_secret_key: Secp256k1SecretKey, cert_validity_sec: u64, + #[serde(alias = "coinbase_output")] // only one is allowed, so don't make the user type the plural #[serde(deserialize_with = "config_helpers::deserialize_vec_exactly_1")] coinbase_outputs: Vec, core_rpc_url: String, diff --git a/roles/pool/src/lib/config.rs b/roles/pool/src/lib/config.rs index 9b2e7a40f1..baad0acdc9 100644 --- a/roles/pool/src/lib/config.rs +++ b/roles/pool/src/lib/config.rs @@ -22,6 +22,7 @@ pub struct PoolConfig { authority_public_key: Secp256k1PublicKey, authority_secret_key: Secp256k1SecretKey, cert_validity_sec: u64, + #[serde(alias = "coinbase_output")] // only one is allowed, so don't make the user type the plural #[serde(deserialize_with = "config_helpers::deserialize_vec_exactly_1")] coinbase_outputs: Vec, pool_signature: String, From 73a87d8f268753847ab0b64e424fba60dca41198 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 5 Jul 2025 22:10:42 +0000 Subject: [PATCH 091/338] roles: update example configurations to use new syntax --- .../jdc-config-hosted-example.toml | 19 +++++++------------ .../jdc-config-local-example.toml | 19 +++++++------------ .../jds-config-hosted-example.toml | 16 +++++----------- .../jds-config-local-example.toml | 16 +++++----------- .../pool-config-hosted-tp-example.toml | 16 +++++----------- .../pool-config-local-tp-example.toml | 16 +++++----------- 6 files changed, 34 insertions(+), 68 deletions(-) diff --git a/roles/jd-client/config-examples/jdc-config-hosted-example.toml b/roles/jd-client/config-examples/jdc-config-hosted-example.toml index 473db3b27c..b9f5bc38ef 100644 --- a/roles/jd-client/config-examples/jdc-config-hosted-example.toml +++ b/roles/jd-client/config-examples/jdc-config-hosted-example.toml @@ -26,18 +26,13 @@ tp_authority_public_key = "9bwHCYnjhbHm4AS3pWg9MtAH83mzWohoJJJDELYBqZhDNqszDLc" jdc_signature = "JDC" # Solo Mining config -# List of coinbase outputs used to build the coinbase tx in case of Solo Mining (as last-resort solution of the pools fallback system) -# ! Put your Extended Public Key or Script as output_script_value ! -# ! Right now only one output is supported, so comment all the ones you don't need ! -# For P2PK, P2PKH, P2WPKH, P2TR a public key is needed. For P2SH and P2WSH, a redeem script is needed. -coinbase_outputs = [ - #{ output_script_type = "P2PK", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2PKH", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2SH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - #{ output_script_type = "P2WSH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - { output_script_type = "P2WPKH", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, - #{ output_script_type = "P2TR", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, -] +# Coinbase output used to build the coinbase tx in case of Solo Mining (as last-resort solution of the pools fallback system) +# +# Coinbase outputs are specified as descriptors. A full list of descriptors is available at +# https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki#appendix-b-index-of-script-expressions +# Although the `musig` descriptor is not yet supported and the legacy `combo` descriptor never +# will be. If you have an address, embed it in a descriptor like `addr(
    )`. +coinbase_output = "addr(tb1qa0sm0hxzj0x25rh8gw5xlzwlsfvvyz8u96w3p8)" # Enable this option to set a predefined log file path. # When enabled, logs will always be written to this file. diff --git a/roles/jd-client/config-examples/jdc-config-local-example.toml b/roles/jd-client/config-examples/jdc-config-local-example.toml index 238dd94a42..8594963439 100644 --- a/roles/jd-client/config-examples/jdc-config-local-example.toml +++ b/roles/jd-client/config-examples/jdc-config-local-example.toml @@ -25,18 +25,13 @@ tp_address = "127.0.0.1:8442" jdc_signature = "JDC" # Solo Mining config -# List of coinbase outputs used to build the coinbase tx in case of Solo Mining (as last-resort solution of the pools fallback system) -# ! Put your Extended Public Key or Script as output_script_value ! -# ! Right now only one output is supported, so comment all the ones you don't need ! -# For P2PK, P2PKH, P2WPKH, P2TR a public key is needed. For P2SH and P2WSH, a redeem script is needed. -coinbase_outputs = [ - #{ output_script_type = "P2PK", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2PKH", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2SH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - #{ output_script_type = "P2WSH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - { output_script_type = "P2WPKH", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, - #{ output_script_type = "P2TR", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, -] +# Coinbase output used to build the coinbase tx in case of Solo Mining (as last-resort solution of the pools fallback system) +# +# Coinbase outputs are specified as descriptors. A full list of descriptors is available at +# https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki#appendix-b-index-of-script-expressions +# Although the `musig` descriptor is not yet supported and the legacy `combo` descriptor never +# will be. If you have an address, embed it in a descriptor like `addr(
    )`. +coinbase_output = "addr(tb1qa0sm0hxzj0x25rh8gw5xlzwlsfvvyz8u96w3p8)" # Enable this option to set a predefined log file path. # When enabled, logs will always be written to this file. diff --git a/roles/jd-server/config-examples/jds-config-hosted-example.toml b/roles/jd-server/config-examples/jds-config-hosted-example.toml index d9bd59531b..301d75ba7c 100644 --- a/roles/jd-server/config-examples/jds-config-hosted-example.toml +++ b/roles/jd-server/config-examples/jds-config-hosted-example.toml @@ -6,17 +6,11 @@ authority_public_key = "9auqWEzQDVyd2oe1JVGFLMLHZtCo2FFqZwtKA5gd9xbuEu7PH72" authority_secret_key = "mkDLTBBRxdBv998612qipDYoTK3YUrqLe8uWw7gu3iXbSrn2n" cert_validity_sec = 3600 -# List of coinbase outputs used to build the coinbase tx -# ! Right now only one output is supported, so comment all the ones you don't need ! -# For P2PK, P2PKH, P2WPKH, P2TR a public key is needed. For P2SH and P2WSH, a redeem script is needed. -coinbase_outputs = [ - #{ output_script_type = "P2PK", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2PKH", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2SH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - #{ output_script_type = "P2WSH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - { output_script_type = "P2WPKH", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, - #{ output_script_type = "P2TR", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, -] +# Coinbase outputs are specified as descriptors. A full list of descriptors is available at +# https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki#appendix-b-index-of-script-expressions +# Although the `musig` descriptor is not yet supported and the legacy `combo` descriptor never +# will be. If you have an address, embed it in a descriptor like `addr(
    )`. +coinbase_output = "addr(tb1qa0sm0hxzj0x25rh8gw5xlzwlsfvvyz8u96w3p8)" # Enable this option to set a predefined log file path. # When enabled, logs will always be written to this file. diff --git a/roles/jd-server/config-examples/jds-config-local-example.toml b/roles/jd-server/config-examples/jds-config-local-example.toml index b23e0b4a48..f81db61de2 100644 --- a/roles/jd-server/config-examples/jds-config-local-example.toml +++ b/roles/jd-server/config-examples/jds-config-local-example.toml @@ -6,17 +6,11 @@ authority_public_key = "9auqWEzQDVyd2oe1JVGFLMLHZtCo2FFqZwtKA5gd9xbuEu7PH72" authority_secret_key = "mkDLTBBRxdBv998612qipDYoTK3YUrqLe8uWw7gu3iXbSrn2n" cert_validity_sec = 3600 -# List of coinbase outputs used to build the coinbase tx -# ! Right now only one output is supported, so comment all the ones you don't need ! -# For P2PK, P2PKH, P2WPKH, P2TR a public key is needed. For P2SH and P2WSH, a redeem script is needed. -coinbase_outputs = [ - #{ output_script_type = "P2PK", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2PKH", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2SH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - #{ output_script_type = "P2WSH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - { output_script_type = "P2WPKH", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, - #{ output_script_type = "P2TR", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, -] +# Coinbase outputs are specified as descriptors. A full list of descriptors is available at +# https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki#appendix-b-index-of-script-expressions +# Although the `musig` descriptor is not yet supported and the legacy `combo` descriptor never +# will be. If you have an address, embed it in a descriptor like `addr(
    )`. +coinbase_output = "addr(tb1qa0sm0hxzj0x25rh8gw5xlzwlsfvvyz8u96w3p8)" # Enable this option to set a predefined log file path. # When enabled, logs will always be written to this file. diff --git a/roles/pool/config-examples/pool-config-hosted-tp-example.toml b/roles/pool/config-examples/pool-config-hosted-tp-example.toml index 8e639a6894..2080e8fed4 100644 --- a/roles/pool/config-examples/pool-config-hosted-tp-example.toml +++ b/roles/pool/config-examples/pool-config-hosted-tp-example.toml @@ -5,17 +5,11 @@ cert_validity_sec = 3600 test_only_listen_adress_plain = "0.0.0.0:34250" listen_address = "0.0.0.0:34254" -# List of coinbase outputs used to build the coinbase tx -# ! Right now only one output is supported, so comment all the ones you don't need ! -# For P2PK, P2PKH, P2WPKH, P2TR a public key is needed. For P2SH and P2WSH, a redeem script is needed. -coinbase_outputs = [ - #{ output_script_type = "P2PK", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2PKH", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2SH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - #{ output_script_type = "P2WSH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - { output_script_type = "P2WPKH", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, - #{ output_script_type = "P2TR", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, -] +# Coinbase outputs are specified as descriptors. A full list of descriptors is available at +# https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki#appendix-b-index-of-script-expressions +# Although the `musig` descriptor is not yet supported and the legacy `combo` descriptor never +# will be. If you have an address, embed it in a descriptor like `addr(
    )`. +coinbase_output = "addr(tb1qa0sm0hxzj0x25rh8gw5xlzwlsfvvyz8u96w3p8)" # Pool signature (string to be included in coinbase tx) pool_signature = "Stratum V2 SRI Pool" diff --git a/roles/pool/config-examples/pool-config-local-tp-example.toml b/roles/pool/config-examples/pool-config-local-tp-example.toml index fe05f1d588..c61ae058cf 100644 --- a/roles/pool/config-examples/pool-config-local-tp-example.toml +++ b/roles/pool/config-examples/pool-config-local-tp-example.toml @@ -5,17 +5,11 @@ cert_validity_sec = 3600 test_only_listen_adress_plain = "0.0.0.0:34250" listen_address = "0.0.0.0:34254" -# List of coinbase outputs used to build the coinbase tx -# ! Right now only one output is supported, so comment all the ones you don't need ! -# For P2PK, P2PKH, P2WPKH, P2TR a public key is needed. For P2SH and P2WSH, a redeem script is needed. -coinbase_outputs = [ - #{ output_script_type = "P2PK", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2PKH", output_script_value = "0372c47307e5b75ce365daf835f226d246c5a7a92fe24395018d5552123354f086" }, - #{ output_script_type = "P2SH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - #{ output_script_type = "P2WSH", output_script_value = "00142ef89234bc95136eb9e6fee9d32722ebd8c1f0ab" }, - { output_script_type = "P2WPKH", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, - #{ output_script_type = "P2TR", output_script_value = "036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075" }, -] +# Coinbase outputs are specified as descriptors. A full list of descriptors is available at +# https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki#appendix-b-index-of-script-expressions +# Although the `musig` descriptor is not yet supported and the legacy `combo` descriptor never +# will be. If you have an address, embed it in a descriptor like `addr(
    )`. +coinbase_output = "addr(tb1qa0sm0hxzj0x25rh8gw5xlzwlsfvvyz8u96w3p8)" # Pool signature (string to be included in coinbase tx) pool_signature = "Stratum V2 SRI Pool" From 9b7d0f9644ea0fd0fcf440173fdc4f10fd1985bb Mon Sep 17 00:00:00 2001 From: GitGab19 Date: Wed, 9 Jul 2025 18:26:17 +0200 Subject: [PATCH 092/338] fix: add missing dependency versions for cargo publish Adds version specifications to local path dependencies in common, roles-logic-sv2, bip32-derivation, and rpc crates. --- common/Cargo.toml | 4 ++-- protocols/v2/roles-logic-sv2/Cargo.toml | 2 +- roles/roles-utils/rpc/Cargo.toml | 2 +- utils/bip32-key-derivation/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/Cargo.toml b/common/Cargo.toml index 74fff4abea..1058706cdc 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/stratum-mining/stratum" [dependencies] -roles_logic_sv2 = { path = "../protocols/v2/roles-logic-sv2" } -network_helpers_sv2 = { path = "../roles/roles-utils/network-helpers", features = ["with_buffer_pool"], optional = true } +roles_logic_sv2 = { path = "../protocols/v2/roles-logic-sv2", version = "3.0.0" } +network_helpers_sv2 = { path = "../roles/roles-utils/network-helpers", version = "4.0.0", features = ["with_buffer_pool"], optional = true } [features] with_network_helpers = ["dep:network_helpers_sv2"] diff --git a/protocols/v2/roles-logic-sv2/Cargo.toml b/protocols/v2/roles-logic-sv2/Cargo.toml index aa78357564..b49fd28d69 100644 --- a/protocols/v2/roles-logic-sv2/Cargo.toml +++ b/protocols/v2/roles-logic-sv2/Cargo.toml @@ -23,7 +23,7 @@ chacha20poly1305 = { version = "0.10.1"} nohash-hasher = "0.2.0" primitive-types = "0.13.1" hex = {package = "hex-conservative", version = "0.3.0"} -codec_sv2 = { path = "../../../protocols/v2/codec-sv2", features = ["noise_sv2", "with_buffer_pool"] } +codec_sv2 = { path = "../../../protocols/v2/codec-sv2", version = "^2.0.0", features = ["noise_sv2", "with_buffer_pool"] } [dev-dependencies] quickcheck = "1.0.3" diff --git a/roles/roles-utils/rpc/Cargo.toml b/roles/roles-utils/rpc/Cargo.toml index 9d6eb60512..6fd2cbe18e 100644 --- a/roles/roles-utils/rpc/Cargo.toml +++ b/roles/roles-utils/rpc/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -stratum-common = { path = "../../../common" } +stratum-common = { path = "../../../common", version = "3.0.0" } serde = { version = "1.0.89", features = ["derive", "alloc"], default-features = false } serde_json = { version = "1.0", default-features = false, features = ["alloc","raw_value"] } hex = "0.4.3" diff --git a/utils/bip32-key-derivation/Cargo.toml b/utils/bip32-key-derivation/Cargo.toml index 3faf95753a..fa5ae87e15 100644 --- a/utils/bip32-key-derivation/Cargo.toml +++ b/utils/bip32-key-derivation/Cargo.toml @@ -20,7 +20,7 @@ name = "bip32_derivation-bin" path = "src/main.rs" [dependencies] -stratum-common = { path = "../../common" } +stratum-common = { path = "../../common", version = "3.0.0" } [dev-dependencies] From c6a280d3d8feb66a2c2e1f6b112a2df1618eac00 Mon Sep 17 00:00:00 2001 From: GitGab19 Date: Wed, 9 Jul 2025 19:18:53 +0200 Subject: [PATCH 093/338] fix: publish dependencies before dependents in release workflow --- .github/workflows/release-libs.yaml | 61 +++++++++++++++++------------ 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/.github/workflows/release-libs.yaml b/.github/workflows/release-libs.yaml index 20f3e5df2e..225ca9fae5 100644 --- a/.github/workflows/release-libs.yaml +++ b/.github/workflows/release-libs.yaml @@ -29,38 +29,47 @@ jobs: - name: Login run: cargo login ${{ secrets.CRATES_IO_DEPLOY_KEY }} - - name: Publish crate common - run: | - ./scripts/release-libs.sh common - + # Base dependencies with no local dependencies - name: Publish crate buffer_sv2 run: | ./scripts/release-libs.sh utils/buffer - - name: Publish crate binary_sv2 derive_codec + - name: Publish crate error-handling run: | - ./scripts/release-libs.sh protocols/v2/binary-sv2/derive_codec + ./scripts/release-libs.sh utils/error-handling + + - name: Publish crate key-utils + run: | + ./scripts/release-libs.sh utils/key-utils + - name: Publish crate noise_sv2 + run: | + ./scripts/release-libs.sh protocols/v2/noise-sv2 + + # binary_sv2 (depends on buffer_sv2) - name: Publish crate binary_sv2 codec run: | ./scripts/release-libs.sh protocols/v2/binary-sv2/codec + - name: Publish crate binary_sv2 derive_codec + run: | + ./scripts/release-libs.sh protocols/v2/binary-sv2/derive_codec + - name: Publish crate binary_sv2 run: | ./scripts/release-libs.sh protocols/v2/binary-sv2 + # framing_sv2(depends on binary_sv2, buffer_sv2, noise_sv2) - name: Publish crate framing_sv2 run: | ./scripts/release-libs.sh protocols/v2/framing-sv2 - - name: Publish crate noise_sv2 - run: | - ./scripts/release-libs.sh protocols/v2/noise-sv2 - + # codec_sv2 (depends on framing_sv2, noise_sv2, binary_sv2, buffer_sv2, key-utils) - name: Publish crate codec_sv2 run: | ./scripts/release-libs.sh protocols/v2/codec-sv2 + # Subprotocols (depend on binary_sv2) - name: Publish crate common_messages run: | ./scripts/release-libs.sh protocols/v2/subprotocols/common-messages @@ -77,34 +86,36 @@ jobs: run: | ./scripts/release-libs.sh protocols/v2/subprotocols/template-distribution + # sv1_api (depends on binary_sv2) + - name: Publish crate v1 + run: | + ./scripts/release-libs.sh protocols/v1 + + # sv2_ffi (depends on codec_sv2, binary_sv2, common_messages, template_distribution) - name: Publish crate sv2_ffi run: | ./scripts/release-libs.sh protocols/v2/sv2-ffi + # Roles logic (depends on codec_sv2 and subprotocols) - name: Publish crate roles_logic_sv2 run: | ./scripts/release-libs.sh protocols/v2/roles-logic-sv2 - - name: Publish crate v1 - run: | - ./scripts/release-libs.sh protocols/v1 - - - name: Publish crate bip32-key-derivation - run: | - ./scripts/release-libs.sh utils/bip32-key-derivation - - - name: Publish crate error-handling + # Network helpers (depends on codec_sv2, sv1_api) + - name: Publish crate network_helpers_sv2 run: | - ./scripts/release-libs.sh utils/error-handling + ./scripts/release-libs.sh roles/roles-utils/network-helpers - - name: Publish crate key-utils + # Common (depends on roles_logic_sv2 and network_helpers_sv2) + - name: Publish crate common run: | - ./scripts/release-libs.sh utils/key-utils + ./scripts/release-libs.sh common - - name: Publish crate network_helpers_sv2 + # Utilities that depend on stratum-common + - name: Publish crate bip32-key-derivation run: | - ./scripts/release-libs.sh roles/roles-utils/network-helpers + ./scripts/release-libs.sh utils/bip32-key-derivation - name: Publish crate rpc_sv2 run: | - ./scripts/release-libs.sh roles/roles-utils/rpc + ./scripts/release-libs.sh roles/roles-utils/rpc \ No newline at end of file From 8481b3c152914e3574b28a626565be99bcd812b8 Mon Sep 17 00:00:00 2001 From: plebhash Date: Mon, 30 Jun 2025 21:07:11 -0300 Subject: [PATCH 094/338] isolate channels-sv2 into a standalone crate --- common/Cargo.lock | 15 ++ protocols/Cargo.toml | 1 + protocols/v2/channels-sv2/Cargo.toml | 24 +++ protocols/v2/channels-sv2/README.md | 11 ++ .../src}/chain_tip.rs | 2 +- .../src}/client/error.rs | 0 .../src}/client/extended.rs | 19 +- .../src}/client/group.rs | 2 +- .../src}/client/mod.rs | 0 .../src}/client/share_accounting.rs | 0 .../src}/client/standard.rs | 19 +- .../mod.rs => channels-sv2/src/lib.rs} | 3 + protocols/v2/channels-sv2/src/merkle_root.rs | 73 ++++++++ .../src}/server/error.rs | 2 +- .../src}/server/extended.rs | 24 ++- .../src}/server/group.rs | 6 +- .../src}/server/jobs/error.rs | 0 .../src}/server/jobs/extended.rs | 12 +- .../src}/server/jobs/factory.rs | 31 +++- .../src}/server/jobs/job_store.rs | 0 .../src}/server/jobs/mod.rs | 0 .../src}/server/jobs/standard.rs | 6 +- .../src}/server/mod.rs | 0 .../src}/server/share_accounting.rs | 0 .../src}/server/standard.rs | 20 +-- protocols/v2/channels-sv2/src/target.rs | 164 ++++++++++++++++++ protocols/v2/channels-sv2/src/template.rs | 23 +++ protocols/v2/roles-logic-sv2/Cargo.toml | 1 + protocols/v2/roles-logic-sv2/src/errors.rs | 8 +- protocols/v2/roles-logic-sv2/src/lib.rs | 2 +- roles/Cargo.lock | 15 ++ test/integration-tests/Cargo.lock | 15 ++ utils/Cargo.lock | 15 ++ 33 files changed, 438 insertions(+), 75 deletions(-) create mode 100644 protocols/v2/channels-sv2/Cargo.toml create mode 100644 protocols/v2/channels-sv2/README.md rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/chain_tip.rs (95%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/client/error.rs (100%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/client/extended.rs (98%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/client/group.rs (98%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/client/mod.rs (100%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/client/share_accounting.rs (100%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/client/standard.rs (98%) rename protocols/v2/{roles-logic-sv2/src/channels/mod.rs => channels-sv2/src/lib.rs} (52%) create mode 100644 protocols/v2/channels-sv2/src/merkle_root.rs rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/error.rs (91%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/extended.rs (99%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/group.rs (99%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/jobs/error.rs (100%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/jobs/extended.rs (96%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/jobs/factory.rs (97%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/jobs/job_store.rs (100%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/jobs/mod.rs (100%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/jobs/standard.rs (94%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/mod.rs (100%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/share_accounting.rs (100%) rename protocols/v2/{roles-logic-sv2/src/channels => channels-sv2/src}/server/standard.rs (98%) create mode 100644 protocols/v2/channels-sv2/src/target.rs create mode 100644 protocols/v2/channels-sv2/src/template.rs diff --git a/common/Cargo.lock b/common/Cargo.lock index 2b6acb5efc..54bfb63e32 100644 --- a/common/Cargo.lock +++ b/common/Cargo.lock @@ -276,6 +276,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "channels_sv2" +version = "0.1.0" +dependencies = [ + "binary_sv2", + "bitcoin", + "common_messages_sv2", + "job_declaration_sv2", + "mining_sv2", + "primitive-types", + "template_distribution_sv2", + "tracing", +] + [[package]] name = "cipher" version = "0.4.4" @@ -927,6 +941,7 @@ version = "3.0.0" dependencies = [ "bitcoin", "chacha20poly1305", + "channels_sv2", "codec_sv2", "common_messages_sv2", "hex-conservative 0.3.0", diff --git a/protocols/Cargo.toml b/protocols/Cargo.toml index 92b84df262..f99392ad1a 100644 --- a/protocols/Cargo.toml +++ b/protocols/Cargo.toml @@ -16,6 +16,7 @@ members = [ "v2/subprotocols/job-declaration", "v2/sv2-ffi", "v2/roles-logic-sv2", + "v2/channels-sv2", ] [profile.dev] diff --git a/protocols/v2/channels-sv2/Cargo.toml b/protocols/v2/channels-sv2/Cargo.toml new file mode 100644 index 0000000000..e077f82455 --- /dev/null +++ b/protocols/v2/channels-sv2/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "channels_sv2" +version = "0.1.0" +authors = ["The Stratum V2 Developers"] +edition = "2018" +readme = "README.md" +description = "Sv2 Channel Primitives" +documentation = "https://docs.rs/channels_sv2" +license = "MIT OR Apache-2.0" +repository = "https://github.com/stratum-mining/stratum" +homepage = "https://stratumprotocol.org" +keywords = ["stratum", "mining", "bitcoin", "protocol"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +binary_sv2 = { path = "../binary-sv2", version = "^3.0.0" } +common_messages_sv2 = { path = "../subprotocols/common-messages", version = "^5.0.0" } +mining_sv2 = { path = "../subprotocols/mining", version = "^4.0.0" } +template_distribution_sv2 = { path = "../subprotocols/template-distribution", version = "^3.0.0" } +job_declaration_sv2 = { path = "../subprotocols/job-declaration", version = "^4.0.0" } +tracing = { version = "0.1"} +bitcoin = { version = "0.32.5" } +primitive-types = "0.13.1" \ No newline at end of file diff --git a/protocols/v2/channels-sv2/README.md b/protocols/v2/channels-sv2/README.md new file mode 100644 index 0000000000..e48e2d8bcb --- /dev/null +++ b/protocols/v2/channels-sv2/README.md @@ -0,0 +1,11 @@ +# `channels_sv2` + +[![crates.io](https://img.shields.io/crates/v/channels_sv2.svg)](https://crates.io/crates/channels_sv2) +[![docs.rs](https://docs.rs/channels_sv2/badge.svg)](https://docs.rs/channels_sv2) +[![rustc+](https://img.shields.io/badge/rustc-1.75.0%2B-lightgrey.svg)](https://blog.rust-lang.org/2023/12/28/Rust-1.75.0.html) +[![license](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/stratum-mining/stratum/blob/main/LICENSE.md) +[![codecov](https://codecov.io/gh/stratum-mining/stratum/branch/main/graph/badge.svg?flag=channels_sv2-coverage)](https://codecov.io/gh/stratum-mining/stratum) + +`channels_sv2` provides primitives and abstractions for Stratum V2 (Sv2) Channels. + +This crate implements the core channel management functionality for both mining clients and servers, including standard, extended and group channels, and share accounting mechanisms. \ No newline at end of file diff --git a/protocols/v2/roles-logic-sv2/src/channels/chain_tip.rs b/protocols/v2/channels-sv2/src/chain_tip.rs similarity index 95% rename from protocols/v2/roles-logic-sv2/src/channels/chain_tip.rs rename to protocols/v2/channels-sv2/src/chain_tip.rs index 2371d4a6af..b8beca1ecd 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/chain_tip.rs +++ b/protocols/v2/channels-sv2/src/chain_tip.rs @@ -1,5 +1,5 @@ //! # Chain Tip -use codec_sv2::binary_sv2::U256; +use binary_sv2::U256; /// An abstraction over the chain tip, carrying information from `SetNewPrevHash` messages. /// diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/error.rs b/protocols/v2/channels-sv2/src/client/error.rs similarity index 100% rename from protocols/v2/roles-logic-sv2/src/channels/client/error.rs rename to protocols/v2/channels-sv2/src/client/error.rs diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs b/protocols/v2/channels-sv2/src/client/extended.rs similarity index 98% rename from protocols/v2/roles-logic-sv2/src/channels/client/extended.rs rename to protocols/v2/channels-sv2/src/client/extended.rs index ec824a1a93..1af2f2e03b 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/client/extended.rs +++ b/protocols/v2/channels-sv2/src/client/extended.rs @@ -1,21 +1,20 @@ //! Mining Client abstraction over the state of a Sv2 Extended Channel use crate::{ - channels::{ - chain_tip::ChainTip, - client::{ - error::ExtendedChannelError, - share_accounting::{ShareAccounting, ShareValidationError, ShareValidationResult}, - }, + chain_tip::ChainTip, + client::{ + error::ExtendedChannelError, + share_accounting::{ShareAccounting, ShareValidationError, ShareValidationResult}, }, - utils::{bytes_to_hex, merkle_root_from_path, target_to_difficulty, u256_to_block_hash}, + merkle_root::merkle_root_from_path, + target::{bytes_to_hex, target_to_difficulty, u256_to_block_hash}, }; +use binary_sv2::{self, Sv2Option}; use bitcoin::{ blockdata::block::{Header, Version}, hashes::sha256d::Hash, CompactTarget, Target as BitcoinTarget, }; -use codec_sv2::binary_sv2::{self, Sv2Option}; use mining_sv2::{ NewExtendedMiningJob, SetNewPrevHash as SetNewPrevHashMp, SubmitSharesExtended, Target, MAX_EXTRANONCE_LEN, @@ -386,11 +385,11 @@ impl<'a> ExtendedChannel<'a> { #[cfg(test)] mod tests { - use crate::channels::client::{ + use crate::client::{ extended::ExtendedChannel, share_accounting::{ShareValidationError, ShareValidationResult}, }; - use codec_sv2::binary_sv2::Sv2Option; + use binary_sv2::Sv2Option; use mining_sv2::{ NewExtendedMiningJob, SetNewPrevHash as SetNewPrevHashMp, SubmitSharesExtended, MAX_EXTRANONCE_LEN, diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/group.rs b/protocols/v2/channels-sv2/src/client/group.rs similarity index 98% rename from protocols/v2/roles-logic-sv2/src/channels/client/group.rs rename to protocols/v2/channels-sv2/src/client/group.rs index ded10529ed..06e9d36c10 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/client/group.rs +++ b/protocols/v2/channels-sv2/src/client/group.rs @@ -1,6 +1,6 @@ //! Abstraction over the state of a Sv2 Group Channel, as seen by a Mining Client -use crate::channels::client::error::GroupChannelError; +use crate::client::error::GroupChannelError; use std::collections::{HashMap, HashSet}; diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/mod.rs b/protocols/v2/channels-sv2/src/client/mod.rs similarity index 100% rename from protocols/v2/roles-logic-sv2/src/channels/client/mod.rs rename to protocols/v2/channels-sv2/src/client/mod.rs diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/share_accounting.rs b/protocols/v2/channels-sv2/src/client/share_accounting.rs similarity index 100% rename from protocols/v2/roles-logic-sv2/src/channels/client/share_accounting.rs rename to protocols/v2/channels-sv2/src/client/share_accounting.rs diff --git a/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs b/protocols/v2/channels-sv2/src/client/standard.rs similarity index 98% rename from protocols/v2/roles-logic-sv2/src/channels/client/standard.rs rename to protocols/v2/channels-sv2/src/client/standard.rs index fe1d9cc372..efe15f590e 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/client/standard.rs +++ b/protocols/v2/channels-sv2/src/client/standard.rs @@ -1,21 +1,20 @@ //! Abstraction over the state of a Sv2 Standard Channel, as seen by a Mining Client use crate::{ - channels::{ - chain_tip::ChainTip, - client::{ - error::StandardChannelError, - share_accounting::{ShareAccounting, ShareValidationError, ShareValidationResult}, - }, + chain_tip::ChainTip, + client::{ + error::StandardChannelError, + share_accounting::{ShareAccounting, ShareValidationError, ShareValidationResult}, }, - utils::{bytes_to_hex, merkle_root_from_path, target_to_difficulty, u256_to_block_hash}, + merkle_root::merkle_root_from_path, + target::{bytes_to_hex, target_to_difficulty, u256_to_block_hash}, }; +use binary_sv2::{self, Sv2Option}; use bitcoin::{ blockdata::block::{Header, Version}, hashes::sha256d::Hash, CompactTarget, Target as BitcoinTarget, }; -use codec_sv2::binary_sv2::{self, Sv2Option}; use mining_sv2::{ NewExtendedMiningJob, NewMiningJob, SetNewPrevHash as SetNewPrevHashMp, SubmitSharesStandard, Target, MAX_EXTRANONCE_LEN, @@ -346,11 +345,11 @@ impl<'a> StandardChannel<'a> { #[cfg(test)] mod tests { - use crate::channels::client::{ + use crate::client::{ share_accounting::{ShareValidationError, ShareValidationResult}, standard::StandardChannel, }; - use codec_sv2::binary_sv2::Sv2Option; + use binary_sv2::Sv2Option; use mining_sv2::{NewMiningJob, SetNewPrevHash as SetNewPrevHashMp, SubmitSharesStandard}; #[test] diff --git a/protocols/v2/roles-logic-sv2/src/channels/mod.rs b/protocols/v2/channels-sv2/src/lib.rs similarity index 52% rename from protocols/v2/roles-logic-sv2/src/channels/mod.rs rename to protocols/v2/channels-sv2/src/lib.rs index a74782d433..39a627e48d 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/mod.rs +++ b/protocols/v2/channels-sv2/src/lib.rs @@ -1,3 +1,6 @@ pub mod chain_tip; pub mod client; +mod merkle_root; pub mod server; +mod target; +pub mod template; diff --git a/protocols/v2/channels-sv2/src/merkle_root.rs b/protocols/v2/channels-sv2/src/merkle_root.rs new file mode 100644 index 0000000000..73636c38f6 --- /dev/null +++ b/protocols/v2/channels-sv2/src/merkle_root.rs @@ -0,0 +1,73 @@ +use bitcoin::{ + consensus, + hashes::{sha256d::Hash as DHash, Hash}, + Transaction, +}; +use tracing::error; + +/// Computes the Merkle root from coinbase transaction components and a path of transaction hashes. +/// +/// Validates and deserializes a coinbase transaction before building the 32-byte Merkle root. +/// Returns [`None`] is the arguments are invalid. +/// +/// ## Components +/// * `coinbase_tx_prefix`: First part of the coinbase transaction (the part before the extranonce). +/// Should be converted from [`binary_sv2::B064K`]. +/// * `coinbase_tx_suffix`: Coinbase transaction suffix (the part after the extranonce). Should be +/// converted from [`binary_sv2::B064K`]. +/// * `extranonce`: Extra nonce space. Should be converted from [`binary_sv2::B032`] and padded with +/// zeros if not `32` bytes long. +/// * `path`: List of transaction hashes. Should be converted from [`binary_sv2::U256`]. +pub fn merkle_root_from_path>( + coinbase_tx_prefix: &[u8], + coinbase_tx_suffix: &[u8], + extranonce: &[u8], + path: &[T], +) -> Option> { + let mut coinbase = + Vec::with_capacity(coinbase_tx_prefix.len() + coinbase_tx_suffix.len() + extranonce.len()); + coinbase.extend_from_slice(coinbase_tx_prefix); + coinbase.extend_from_slice(extranonce); + coinbase.extend_from_slice(coinbase_tx_suffix); + let coinbase: Transaction = match consensus::deserialize(&coinbase[..]) { + Ok(trans) => trans, + Err(e) => { + error!("ERROR: {}", e); + dbg!(e); + return None; + } + }; + + let coinbase_id: [u8; 32] = *coinbase.compute_txid().as_ref(); + + Some(merkle_root_from_path_(coinbase_id, path).to_vec()) +} + +/// Computes the Merkle root from a validated coinbase transaction and a path of transaction +/// hashes. +/// +/// If the `path` is empty, the coinbase transaction hash (`coinbase_id`) is returned as the root. +/// +/// ## Components +/// * `coinbase_id`: Coinbase transaction hash. +/// * `path`: List of transaction hashes. Should be converted from [`binary_sv2::U256`]. +pub fn merkle_root_from_path_>(coinbase_id: [u8; 32], path: &[T]) -> [u8; 32] { + match path.len() { + 0 => coinbase_id, + _ => reduce_path(coinbase_id, path), + } +} + +// Computes the Merkle root by iteratively combining the coinbase transaction hash with each +// transaction hash in the `path`. +// +// Handles the core logic of combining hashes using the Bitcoin double-SHA256 hashing algorithm. +fn reduce_path>(coinbase_id: [u8; 32], path: &[T]) -> [u8; 32] { + let mut root = coinbase_id; + for node in path { + let to_hash = [&root[..], node.as_ref()].concat(); + let hash = DHash::hash(&to_hash); + root = *hash.as_ref(); + } + root +} diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/error.rs b/protocols/v2/channels-sv2/src/server/error.rs similarity index 91% rename from protocols/v2/roles-logic-sv2/src/channels/server/error.rs rename to protocols/v2/channels-sv2/src/server/error.rs index f56a99b3ca..615988f684 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/error.rs +++ b/protocols/v2/channels-sv2/src/server/error.rs @@ -1,4 +1,4 @@ -use crate::channels::server::jobs::error::JobFactoryError; +use crate::server::jobs::error::JobFactoryError; #[derive(Debug)] pub enum ExtendedChannelError { diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs b/protocols/v2/channels-sv2/src/server/extended.rs similarity index 99% rename from protocols/v2/roles-logic-sv2/src/channels/server/extended.rs rename to protocols/v2/channels-sv2/src/server/extended.rs index 574a342600..c52e5bb17c 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/extended.rs +++ b/protocols/v2/channels-sv2/src/server/extended.rs @@ -1,26 +1,22 @@ //! Mining Server abstraction over the state of a Sv2 Extended Channel use crate::{ - channels::{ - chain_tip::ChainTip, - server::{ - error::ExtendedChannelError, - jobs::{extended::ExtendedJob, factory::JobFactory, job_store::JobStore, JobOrigin}, - share_accounting::{ShareAccounting, ShareValidationError, ShareValidationResult}, - }, - }, - utils::{ - bytes_to_hex, hash_rate_to_target, merkle_root_from_path, target_to_difficulty, - u256_to_block_hash, + chain_tip::ChainTip, + merkle_root::merkle_root_from_path, + server::{ + error::ExtendedChannelError, + jobs::{extended::ExtendedJob, factory::JobFactory, job_store::JobStore, JobOrigin}, + share_accounting::{ShareAccounting, ShareValidationError, ShareValidationResult}, }, + target::{bytes_to_hex, hash_rate_to_target, target_to_difficulty, u256_to_block_hash}, }; +use binary_sv2::{self}; use bitcoin::{ blockdata::block::{Header, Version}, hashes::sha256d::Hash, transaction::TxOut, CompactTarget, Target as BitcoinTarget, }; -use codec_sv2::binary_sv2; use mining_sv2::{SetCustomMiningJob, SubmitSharesExtended, Target, MAX_EXTRANONCE_LEN}; use std::{collections::HashMap, convert::TryInto}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash as SetNewPrevHashTdp}; @@ -564,7 +560,7 @@ impl<'a> ExtendedChannel<'a> { #[cfg(test)] mod tests { - use crate::channels::{ + use crate::{ chain_tip::ChainTip, server::{ error::ExtendedChannelError, @@ -573,8 +569,8 @@ mod tests { share_accounting::{ShareValidationError, ShareValidationResult}, }, }; + use binary_sv2::Sv2Option; use bitcoin::{transaction::TxOut, Amount, ScriptBuf}; - use codec_sv2::binary_sv2::Sv2Option; use mining_sv2::{NewExtendedMiningJob, SubmitSharesExtended, Target, MAX_EXTRANONCE_LEN}; use std::convert::TryInto; use template_distribution_sv2::{NewTemplate, SetNewPrevHash}; diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/group.rs b/protocols/v2/channels-sv2/src/server/group.rs similarity index 99% rename from protocols/v2/roles-logic-sv2/src/channels/server/group.rs rename to protocols/v2/channels-sv2/src/server/group.rs index b120c8bae1..8123c0af35 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/group.rs +++ b/protocols/v2/channels-sv2/src/server/group.rs @@ -1,5 +1,5 @@ //! Abstraction over the state of a Sv2 Group Channel, as seen by a Mining Server -use crate::channels::{ +use crate::{ chain_tip::ChainTip, server::{ error::GroupChannelError, @@ -172,12 +172,12 @@ impl<'a> GroupChannel<'a> { #[cfg(test)] mod tests { - use crate::channels::{ + use crate::{ chain_tip::ChainTip, server::{group::GroupChannel, jobs::job_store::DefaultJobStore}, }; + use binary_sv2::Sv2Option; use bitcoin::{transaction::TxOut, Amount, ScriptBuf}; - use codec_sv2::binary_sv2::Sv2Option; use mining_sv2::NewExtendedMiningJob; use std::convert::TryInto; use template_distribution_sv2::{NewTemplate, SetNewPrevHash}; diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs b/protocols/v2/channels-sv2/src/server/jobs/error.rs similarity index 100% rename from protocols/v2/roles-logic-sv2/src/channels/server/jobs/error.rs rename to protocols/v2/channels-sv2/src/server/jobs/error.rs diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs b/protocols/v2/channels-sv2/src/server/jobs/extended.rs similarity index 96% rename from protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs rename to protocols/v2/channels-sv2/src/server/jobs/extended.rs index d3e43b2fd3..95275857b8 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/extended.rs +++ b/protocols/v2/channels-sv2/src/server/jobs/extended.rs @@ -1,19 +1,17 @@ use super::Job; use crate::{ - channels::{ - chain_tip::ChainTip, - server::jobs::{error::ExtendedJobError, JobOrigin}, - }, - template_distribution_sv2::NewTemplate, - utils::deserialize_template_outputs, + chain_tip::ChainTip, + server::jobs::{error::ExtendedJobError, JobOrigin}, + template::deserialize_template_outputs, }; +use binary_sv2::{Seq0255, Sv2Option, B0255, B064K, U256}; use bitcoin::{ consensus::{deserialize, serialize}, transaction::{Transaction, TxOut}, }; -use codec_sv2::binary_sv2::{Seq0255, Sv2Option, B0255, B064K, U256}; use mining_sv2::{NewExtendedMiningJob, SetCustomMiningJob, MAX_EXTRANONCE_LEN}; use std::convert::TryInto; +use template_distribution_sv2::NewTemplate; /// Abstraction of an extended mining job with: /// - the `NewTemplate` OR `SetCustomMiningJob` message that originated it diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs b/protocols/v2/channels-sv2/src/server/jobs/factory.rs similarity index 97% rename from protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs rename to protocols/v2/channels-sv2/src/server/jobs/factory.rs index 59acb0d115..a2c8ae4703 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/factory.rs +++ b/protocols/v2/channels-sv2/src/server/jobs/factory.rs @@ -1,12 +1,11 @@ //! Abstraction of a factory for creating Sv2 Extended or Standard Jobs. use crate::{ - channels::{ - chain_tip::ChainTip, - server::jobs::{error::*, extended::ExtendedJob, standard::StandardJob}, - }, - template_distribution_sv2::NewTemplate, - utils::{deserialize_template_outputs, merkle_root_from_path, Id as JobIdFactory}, + chain_tip::ChainTip, + merkle_root::merkle_root_from_path, + server::jobs::{error::*, extended::ExtendedJob, standard::StandardJob}, + template::deserialize_template_outputs, }; +use binary_sv2::{Sv2Option, B064K}; use bitcoin::{ absolute::LockTime, blockdata::witness::Witness, @@ -14,9 +13,27 @@ use bitcoin::{ transaction::{OutPoint, Transaction, TxIn, TxOut, Version}, Amount, Sequence, }; -use codec_sv2::binary_sv2::{Sv2Option, B064K}; use mining_sv2::{NewExtendedMiningJob, NewMiningJob, SetCustomMiningJob, MAX_EXTRANONCE_LEN}; use std::convert::TryInto; +use template_distribution_sv2::NewTemplate; + +#[derive(Debug, PartialEq, Eq, Clone)] +struct JobIdFactory { + state: u32, +} + +impl JobIdFactory { + /// Creates a new [`Id`] instance initialized to `0`. + fn new() -> Self { + Self { state: 0 } + } + + /// Increments then returns the internal state on a new ID. + fn next(&mut self) -> u32 { + self.state += 1; + self.state + } +} /// A Factory for creating Extended or Standard Jobs. /// diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/job_store.rs b/protocols/v2/channels-sv2/src/server/jobs/job_store.rs similarity index 100% rename from protocols/v2/roles-logic-sv2/src/channels/server/jobs/job_store.rs rename to protocols/v2/channels-sv2/src/server/jobs/job_store.rs diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/mod.rs b/protocols/v2/channels-sv2/src/server/jobs/mod.rs similarity index 100% rename from protocols/v2/roles-logic-sv2/src/channels/server/jobs/mod.rs rename to protocols/v2/channels-sv2/src/server/jobs/mod.rs diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs b/protocols/v2/channels-sv2/src/server/jobs/standard.rs similarity index 94% rename from protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs rename to protocols/v2/channels-sv2/src/server/jobs/standard.rs index 40930217e7..a9ad17da93 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/jobs/standard.rs +++ b/protocols/v2/channels-sv2/src/server/jobs/standard.rs @@ -1,9 +1,9 @@ use crate::{ - channels::server::jobs::{error::StandardJobError, Job}, - utils::deserialize_template_outputs, + server::jobs::{error::StandardJobError, Job}, + template::deserialize_template_outputs, }; +use binary_sv2::{Sv2Option, U256}; use bitcoin::transaction::TxOut; -use codec_sv2::binary_sv2::{Sv2Option, U256}; use mining_sv2::NewMiningJob; use template_distribution_sv2::NewTemplate; diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/mod.rs b/protocols/v2/channels-sv2/src/server/mod.rs similarity index 100% rename from protocols/v2/roles-logic-sv2/src/channels/server/mod.rs rename to protocols/v2/channels-sv2/src/server/mod.rs diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/share_accounting.rs b/protocols/v2/channels-sv2/src/server/share_accounting.rs similarity index 100% rename from protocols/v2/roles-logic-sv2/src/channels/server/share_accounting.rs rename to protocols/v2/channels-sv2/src/server/share_accounting.rs diff --git a/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs b/protocols/v2/channels-sv2/src/server/standard.rs similarity index 98% rename from protocols/v2/roles-logic-sv2/src/channels/server/standard.rs rename to protocols/v2/channels-sv2/src/server/standard.rs index dfc7fa0b05..b4612b2d44 100644 --- a/protocols/v2/roles-logic-sv2/src/channels/server/standard.rs +++ b/protocols/v2/channels-sv2/src/server/standard.rs @@ -1,15 +1,14 @@ //! Abstraction over the state of a Sv2 Standard Channel, as seen by a Mining Server use crate::{ - channels::{ - chain_tip::ChainTip, - server::{ - error::StandardChannelError, - jobs::{factory::JobFactory, job_store::JobStore, standard::StandardJob}, - share_accounting::{ShareAccounting, ShareValidationError, ShareValidationResult}, - }, + chain_tip::ChainTip, + server::{ + error::StandardChannelError, + jobs::{factory::JobFactory, job_store::JobStore, standard::StandardJob}, + share_accounting::{ShareAccounting, ShareValidationError, ShareValidationResult}, }, - utils::{bytes_to_hex, hash_rate_to_target, target_to_difficulty, u256_to_block_hash}, + target::{bytes_to_hex, hash_rate_to_target, target_to_difficulty, u256_to_block_hash}, }; +use binary_sv2::{self}; use bitcoin::{ absolute::LockTime, blockdata::{ @@ -21,7 +20,6 @@ use bitcoin::{ transaction::{OutPoint, Transaction, TxIn, TxOut, Version as TxVersion}, CompactTarget, Sequence, Target as BitcoinTarget, }; -use codec_sv2::binary_sv2; use mining_sv2::{SubmitSharesStandard, Target, MAX_EXTRANONCE_LEN}; use std::{collections::HashMap, convert::TryInto}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash}; @@ -492,7 +490,7 @@ impl<'a> StandardChannel<'a> { #[cfg(test)] mod tests { - use crate::channels::{ + use crate::{ chain_tip::ChainTip, server::{ error::StandardChannelError, @@ -501,8 +499,8 @@ mod tests { standard::StandardChannel, }, }; + use binary_sv2::Sv2Option; use bitcoin::{transaction::TxOut, Amount, ScriptBuf}; - use codec_sv2::binary_sv2::Sv2Option; use mining_sv2::{NewMiningJob, SubmitSharesStandard, Target}; use std::convert::TryInto; use template_distribution_sv2::{NewTemplate, SetNewPrevHash as SetNewPrevHashTdp}; diff --git a/protocols/v2/channels-sv2/src/target.rs b/protocols/v2/channels-sv2/src/target.rs new file mode 100644 index 0000000000..bdc8fc62e5 --- /dev/null +++ b/protocols/v2/channels-sv2/src/target.rs @@ -0,0 +1,164 @@ +use binary_sv2::U256; +use bitcoin::{hash_types::BlockHash, hashes::Hash}; +use mining_sv2::Target; +use primitive_types::U256 as U256Primitive; +use std::{cmp::max, convert::TryInto, fmt::Write, ops::Div}; + +/// Converts a `Target` to a `f64` difficulty. +pub fn target_to_difficulty(target: Target) -> f64 { + // Genesis block target: 0x00000000ffff0000000000000000000000000000000000000000000000000000 + // (in little endian) + let max_target_bytes = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, + ]; + let max_target = U256Primitive::from_little_endian(&max_target_bytes); + + // Convert input target to U256Primitive + let target_u256: U256<'static> = target.into(); + let mut target_bytes = [0u8; 32]; + target_bytes.copy_from_slice(target_u256.inner_as_ref()); + let target = U256Primitive::from_little_endian(&target_bytes); + + // Calculate difficulty = max_target / target + // We need to handle the full 256-bit values properly + // Convert to f64 by taking the ratio of the most significant bits + let max_target_high = (max_target >> 128).low_u128() as f64; + let max_target_low = max_target.low_u128() as f64; + let target_high = (target >> 128).low_u128() as f64; + let target_low = target.low_u128() as f64; + + // Combine high and low parts with appropriate scaling + let max_target_f64 = max_target_high * (2.0f64.powi(128)) + max_target_low; + let target_f64 = target_high * (2.0f64.powi(128)) + target_low; + + max_target_f64 / target_f64 +} + +/// Converts a `u256` to a [`BlockHash`] type. +pub fn u256_to_block_hash(v: U256<'static>) -> BlockHash { + let hash: [u8; 32] = v.to_vec().try_into().unwrap(); + let hash = Hash::from_slice(&hash).unwrap(); + BlockHash::from_raw_hash(hash) +} + +// Helper function to format bytes as hex string +// useful for visualizing targets +pub fn bytes_to_hex(bytes: &[u8]) -> String { + let mut s = String::with_capacity(bytes.len() * 2); + for &b in bytes { + write!(&mut s, "{b:02x}") + .expect("Writing hex bytes to pre-allocated string should never fail"); + } + s +} + +/// Calculates the mining target threshold for a mining device based on its hashrate (H/s) and +/// desired share frequency (shares/min). +/// +/// Determines the maximum hash value (target), in big endian, that a mining device can produce to +/// find a valid share. The target is derived from the miner's hashrate and the expected number of +/// shares per minute, aligning the miner's workload with the upstream's (e.g. pool's) share +/// frequency requirements. +/// +/// Typically used during connection setup to assign a starting target based on the mining device's +/// reported hashrate and to recalculate during runtime when a mining device's hashrate changes, +/// ensuring they submit shares at the desired rate. +/// +/// ## Formula +/// ```text +/// t = (2^256 - sh) / (sh + 1) +/// ``` +/// +/// Where: +/// - `h`: Mining device hashrate (H/s). +/// - `s`: Shares per second `60 / shares/min` (s). +/// - `sh`: `h * s`, the mining device's work over `s` seconds. +/// +/// According to \[1] and \[2], it is possible to model the probability of finding a block with +/// a random variable X whose distribution is negative hypergeometric \[3]. Such a variable is +/// characterized as follows: +/// +/// Say that there are `n` (`2^256`) elements (possible hash values), of which `t` (values <= +/// target) are defined as success and the remaining as failures. The variable `X` has co-domain +/// the positive integers, and `X=k` is the event where element are drawn one after the other, +/// without replacement, and only the `k`th element is successful. The expected value of this +/// variable is `(n-t)/(t+1)`. So, on average, a miner has to perform `(2^256-t)/(t+1)` hashes +/// before finding hash whose value is below the target `t`. +/// +/// If the pool wants, on average, a share every `s` seconds, then, on average, the miner has to +/// perform `h*s` hashes before finding one that is smaller than the target, where `h` is the +/// miner's hashrate. Therefore, `s*h= (2^256-t)/(t+1)`. If we consider `h` the global Bitcoin's +/// hashrate, `s = 600` seconds and `t` the Bitcoin global target, then, for all the blocks we +/// tried, the two members of the equations have the same order of magnitude and, most of the +/// cases, they coincide with the first two digits. +/// +/// We take this as evidence of the correctness of our calculations. Thus, if the pool wants on +/// average a share every `s` seconds from a miner with hashrate `h`, then the target `t` for the +/// miner is `t = (2^256-sh)/(sh+1)`. +/// +/// \[1] [https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3399742](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3399742) +/// +/// \[2] [https://www.zora.uzh.ch/id/eprint/173483/1/SSRN-id3399742-2.pdf](https://www.zora.uzh.ch/id/eprint/173483/1/SSRN-id3399742-2.pdf) +/// +/// \[3] [https://en.wikipedia.org/wiki/Negative_hypergeometric_distribution](https://en.wikipedia.org/wiki/Negative_hypergeometric_distribution) +pub fn hash_rate_to_target( + hashrate: f64, + share_per_min: f64, +) -> Result, HashRateToTargetError> { + // checks that we are not dividing by zero + if share_per_min == 0.0 { + return Err(HashRateToTargetError::DivisionByZero); + } + if share_per_min.is_sign_negative() { + return Err(HashRateToTargetError::NegativeInput); + }; + if hashrate.is_sign_negative() { + return Err(HashRateToTargetError::NegativeInput); + }; + + // if we want 5 shares per minute, this means that s=60/5=12 seconds interval between shares + // this quantity will be at the numerator, so we multiply the result by 100 again later + let shares_occurrency_frequence = 60_f64 / share_per_min; + + let h_times_s = hashrate * shares_occurrency_frequence; + let h_times_s = h_times_s as u128; + + // We calculate the denominator: h*s+1 + // the denominator is h*s+1, where h*s is an u128, so always positive. + // this means that the denominator can never be zero + // we add 100 in place of 1 because h*s is actually h*s*100, we in order to simplify later we + // must calculate (h*s+1)*100 + let h_times_s_plus_one = max(h_times_s, h_times_s + 1); + + let h_times_s_plus_one = from_u128_to_u256(h_times_s_plus_one); + let denominator = h_times_s_plus_one; + + // We calculate the numerator: 2^256-sh + let two_to_256_minus_one = [255_u8; 32]; + let two_to_256_minus_one = U256Primitive::from_big_endian(two_to_256_minus_one.as_ref()); + + let mut h_times_s_array = [0u8; 32]; + h_times_s_array[16..].copy_from_slice(&h_times_s.to_be_bytes()); + let numerator = two_to_256_minus_one - U256Primitive::from_big_endian(h_times_s_array.as_ref()); + + let mut target = numerator.div(denominator).to_big_endian(); + target.reverse(); + Ok(U256::<'static>::from(target)) +} + +/// Converts a `u128` to a [`U256`]. +pub fn from_u128_to_u256(input: u128) -> U256Primitive { + let input: [u8; 16] = input.to_be_bytes(); + let mut be_bytes = [0_u8; 32]; + for (i, b) in input.iter().enumerate() { + be_bytes[16 + i] = *b; + } + U256Primitive::from_big_endian(be_bytes.as_ref()) +} + +pub enum HashRateToTargetError { + DivisionByZero, + NegativeInput, +} diff --git a/protocols/v2/channels-sv2/src/template.rs b/protocols/v2/channels-sv2/src/template.rs new file mode 100644 index 0000000000..b7a64bb9dc --- /dev/null +++ b/protocols/v2/channels-sv2/src/template.rs @@ -0,0 +1,23 @@ +use bitcoin::{consensus::Decodable, transaction::TxOut}; +use std::io::Cursor; + +/// Deserializes a vector of serialized outputs into a vector of TxOuts. +/// +/// Only to be used for deserializing outputs from a NewTemplate message. +/// +/// Not suitable for deserializing outputs from a SetCustomMiningJob message or +/// AllocateMiningJobToken.Success. +pub fn deserialize_template_outputs( + serialized_outputs: Vec, + coinbase_tx_outputs_count: u32, +) -> Result, TemplateOutputsDeserializationError> { + let mut cursor = Cursor::new(serialized_outputs); + + (0..coinbase_tx_outputs_count) + .map(|_| { + TxOut::consensus_decode(&mut cursor).map_err(|_| TemplateOutputsDeserializationError) + }) + .collect() +} + +pub struct TemplateOutputsDeserializationError; diff --git a/protocols/v2/roles-logic-sv2/Cargo.toml b/protocols/v2/roles-logic-sv2/Cargo.toml index b49fd28d69..836178df57 100644 --- a/protocols/v2/roles-logic-sv2/Cargo.toml +++ b/protocols/v2/roles-logic-sv2/Cargo.toml @@ -14,6 +14,7 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] [dependencies] bitcoin = { version = "0.32.5" } +channels_sv2 = { path = "../channels-sv2", version = "^0.1.0" } common_messages_sv2 = { path = "../../../protocols/v2/subprotocols/common-messages", version = "^5.0.0" } mining_sv2 = { path = "../../../protocols/v2/subprotocols/mining", version = "^4.0.0" } template_distribution_sv2 = { path = "../../../protocols/v2/subprotocols/template-distribution", version = "^3.0.0" } diff --git a/protocols/v2/roles-logic-sv2/src/errors.rs b/protocols/v2/roles-logic-sv2/src/errors.rs index 2c7f20567c..c335eeec52 100644 --- a/protocols/v2/roles-logic-sv2/src/errors.rs +++ b/protocols/v2/roles-logic-sv2/src/errors.rs @@ -3,13 +3,9 @@ //! This module defines error types and utilities for handling errors in the `roles_logic_sv2` //! module. It includes the [`Error`] enum for representing various errors. -use crate::{ - channels::server::error::{ExtendedChannelError, GroupChannelError, StandardChannelError}, - parsers::AnyMessage as AllMessages, - utils::InputError, - vardiff::error::VardiffError, -}; +use crate::{parsers::AnyMessage as AllMessages, utils::InputError, vardiff::error::VardiffError}; use bitcoin::hashes::FromSliceError; +use channels_sv2::server::error::{ExtendedChannelError, GroupChannelError, StandardChannelError}; use codec_sv2::binary_sv2::Error as BinarySv2Error; use mining_sv2::ExtendedExtranonceError; use std::{ diff --git a/protocols/v2/roles-logic-sv2/src/lib.rs b/protocols/v2/roles-logic-sv2/src/lib.rs index 865b73b92f..1000354978 100644 --- a/protocols/v2/roles-logic-sv2/src/lib.rs +++ b/protocols/v2/roles-logic-sv2/src/lib.rs @@ -18,7 +18,6 @@ //! //! - `prop_test`: Enables support for property testing in [`template_distribution_sv2`] crate. pub mod channel_logic; -pub mod channels; pub mod errors; pub mod handlers; pub mod job_creator; @@ -26,6 +25,7 @@ pub mod parsers; pub mod utils; pub mod vardiff; pub use bitcoin; +pub use channels_sv2; pub use codec_sv2; pub use common_messages_sv2; pub use errors::Error; diff --git a/roles/Cargo.lock b/roles/Cargo.lock index f47ca40894..8cd7968c85 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -586,6 +586,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "channels_sv2" +version = "0.1.0" +dependencies = [ + "binary_sv2", + "bitcoin", + "common_messages_sv2", + "job_declaration_sv2", + "mining_sv2", + "primitive-types", + "template_distribution_sv2", + "tracing", +] + [[package]] name = "cipher" version = "0.4.4" @@ -2181,6 +2195,7 @@ version = "3.0.0" dependencies = [ "bitcoin", "chacha20poly1305", + "channels_sv2", "codec_sv2", "common_messages_sv2", "hex-conservative 0.3.0", diff --git a/test/integration-tests/Cargo.lock b/test/integration-tests/Cargo.lock index 70dbaa8c9f..96a7cd44c1 100644 --- a/test/integration-tests/Cargo.lock +++ b/test/integration-tests/Cargo.lock @@ -464,6 +464,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "channels_sv2" +version = "0.1.0" +dependencies = [ + "binary_sv2", + "bitcoin", + "common_messages_sv2", + "job_declaration_sv2", + "mining_sv2", + "primitive-types", + "template_distribution_sv2", + "tracing", +] + [[package]] name = "cipher" version = "0.4.4" @@ -1946,6 +1960,7 @@ version = "3.0.0" dependencies = [ "bitcoin", "chacha20poly1305", + "channels_sv2", "codec_sv2", "common_messages_sv2", "hex-conservative 0.3.0", diff --git a/utils/Cargo.lock b/utils/Cargo.lock index 419e2830c5..25c015877a 100644 --- a/utils/Cargo.lock +++ b/utils/Cargo.lock @@ -287,6 +287,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "channels_sv2" +version = "0.1.0" +dependencies = [ + "binary_sv2", + "bitcoin", + "common_messages_sv2", + "job_declaration_sv2", + "mining_sv2", + "primitive-types", + "template_distribution_sv2", + "tracing", +] + [[package]] name = "cipher" version = "0.4.4" @@ -1009,6 +1023,7 @@ version = "3.0.0" dependencies = [ "bitcoin", "chacha20poly1305", + "channels_sv2", "codec_sv2", "common_messages_sv2", "hex-conservative 0.3.0", From 8f56c99ab44ea99c2ab944d49f206d25c0f069a5 Mon Sep 17 00:00:00 2001 From: plebhash Date: Tue, 1 Jul 2025 12:40:11 -0300 Subject: [PATCH 095/338] adapt pool to use standalone channels_sv2 crate (re-exported via roles_logic_sv2) --- roles/pool/src/lib/mining_pool/message_handler.rs | 2 +- roles/pool/src/lib/mining_pool/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index 586d9ea535..fda1fc7506 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -12,7 +12,7 @@ use std::{ }; use stratum_common::roles_logic_sv2::{ bitcoin::{consensus::Decodable, transaction::TxOut, Amount}, - channels::server::{ + channels_sv2::server::{ error::{ExtendedChannelError, StandardChannelError}, extended::ExtendedChannel, group::GroupChannel, diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 62bc019935..92d686bd2d 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -42,7 +42,7 @@ use stratum_common::{ roles_logic_sv2::{ self, bitcoin::{Amount, TxOut}, - channels::server::{ + channels_sv2::server::{ extended::ExtendedChannel, group::GroupChannel, standard::StandardChannel, }, codec_sv2::{ From 68450b6907554f0c5c4cbc6cf249824df6d76375 Mon Sep 17 00:00:00 2001 From: plebhash Date: Tue, 8 Jul 2025 17:32:04 -0300 Subject: [PATCH 096/338] cover channels_sv2 crate on CI --- .github/workflows/coverage-protocols.yaml | 8 ++++++++ .github/workflows/docs.yaml | 5 +++++ .github/workflows/release-libs.yaml | 5 +++++ .github/workflows/semver-check.yaml | 4 ++++ scripts/coverage-protocols.sh | 1 + 5 files changed, 23 insertions(+) diff --git a/.github/workflows/coverage-protocols.yaml b/.github/workflows/coverage-protocols.yaml index 6a870ff17a..219c69978b 100644 --- a/.github/workflows/coverage-protocols.yaml +++ b/.github/workflows/coverage-protocols.yaml @@ -53,6 +53,14 @@ jobs: flags: codec_sv2-coverage token: ${{ secrets.CODECOV_TOKEN }} + - name: Upload channels_sv2-coverage to codecov.io + uses: codecov/codecov-action@v4 + with: + directory: ./protocols/target/tarpaulin-reports/channels-sv2-coverage + file: ./protocols/target/tarpaulin-reports/channels-sv2-coverage/cobertura.xml + flags: channels_sv2-coverage + token: ${{ secrets.CODECOV_TOKEN }} + - name: Upload common_messages_sv2-coverage to codecov.io uses: codecov/codecov-action@v4 with: diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 1d0fe6f812..b9dcdcaeea 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -49,6 +49,11 @@ jobs: cd protocols/v2/binary-sv2 cargo doc --features with_buffer_pool + - name: Rust Docs crate channels_sv2 + run: | + cd protocols/v2/channels-sv2 + cargo doc + - name: Rust Docs crate framing_sv2 run: | cd protocols/v2/framing-sv2 diff --git a/.github/workflows/release-libs.yaml b/.github/workflows/release-libs.yaml index 225ca9fae5..368622df3a 100644 --- a/.github/workflows/release-libs.yaml +++ b/.github/workflows/release-libs.yaml @@ -86,6 +86,11 @@ jobs: run: | ./scripts/release-libs.sh protocols/v2/subprotocols/template-distribution + # channels_sv2 (depends on binary_sv2, common_messages_sv2, mining_sv2, template_distribution_sv2, job_declaration_sv2) + - name: Publish crate channels_sv2 + run: | + ./scripts/release-libs.sh protocols/v2/channels-sv2 + # sv1_api (depends on binary_sv2) - name: Publish crate v1 run: | diff --git a/.github/workflows/semver-check.yaml b/.github/workflows/semver-check.yaml index 30596e25b0..d71ab451b9 100644 --- a/.github/workflows/semver-check.yaml +++ b/.github/workflows/semver-check.yaml @@ -93,6 +93,10 @@ jobs: working-directory: protocols/v2/roles-logic-sv2 run: cargo semver-checks --default-features + - name: Run semver checks for protocols/v2/channels-sv2 + working-directory: protocols/v2/channels-sv2 + run: cargo semver-checks + - name: Run semver checks for protocols/v1 working-directory: protocols/v1 run: cargo semver-checks diff --git a/scripts/coverage-protocols.sh b/scripts/coverage-protocols.sh index d53aa982c9..1a983d8c6e 100755 --- a/scripts/coverage-protocols.sh +++ b/scripts/coverage-protocols.sh @@ -14,6 +14,7 @@ crates=( "v2/binary-sv2/codec" "v2/binary-sv2/derive_codec" "v2/binary-sv2" + "v2/channels-sv2" "v2/noise-sv2" "v2/framing-sv2" "v2/codec-sv2" From 1406f363703bb61b47144d1d68d6671f93a48002 Mon Sep 17 00:00:00 2001 From: plebhash Date: Tue, 8 Jul 2025 18:02:57 -0300 Subject: [PATCH 097/338] isolate parsers-sv2 into a standalone crate --- common/Cargo.lock | 13 +++ protocols/Cargo.toml | 1 + protocols/v2/parsers-sv2/Cargo.toml | 23 ++++ protocols/v2/parsers-sv2/README.md | 11 ++ protocols/v2/parsers-sv2/src/error.rs | 28 +++++ .../src/parsers.rs => parsers-sv2/src/lib.rs} | 109 +++++++++--------- protocols/v2/roles-logic-sv2/Cargo.toml | 1 + .../src/channel_logic/channel_factory.rs | 2 +- protocols/v2/roles-logic-sv2/src/errors.rs | 12 +- .../v2/roles-logic-sv2/src/handlers/common.rs | 14 ++- .../src/handlers/job_declaration.rs | 13 ++- .../v2/roles-logic-sv2/src/handlers/mining.rs | 7 +- .../src/handlers/template_distribution.rs | 7 +- protocols/v2/roles-logic-sv2/src/lib.rs | 2 +- roles/Cargo.lock | 13 +++ utils/Cargo.lock | 13 +++ 16 files changed, 198 insertions(+), 71 deletions(-) create mode 100644 protocols/v2/parsers-sv2/Cargo.toml create mode 100644 protocols/v2/parsers-sv2/README.md create mode 100644 protocols/v2/parsers-sv2/src/error.rs rename protocols/v2/{roles-logic-sv2/src/parsers.rs => parsers-sv2/src/lib.rs} (96%) diff --git a/common/Cargo.lock b/common/Cargo.lock index 54bfb63e32..acb9cc2c65 100644 --- a/common/Cargo.lock +++ b/common/Cargo.lock @@ -808,6 +808,18 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "parsers_sv2" +version = "0.1.0" +dependencies = [ + "binary_sv2", + "common_messages_sv2", + "framing_sv2", + "job_declaration_sv2", + "mining_sv2", + "template_distribution_sv2", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -948,6 +960,7 @@ dependencies = [ "job_declaration_sv2", "mining_sv2", "nohash-hasher", + "parsers_sv2", "primitive-types", "template_distribution_sv2", "tracing", diff --git a/protocols/Cargo.toml b/protocols/Cargo.toml index f99392ad1a..3380c8827c 100644 --- a/protocols/Cargo.toml +++ b/protocols/Cargo.toml @@ -17,6 +17,7 @@ members = [ "v2/sv2-ffi", "v2/roles-logic-sv2", "v2/channels-sv2", + "v2/parsers-sv2", ] [profile.dev] diff --git a/protocols/v2/parsers-sv2/Cargo.toml b/protocols/v2/parsers-sv2/Cargo.toml new file mode 100644 index 0000000000..b94369a2a3 --- /dev/null +++ b/protocols/v2/parsers-sv2/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "parsers_sv2" +version = "0.1.0" +authors = ["The Stratum V2 Developers"] +edition = "2018" +readme = "README.md" +description = "Sv2 Message Parsers" +documentation = "https://docs.rs/parsers_sv2" +license = "MIT OR Apache-2.0" +repository = "https://github.com/stratum-mining/stratum" +homepage = "https://stratumprotocol.org" +keywords = ["stratum", "mining", "bitcoin", "protocol"] + +[dependencies] +binary_sv2 = { path = "../binary-sv2", version = "^3.0.0" } +framing_sv2 = { path = "../framing-sv2", version = "^5.0.0" } +common_messages_sv2 = { path = "../subprotocols/common-messages", version = "^5.0.0" } +mining_sv2 = { path = "../subprotocols/mining", version = "^4.0.0" } +template_distribution_sv2 = { path = "../subprotocols/template-distribution", version = "^3.0.0" } +job_declaration_sv2 = { path = "../subprotocols/job-declaration", version = "^4.0.0" } + +[dev-dependencies] +codec_sv2 = { path = "../codec-sv2", features = ["noise_sv2", "with_buffer_pool"] } diff --git a/protocols/v2/parsers-sv2/README.md b/protocols/v2/parsers-sv2/README.md new file mode 100644 index 0000000000..fa912f55b0 --- /dev/null +++ b/protocols/v2/parsers-sv2/README.md @@ -0,0 +1,11 @@ +# `parsers_sv2` + +[![crates.io](https://img.shields.io/crates/v/parsers_sv2.svg)](https://crates.io/crates/parsers_sv2) +[![docs.rs](https://docs.rs/parsers_sv2/badge.svg)](https://docs.rs/parsers_sv2) +[![rustc+](https://img.shields.io/badge/rustc-1.75.0%2B-lightgrey.svg)](https://blog.rust-lang.org/2023/12/28/Rust-1.75.0.html) +[![license](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/stratum-mining/stratum/blob/main/LICENSE.md) +[![codecov](https://codecov.io/gh/stratum-mining/stratum/branch/main/graph/badge.svg?flag=parsers_sv2-coverage)](https://codecov.io/gh/stratum-mining/stratum) + +`parsers_sv2` provides logic to convert raw Stratum V2 (Sv2) message data into Rust types, as well as logic to handle conversions among Sv2 Rust types. + +Most of the logic on this crate is tightly coupled with the [`binary_sv2`](https://docs.rs/binary_sv2/latest/binary_sv2/) crate. \ No newline at end of file diff --git a/protocols/v2/parsers-sv2/src/error.rs b/protocols/v2/parsers-sv2/src/error.rs new file mode 100644 index 0000000000..8db3d7b41f --- /dev/null +++ b/protocols/v2/parsers-sv2/src/error.rs @@ -0,0 +1,28 @@ +#[derive(Debug)] +pub enum ParserError { + UnexpectedMessage(u8), + BadPayloadSize, + UnexpectedPoolMessage, + BinaryError(binary_sv2::Error), +} + +impl From for ParserError { + fn from(e: binary_sv2::Error) -> Self { + ParserError::BinaryError(e) + } +} + +impl std::fmt::Display for ParserError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParserError::UnexpectedMessage(msg_type) => { + write!(f, "Unexpected message type: {msg_type}") + } + ParserError::BadPayloadSize => write!(f, "Bad payload size"), + ParserError::UnexpectedPoolMessage => write!(f, "Unexpected pool message"), + ParserError::BinaryError(e) => write!(f, "Binary error: {e:?}"), + } + } +} + +impl std::error::Error for ParserError {} diff --git a/protocols/v2/roles-logic-sv2/src/parsers.rs b/protocols/v2/parsers-sv2/src/lib.rs similarity index 96% rename from protocols/v2/roles-logic-sv2/src/parsers.rs rename to protocols/v2/parsers-sv2/src/lib.rs index dc1dc84322..ec70629ea1 100644 --- a/protocols/v2/roles-logic-sv2/src/parsers.rs +++ b/protocols/v2/parsers-sv2/src/lib.rs @@ -20,21 +20,21 @@ //! - **Mining Protocol**: Manages standard mining communication (e.g., job dispatch, shares //! submission). -use crate::Error; -use codec_sv2::{ - binary_sv2::{ - self, - decodable::{DecodableField, FieldMarker}, - encodable::EncodableField, - from_bytes, Deserialize, GetSize, - }, - framing_sv2::framing::Sv2Frame, +pub mod error; + +use binary_sv2::{ + self, + decodable::{DecodableField, FieldMarker}, + encodable::EncodableField, + from_bytes, Deserialize, GetSize, }; use common_messages_sv2::*; use core::{ convert::{TryFrom, TryInto}, fmt, }; +pub use error::ParserError; +use framing_sv2::framing::Sv2Frame; use job_declaration_sv2::*; use mining_sv2::*; use template_distribution_sv2::*; @@ -781,22 +781,22 @@ pub enum CommonMessageTypes { } impl TryFrom for CommonMessageTypes { - type Error = Error; + type Error = ParserError; - fn try_from(v: u8) -> Result { + fn try_from(v: u8) -> Result { match v { MESSAGE_TYPE_SETUP_CONNECTION => Ok(CommonMessageTypes::SetupConnection), MESSAGE_TYPE_SETUP_CONNECTION_SUCCESS => Ok(CommonMessageTypes::SetupConnectionSuccess), MESSAGE_TYPE_SETUP_CONNECTION_ERROR => Ok(CommonMessageTypes::SetupConnectionError), MESSAGE_TYPE_CHANNEL_ENDPOINT_CHANGED => Ok(CommonMessageTypes::ChannelEndpointChanged), MESSAGE_TYPE_RECONNECT => Ok(CommonMessageTypes::Reconnect), - _ => Err(Error::UnexpectedMessage(v)), + _ => Err(ParserError::UnexpectedMessage(v)), } } } impl<'a> TryFrom<(u8, &'a mut [u8])> for CommonMessages<'a> { - type Error = Error; + type Error = ParserError; fn try_from(v: (u8, &'a mut [u8])) -> Result { let msg_type: CommonMessageTypes = v.0.try_into()?; @@ -840,9 +840,9 @@ pub enum TemplateDistributionTypes { } impl TryFrom for TemplateDistributionTypes { - type Error = Error; + type Error = ParserError; - fn try_from(v: u8) -> Result { + fn try_from(v: u8) -> Result { match v { MESSAGE_TYPE_COINBASE_OUTPUT_CONSTRAINTS => { Ok(TemplateDistributionTypes::CoinbaseOutputConstraints) @@ -859,13 +859,13 @@ impl TryFrom for TemplateDistributionTypes { Ok(TemplateDistributionTypes::RequestTransactionDataError) } MESSAGE_TYPE_SUBMIT_SOLUTION => Ok(TemplateDistributionTypes::SubmitSolution), - _ => Err(Error::UnexpectedMessage(v)), + _ => Err(ParserError::UnexpectedMessage(v)), } } } impl<'a> TryFrom<(u8, &'a mut [u8])> for TemplateDistribution<'a> { - type Error = Error; + type Error = ParserError; fn try_from(v: (u8, &'a mut [u8])) -> Result { let msg_type: TemplateDistributionTypes = v.0.try_into()?; @@ -918,9 +918,9 @@ pub enum JobDeclarationTypes { } impl TryFrom for JobDeclarationTypes { - type Error = Error; + type Error = ParserError; - fn try_from(v: u8) -> Result { + fn try_from(v: u8) -> Result { match v { MESSAGE_TYPE_ALLOCATE_MINING_JOB_TOKEN => { Ok(JobDeclarationTypes::AllocateMiningJobToken) @@ -940,13 +940,13 @@ impl TryFrom for JobDeclarationTypes { Ok(JobDeclarationTypes::ProvideMissingTransactionsSuccess) } MESSAGE_TYPE_PUSH_SOLUTION => Ok(JobDeclarationTypes::PushSolution), - _ => Err(Error::UnexpectedMessage(v)), + _ => Err(ParserError::UnexpectedMessage(v)), } } } impl<'a> TryFrom<(u8, &'a mut [u8])> for JobDeclaration<'a> { - type Error = Error; + type Error = ParserError; fn try_from(v: (u8, &'a mut [u8])) -> Result { let msg_type: JobDeclarationTypes = v.0.try_into()?; @@ -1016,9 +1016,9 @@ pub enum MiningTypes { } impl TryFrom for MiningTypes { - type Error = Error; + type Error = ParserError; - fn try_from(v: u8) -> Result { + fn try_from(v: u8) -> Result { match v { MESSAGE_TYPE_CLOSE_CHANNEL => Ok(MiningTypes::CloseChannel), MESSAGE_TYPE_NEW_EXTENDED_MINING_JOB => Ok(MiningTypes::NewExtendedMiningJob), @@ -1047,14 +1047,14 @@ impl TryFrom for MiningTypes { MESSAGE_TYPE_SUBMIT_SHARES_SUCCESS => Ok(MiningTypes::SubmitSharesSuccess), MESSAGE_TYPE_UPDATE_CHANNEL => Ok(MiningTypes::UpdateChannel), MESSAGE_TYPE_UPDATE_CHANNEL_ERROR => Ok(MiningTypes::UpdateChannelError), - MESSAGE_TYPE_SETUP_CONNECTION => Err(Error::UnexpectedMessage(v)), - _ => Err(Error::UnexpectedMessage(v)), + MESSAGE_TYPE_SETUP_CONNECTION => Err(ParserError::UnexpectedMessage(v)), + _ => Err(ParserError::UnexpectedMessage(v)), } } } impl<'a> TryFrom<(u8, &'a mut [u8])> for Mining<'a> { - type Error = Error; + type Error = ParserError; fn try_from(v: (u8, &'a mut [u8])) -> Result { let msg_type: MiningTypes = v.0.try_into()?; @@ -1170,11 +1170,11 @@ impl GetSize for MiningDeviceMessages<'_> { } } impl<'a> TryFrom<(u8, &'a mut [u8])> for MiningDeviceMessages<'a> { - type Error = Error; + type Error = ParserError; fn try_from(v: (u8, &'a mut [u8])) -> Result { - let is_common: Result = v.0.try_into(); - let is_mining: Result = v.0.try_into(); + let is_common: Result = v.0.try_into(); + let is_mining: Result = v.0.try_into(); match (is_common, is_mining) { (Ok(_), Err(_)) => Ok(Self::Common(v.try_into()?)), (Err(_), Ok(_)) => Ok(Self::Mining(v.try_into()?)), @@ -1206,7 +1206,7 @@ impl fmt::Display for AnyMessage<'_> { } impl<'a> TryFrom> for AnyMessage<'a> { - type Error = Error; + type Error = ParserError; fn try_from(value: MiningDeviceMessages<'a>) -> Result { match value { @@ -1274,13 +1274,14 @@ impl IsSv2Message for MiningDeviceMessages<'_> { } impl<'a> TryFrom<(u8, &'a mut [u8])> for AnyMessage<'a> { - type Error = Error; + type Error = ParserError; fn try_from(v: (u8, &'a mut [u8])) -> Result { - let is_common: Result = v.0.try_into(); - let is_mining: Result = v.0.try_into(); - let is_job_declaration: Result = v.0.try_into(); - let is_template_distribution: Result = v.0.try_into(); + let is_common: Result = v.0.try_into(); + let is_mining: Result = v.0.try_into(); + let is_job_declaration: Result = v.0.try_into(); + let is_template_distribution: Result = + v.0.try_into(); match ( is_common, is_mining, @@ -1347,68 +1348,64 @@ impl<'a, T: Into>> From for MiningDeviceMessages<'a> { impl<'decoder, B: AsMut<[u8]> + AsRef<[u8]>> TryFrom> for Sv2Frame, B> { - type Error = Error; + type Error = ParserError; - fn try_from(v: AnyMessage<'decoder>) -> Result { + fn try_from(v: AnyMessage<'decoder>) -> Result { let extension_type = 0; let channel_bit = v.channel_bit(); let message_type = v.message_type(); Sv2Frame::from_message(v, message_type, extension_type, channel_bit) - .ok_or(Error::BadPayloadSize) + .ok_or(ParserError::BadPayloadSize) } } impl<'decoder, B: AsMut<[u8]> + AsRef<[u8]>> TryFrom> for Sv2Frame, B> { - type Error = Error; + type Error = ParserError; - fn try_from(v: MiningDeviceMessages<'decoder>) -> Result { + fn try_from(v: MiningDeviceMessages<'decoder>) -> Result { let extension_type = 0; let channel_bit = v.channel_bit(); let message_type = v.message_type(); Sv2Frame::from_message(v, message_type, extension_type, channel_bit) - .ok_or(Error::BadPayloadSize) + .ok_or(ParserError::BadPayloadSize) } } impl<'decoder, B: AsMut<[u8]> + AsRef<[u8]>> TryFrom> for Sv2Frame, B> { - type Error = Error; + type Error = ParserError; - fn try_from(v: TemplateDistribution<'decoder>) -> Result { + fn try_from(v: TemplateDistribution<'decoder>) -> Result { let extension_type = 0; let channel_bit = v.channel_bit(); let message_type = v.message_type(); Sv2Frame::from_message(v, message_type, extension_type, channel_bit) - .ok_or(Error::BadPayloadSize) + .ok_or(ParserError::BadPayloadSize) } } impl<'a> TryFrom> for MiningDeviceMessages<'a> { - type Error = Error; + type Error = ParserError; - fn try_from(value: AnyMessage<'a>) -> Result { + fn try_from(value: AnyMessage<'a>) -> Result { match value { AnyMessage::Common(message) => Ok(Self::Common(message)), AnyMessage::Mining(message) => Ok(Self::Mining(message)), - AnyMessage::JobDeclaration(_) => Err(Error::UnexpectedPoolMessage), - AnyMessage::TemplateDistribution(_) => Err(Error::UnexpectedPoolMessage), + AnyMessage::JobDeclaration(_) => Err(ParserError::UnexpectedPoolMessage), + AnyMessage::TemplateDistribution(_) => Err(ParserError::UnexpectedPoolMessage), } } } #[cfg(test)] mod test { - use crate::{ - mining_sv2::NewMiningJob, - parsers::{AnyMessage, Mining}, - }; - use codec_sv2::{ - binary_sv2::{Sv2Option, U256}, - StandardSv2Frame, - }; + use crate::{AnyMessage, Mining}; + use binary_sv2::{Sv2Option, U256}; + use codec_sv2::StandardSv2Frame; + use mining_sv2::NewMiningJob; use std::convert::{TryFrom, TryInto}; pub type Message = AnyMessage<'static>; diff --git a/protocols/v2/roles-logic-sv2/Cargo.toml b/protocols/v2/roles-logic-sv2/Cargo.toml index 836178df57..0c4ca7b8bd 100644 --- a/protocols/v2/roles-logic-sv2/Cargo.toml +++ b/protocols/v2/roles-logic-sv2/Cargo.toml @@ -15,6 +15,7 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] [dependencies] bitcoin = { version = "0.32.5" } channels_sv2 = { path = "../channels-sv2", version = "^0.1.0" } +parsers_sv2 = { path = "../parsers-sv2", version = "^0.1.0" } common_messages_sv2 = { path = "../../../protocols/v2/subprotocols/common-messages", version = "^5.0.0" } mining_sv2 = { path = "../../../protocols/v2/subprotocols/mining", version = "^4.0.0" } template_distribution_sv2 = { path = "../../../protocols/v2/subprotocols/template-distribution", version = "^3.0.0" } diff --git a/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs b/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs index 0293f383d2..056f6e6b4e 100644 --- a/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs +++ b/protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs @@ -4,7 +4,6 @@ use crate::{ job_creator::{self, JobsCreators}, - parsers::Mining, utils::{GroupId, Id, Mutex}, Error, }; @@ -15,6 +14,7 @@ use mining_sv2::{ OpenMiningChannelError, SetCustomMiningJob, SetCustomMiningJobSuccess, SetNewPrevHash, SubmitSharesError, SubmitSharesExtended, SubmitSharesStandard, Target, }; +use parsers_sv2::Mining; use hex::DisplayHex; use nohash_hasher::BuildNoHashHasher; diff --git a/protocols/v2/roles-logic-sv2/src/errors.rs b/protocols/v2/roles-logic-sv2/src/errors.rs index c335eeec52..ac6ef3b187 100644 --- a/protocols/v2/roles-logic-sv2/src/errors.rs +++ b/protocols/v2/roles-logic-sv2/src/errors.rs @@ -3,11 +3,12 @@ //! This module defines error types and utilities for handling errors in the `roles_logic_sv2` //! module. It includes the [`Error`] enum for representing various errors. -use crate::{parsers::AnyMessage as AllMessages, utils::InputError, vardiff::error::VardiffError}; +use crate::{utils::InputError, vardiff::error::VardiffError}; use bitcoin::hashes::FromSliceError; use channels_sv2::server::error::{ExtendedChannelError, GroupChannelError, StandardChannelError}; use codec_sv2::binary_sv2::Error as BinarySv2Error; use mining_sv2::ExtendedExtranonceError; +use parsers_sv2::AnyMessage as AllMessages; use std::{ fmt::{self, Display, Formatter}, sync::{MutexGuard, PoisonError}, @@ -128,6 +129,8 @@ pub enum Error { FailedToSendSolution, FailedToSetCustomMiningJob(ExtendedChannelError), FailedToDeserializeCoinbaseOutputs, + /// Error from parsers_sv2 + ParserError(parsers_sv2::ParserError), } impl From for Error { @@ -154,6 +157,12 @@ impl From for Error { } } +impl From for Error { + fn from(v: parsers_sv2::ParserError) -> Error { + Error::ParserError(v) + } +} + impl Display for Error { fn fmt(&self, f: &mut Formatter) -> fmt::Result { use Error::*; @@ -248,6 +257,7 @@ impl Display for Error { FailedToProcessSetNewPrevHashExtendedChannel(e) => write!(f, "Failed to process SetNewPrevHash: {e:?}"), FailedToProcessSetNewPrevHashStandardChannel(e) => write!(f, "Failed to process SetNewPrevHash: {e:?}"), FailedToDeserializeCoinbaseOutputs => write!(f, "Failed to deserialize coinbase outputs"), + ParserError(v) => write!(f, "Parser error: {v}"), } } } diff --git a/protocols/v2/roles-logic-sv2/src/handlers/common.rs b/protocols/v2/roles-logic-sv2/src/handlers/common.rs index a183ab8e58..f1496853f2 100644 --- a/protocols/v2/roles-logic-sv2/src/handlers/common.rs +++ b/protocols/v2/roles-logic-sv2/src/handlers/common.rs @@ -25,7 +25,9 @@ //! Stratum V2 networks. use super::SendTo_; -use crate::{errors::Error, parsers::CommonMessages, utils::Mutex}; +use crate::{errors::Error, utils::Mutex}; +use parsers_sv2::CommonMessages; + use common_messages_sv2::{ ChannelEndpointChanged, Reconnect, SetupConnection, SetupConnectionError, SetupConnectionSuccess, *, @@ -49,7 +51,10 @@ where message_type: u8, payload: &mut [u8], ) -> Result { - Self::handle_message_common_deserialized(self_, (message_type, payload).try_into()) + Self::handle_message_common_deserialized( + self_, + (message_type, payload).try_into().map_err(Into::into), + ) } /// Takes a message and it calls the appropriate handler function @@ -117,7 +122,10 @@ where message_type: u8, payload: &mut [u8], ) -> Result { - Self::handle_message_common_deserialized(self_, (message_type, payload).try_into()) + Self::handle_message_common_deserialized( + self_, + (message_type, payload).try_into().map_err(Into::into), + ) } /// It takes a message do setup connection message, it calls diff --git a/protocols/v2/roles-logic-sv2/src/handlers/job_declaration.rs b/protocols/v2/roles-logic-sv2/src/handlers/job_declaration.rs index 19fd7ffc59..f6830158ca 100644 --- a/protocols/v2/roles-logic-sv2/src/handlers/job_declaration.rs +++ b/protocols/v2/roles-logic-sv2/src/handlers/job_declaration.rs @@ -28,7 +28,8 @@ //! - Error handling mechanisms to address unexpected messages and ensure safe processing, //! particularly in the context of shared state. -use crate::{parsers::JobDeclaration, utils::Mutex}; +use crate::utils::Mutex; +use parsers_sv2::JobDeclaration; use std::sync::Arc; /// see [`SendTo_`] @@ -58,7 +59,10 @@ where message_type: u8, payload: &mut [u8], ) -> Result { - Self::handle_message_job_declaration_deserialized(self_, (message_type, payload).try_into()) + Self::handle_message_job_declaration_deserialized( + self_, + (message_type, payload).try_into().map_err(Into::into), + ) } /// Routes a deserialized job declaration message to the appropriate handler function. @@ -141,7 +145,10 @@ where message_type: u8, payload: &mut [u8], ) -> Result { - Self::handle_message_job_declaration_deserialized(self_, (message_type, payload).try_into()) + Self::handle_message_job_declaration_deserialized( + self_, + (message_type, payload).try_into().map_err(Into::into), + ) } /// Routes a deserialized job declaration message to the appropriate handler function. diff --git a/protocols/v2/roles-logic-sv2/src/handlers/mining.rs b/protocols/v2/roles-logic-sv2/src/handlers/mining.rs index 0488a7c1d5..781edd7c1a 100644 --- a/protocols/v2/roles-logic-sv2/src/handlers/mining.rs +++ b/protocols/v2/roles-logic-sv2/src/handlers/mining.rs @@ -27,7 +27,7 @@ //! - Support for managing mining channels, extranonce prefixes, and share submissions, while //! handling edge cases and ensuring the correctness of the mining process. -use crate::{errors::Error, parsers::Mining}; +use crate::errors::Error; use codec_sv2::binary_sv2; use core::convert::TryInto; use mining_sv2::{ @@ -38,6 +38,7 @@ use mining_sv2::{ SubmitSharesError, SubmitSharesExtended, SubmitSharesStandard, SubmitSharesSuccess, UpdateChannel, UpdateChannelError, }; +use parsers_sv2::Mining; use super::SendTo_; @@ -80,7 +81,7 @@ where { match Self::handle_message_mining_deserialized( self_mutex, - (message_type, payload).try_into(), + (message_type, payload).try_into().map_err(Into::into), ) { Err(Error::UnexpectedMessage(0)) => Err(Error::UnexpectedMessage(message_type)), result => result, @@ -253,7 +254,7 @@ where ) -> Result, Error> { match Self::handle_message_mining_deserialized( self_mutex, - (message_type, payload).try_into(), + (message_type, payload).try_into().map_err(Into::into), ) { Err(Error::UnexpectedMessage(0)) => Err(Error::UnexpectedMessage(message_type)), result => result, diff --git a/protocols/v2/roles-logic-sv2/src/handlers/template_distribution.rs b/protocols/v2/roles-logic-sv2/src/handlers/template_distribution.rs index 0fa218cb1e..9054a23874 100644 --- a/protocols/v2/roles-logic-sv2/src/handlers/template_distribution.rs +++ b/protocols/v2/roles-logic-sv2/src/handlers/template_distribution.rs @@ -26,7 +26,8 @@ //! especially in the context of shared state. use super::SendTo_; -use crate::{errors::Error, parsers::TemplateDistribution, utils::Mutex}; +use crate::{errors::Error, utils::Mutex}; +use parsers_sv2::TemplateDistribution; use template_distribution_sv2::{ CoinbaseOutputConstraints, NewTemplate, RequestTransactionData, RequestTransactionDataError, RequestTransactionDataSuccess, SetNewPrevHash, SubmitSolution, @@ -57,7 +58,7 @@ where ) -> Result { Self::handle_message_template_distribution_deserialized( self_, - (message_type, payload).try_into(), + (message_type, payload).try_into().map_err(Into::into), ) } @@ -144,7 +145,7 @@ where ) -> Result { Self::handle_message_template_distribution_deserialized( self_, - (message_type, payload).try_into(), + (message_type, payload).try_into().map_err(Into::into), ) } diff --git a/protocols/v2/roles-logic-sv2/src/lib.rs b/protocols/v2/roles-logic-sv2/src/lib.rs index 1000354978..9d12f3975d 100644 --- a/protocols/v2/roles-logic-sv2/src/lib.rs +++ b/protocols/v2/roles-logic-sv2/src/lib.rs @@ -21,7 +21,6 @@ pub mod channel_logic; pub mod errors; pub mod handlers; pub mod job_creator; -pub mod parsers; pub mod utils; pub mod vardiff; pub use bitcoin; @@ -31,5 +30,6 @@ pub use common_messages_sv2; pub use errors::Error; pub use job_declaration_sv2; pub use mining_sv2; +pub use parsers_sv2; pub use template_distribution_sv2; pub use vardiff::{classic::VardiffState, Vardiff}; diff --git a/roles/Cargo.lock b/roles/Cargo.lock index 8cd7968c85..eea43b3e75 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -1868,6 +1868,18 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "parsers_sv2" +version = "0.1.0" +dependencies = [ + "binary_sv2", + "common_messages_sv2", + "framing_sv2", + "job_declaration_sv2", + "mining_sv2", + "template_distribution_sv2", +] + [[package]] name = "pathdiff" version = "0.2.3" @@ -2202,6 +2214,7 @@ dependencies = [ "job_declaration_sv2", "mining_sv2", "nohash-hasher", + "parsers_sv2", "primitive-types", "template_distribution_sv2", "tracing", diff --git a/utils/Cargo.lock b/utils/Cargo.lock index 25c015877a..d0a47ff27f 100644 --- a/utils/Cargo.lock +++ b/utils/Cargo.lock @@ -828,6 +828,18 @@ dependencies = [ "syn", ] +[[package]] +name = "parsers_sv2" +version = "0.1.0" +dependencies = [ + "binary_sv2", + "common_messages_sv2", + "framing_sv2", + "job_declaration_sv2", + "mining_sv2", + "template_distribution_sv2", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1030,6 +1042,7 @@ dependencies = [ "job_declaration_sv2", "mining_sv2", "nohash-hasher", + "parsers_sv2", "primitive-types", "template_distribution_sv2", "tracing", From 51a24b5ee4e257347ae27d9d54a132ac63ecf483 Mon Sep 17 00:00:00 2001 From: plebhash Date: Tue, 8 Jul 2025 18:27:02 -0300 Subject: [PATCH 098/338] adapt roles and integration tests to use standalone parsers_sv2 crate (re-exported via roles_logic_sv2) --- roles/jd-client/src/lib/downstream.rs | 4 ++-- roles/jd-client/src/lib/error.rs | 9 +++++++++ .../src/lib/job_declarator/message_handler.rs | 2 +- roles/jd-client/src/lib/job_declarator/mod.rs | 2 +- .../src/lib/job_declarator/setup_connection.rs | 2 +- roles/jd-client/src/lib/status.rs | 1 + .../src/lib/template_receiver/message_handler.rs | 2 +- roles/jd-client/src/lib/template_receiver/mod.rs | 4 ++-- .../src/lib/template_receiver/setup_connection.rs | 2 +- roles/jd-client/src/lib/upstream_sv2/mod.rs | 2 +- roles/jd-client/src/lib/upstream_sv2/upstream.rs | 2 +- roles/jd-server/src/lib/error.rs | 2 +- .../src/lib/job_declarator/message_handler.rs | 4 ++-- roles/jd-server/src/lib/job_declarator/mod.rs | 4 ++-- roles/jd-server/src/lib/mod.rs | 2 +- roles/jd-server/src/lib/status.rs | 2 +- roles/pool/src/lib/error.rs | 10 +++++++++- roles/pool/src/lib/mining_pool/message_handler.rs | 2 +- roles/pool/src/lib/mining_pool/mod.rs | 4 ++-- roles/pool/src/lib/mining_pool/setup_connection.rs | 2 +- roles/pool/src/lib/status.rs | 3 ++- .../src/lib/template_receiver/message_handler.rs | 2 +- roles/pool/src/lib/template_receiver/mod.rs | 2 +- .../src/lib/template_receiver/setup_connection.rs | 2 +- roles/test-utils/mining-device/src/lib/mod.rs | 2 +- roles/translator/src/lib/error.rs | 10 +++++++++- roles/translator/src/lib/proxy/bridge.rs | 2 +- roles/translator/src/lib/status.rs | 3 ++- .../src/lib/upstream_sv2/diff_management.rs | 2 +- roles/translator/src/lib/upstream_sv2/mod.rs | 2 +- roles/translator/src/lib/upstream_sv2/upstream.rs | 4 ++-- test/integration-tests/Cargo.lock | 12 ++++++++++++ test/integration-tests/lib/interceptor.rs | 2 +- test/integration-tests/lib/message_aggregator.rs | 2 +- test/integration-tests/lib/mock_roles.rs | 4 ++-- test/integration-tests/lib/sniffer.rs | 2 +- test/integration-tests/lib/types.rs | 2 +- test/integration-tests/lib/utils.rs | 2 +- test/integration-tests/tests/jd_integration.rs | 6 +++--- test/integration-tests/tests/jdc_fallback.rs | 2 +- test/integration-tests/tests/pool_integration.rs | 2 +- test/integration-tests/tests/sniffer_integration.rs | 2 +- .../tests/translator_integration.rs | 2 +- 43 files changed, 89 insertions(+), 49 deletions(-) diff --git a/roles/jd-client/src/lib/downstream.rs b/roles/jd-client/src/lib/downstream.rs index f319318039..5fa561a301 100644 --- a/roles/jd-client/src/lib/downstream.rs +++ b/roles/jd-client/src/lib/downstream.rs @@ -41,7 +41,7 @@ use stratum_common::roles_logic_sv2::{ }, job_creator::JobsCreators, mining_sv2::*, - parsers::{AnyMessage, Mining, MiningDeviceMessages}, + parsers_sv2::{AnyMessage, Mining, MiningDeviceMessages}, template_distribution_sv2::{NewTemplate, SubmitSolution}, utils::Mutex, }; @@ -1054,7 +1054,7 @@ pub async fn listen_for_downstream_mining( payload, ) { let message = match message { - roles_logic_sv2::parsers::CommonMessages::SetupConnectionSuccess(m) => m, + roles_logic_sv2::parsers_sv2::CommonMessages::SetupConnectionSuccess(m) => m, _ => panic!(), }; diff --git a/roles/jd-client/src/lib/error.rs b/roles/jd-client/src/lib/error.rs index e10c6faa38..407738fe3a 100644 --- a/roles/jd-client/src/lib/error.rs +++ b/roles/jd-client/src/lib/error.rs @@ -17,6 +17,7 @@ use stratum_common::roles_logic_sv2::{ self, codec_sv2::{self, binary_sv2, framing_sv2}, mining_sv2::{ExtendedExtranonce, NewExtendedMiningJob, SetCustomMiningJob}, + parsers_sv2::ParserError, }; pub type ProxyResult<'a, T> = core::result::Result>; @@ -73,6 +74,7 @@ pub enum Error<'a> { // Channel Sender Errors ChannelErrorSender(ChannelSendError<'a>), Infallible(std::convert::Infallible), + Parser(ParserError), } impl fmt::Display for Error<'_> { @@ -95,10 +97,17 @@ impl fmt::Display for Error<'_> { ChannelErrorSender(ref e) => write!(f, "Channel send error: `{e:?}`"), VecToSlice32(ref e) => write!(f, "Standard Error: `{e:?}`"), Infallible(ref e) => write!(f, "Infallible Error:`{e:?}`"), + Parser(ref e) => write!(f, "Parser error: `{e:?}`"), } } } +impl From for Error<'_> { + fn from(e: ParserError) -> Self { + Error::Parser(e) + } +} + impl From for Error<'_> { fn from(e: binary_sv2::Error) -> Self { Error::BinarySv2(e) diff --git a/roles/jd-client/src/lib/job_declarator/message_handler.rs b/roles/jd-client/src/lib/job_declarator/message_handler.rs index 907570550d..67800c320f 100644 --- a/roles/jd-client/src/lib/job_declarator/message_handler.rs +++ b/roles/jd-client/src/lib/job_declarator/message_handler.rs @@ -10,7 +10,7 @@ use stratum_common::roles_logic_sv2::{ AllocateMiningJobTokenSuccess, DeclareMiningJobError, DeclareMiningJobSuccess, ProvideMissingTransactions, ProvideMissingTransactionsSuccess, }, - parsers::JobDeclaration, + parsers_sv2::JobDeclaration, }; use tracing::{debug, error, info}; pub type SendTo = SendTo_, ()>; diff --git a/roles/jd-client/src/lib/job_declarator/mod.rs b/roles/jd-client/src/lib/job_declarator/mod.rs index b86118f217..fe3bb0c0d3 100644 --- a/roles/jd-client/src/lib/job_declarator/mod.rs +++ b/roles/jd-client/src/lib/job_declarator/mod.rs @@ -30,7 +30,7 @@ use stratum_common::{ handlers::SendTo_, job_declaration_sv2::{AllocateMiningJobTokenSuccess, PushSolution}, mining_sv2::SubmitSharesExtended, - parsers::{AnyMessage, JobDeclaration}, + parsers_sv2::{AnyMessage, JobDeclaration}, template_distribution_sv2::SetNewPrevHash, utils::{deserialize_template_outputs, Mutex}, }, diff --git a/roles/jd-client/src/lib/job_declarator/setup_connection.rs b/roles/jd-client/src/lib/job_declarator/setup_connection.rs index 236095db9a..1a3228c219 100644 --- a/roles/jd-client/src/lib/job_declarator/setup_connection.rs +++ b/roles/jd-client/src/lib/job_declarator/setup_connection.rs @@ -12,7 +12,7 @@ use stratum_common::roles_logic_sv2::{ codec_sv2::{StandardEitherFrame, StandardSv2Frame}, common_messages_sv2::{Protocol, Reconnect, SetupConnection}, handlers::common::{ParseCommonMessagesFromUpstream, SendTo}, - parsers::AnyMessage, + parsers_sv2::AnyMessage, utils::Mutex, Error, }; diff --git a/roles/jd-client/src/lib/status.rs b/roles/jd-client/src/lib/status.rs index f6e16bd577..447252d4be 100644 --- a/roles/jd-client/src/lib/status.rs +++ b/roles/jd-client/src/lib/status.rs @@ -163,5 +163,6 @@ pub async fn handle_error( send_status(sender, e, error_handling::ErrorBranch::Break).await } Error::Infallible(_) => send_status(sender, e, error_handling::ErrorBranch::Break).await, + Error::Parser(_) => send_status(sender, e, error_handling::ErrorBranch::Continue).await, } } diff --git a/roles/jd-client/src/lib/template_receiver/message_handler.rs b/roles/jd-client/src/lib/template_receiver/message_handler.rs index 50780b8e9b..a4d9867acc 100644 --- a/roles/jd-client/src/lib/template_receiver/message_handler.rs +++ b/roles/jd-client/src/lib/template_receiver/message_handler.rs @@ -9,7 +9,7 @@ use super::TemplateRx; use stratum_common::roles_logic_sv2::{ errors::Error, handlers::template_distribution::{ParseTemplateDistributionMessagesFromServer, SendTo}, - parsers::TemplateDistribution, + parsers_sv2::TemplateDistribution, template_distribution_sv2::*, }; use tracing::{debug, error, info}; diff --git a/roles/jd-client/src/lib/template_receiver/mod.rs b/roles/jd-client/src/lib/template_receiver/mod.rs index e80468cc92..63bdea6e4d 100644 --- a/roles/jd-client/src/lib/template_receiver/mod.rs +++ b/roles/jd-client/src/lib/template_receiver/mod.rs @@ -22,7 +22,7 @@ use stratum_common::{ codec_sv2::{HandshakeRole, Initiator, StandardEitherFrame, StandardSv2Frame}, handlers::{template_distribution::ParseTemplateDistributionMessagesFromServer, SendTo_}, job_declaration_sv2::AllocateMiningJobTokenSuccess, - parsers::{AnyMessage, TemplateDistribution}, + parsers_sv2::{AnyMessage, TemplateDistribution}, template_distribution_sv2::{ CoinbaseOutputConstraints, NewTemplate, RequestTransactionData, SubmitSolution, }, @@ -35,7 +35,7 @@ use tracing::{error, info, warn}; mod message_handler; mod setup_connection; -pub type SendTo = SendTo_, ()>; +pub type SendTo = SendTo_, ()>; pub type Message = AnyMessage<'static>; pub type StdFrame = StandardSv2Frame; pub type EitherFrame = StandardEitherFrame; diff --git a/roles/jd-client/src/lib/template_receiver/setup_connection.rs b/roles/jd-client/src/lib/template_receiver/setup_connection.rs index bf2a5f3914..b65f190bef 100644 --- a/roles/jd-client/src/lib/template_receiver/setup_connection.rs +++ b/roles/jd-client/src/lib/template_receiver/setup_connection.rs @@ -11,7 +11,7 @@ use stratum_common::roles_logic_sv2::{ codec_sv2::{StandardEitherFrame, StandardSv2Frame}, common_messages_sv2::{Protocol, Reconnect, SetupConnection}, handlers::common::{ParseCommonMessagesFromUpstream, SendTo}, - parsers::AnyMessage, + parsers_sv2::AnyMessage, utils::Mutex, Error, }; diff --git a/roles/jd-client/src/lib/upstream_sv2/mod.rs b/roles/jd-client/src/lib/upstream_sv2/mod.rs index aa5472316e..1fcf604d87 100644 --- a/roles/jd-client/src/lib/upstream_sv2/mod.rs +++ b/roles/jd-client/src/lib/upstream_sv2/mod.rs @@ -1,6 +1,6 @@ use stratum_common::roles_logic_sv2::{ codec_sv2::{StandardEitherFrame, StandardSv2Frame}, - parsers::AnyMessage, + parsers_sv2::AnyMessage, }; pub mod upstream; diff --git a/roles/jd-client/src/lib/upstream_sv2/upstream.rs b/roles/jd-client/src/lib/upstream_sv2/upstream.rs index 0ad1429f75..56004ae6ca 100644 --- a/roles/jd-client/src/lib/upstream_sv2/upstream.rs +++ b/roles/jd-client/src/lib/upstream_sv2/upstream.rs @@ -60,7 +60,7 @@ use stratum_common::{ }, job_declaration_sv2::DeclareMiningJob, mining_sv2::{ExtendedExtranonce, Extranonce, SetCustomMiningJob, SetGroupChannel}, - parsers::{AnyMessage, Mining, MiningDeviceMessages}, + parsers_sv2::{AnyMessage, Mining, MiningDeviceMessages}, utils::{Id, Mutex}, Error as RolesLogicError, }, diff --git a/roles/jd-server/src/lib/error.rs b/roles/jd-server/src/lib/error.rs index 73bcbece79..e4cb7a6c89 100644 --- a/roles/jd-server/src/lib/error.rs +++ b/roles/jd-server/src/lib/error.rs @@ -22,7 +22,7 @@ use std::{ use stratum_common::roles_logic_sv2::{ self, codec_sv2::{self, binary_sv2, noise_sv2}, - parsers::Mining, + parsers_sv2::Mining, }; use crate::mempool::error::JdsMempoolError; diff --git a/roles/jd-server/src/lib/job_declarator/message_handler.rs b/roles/jd-server/src/lib/job_declarator/message_handler.rs index 5f930086ce..69b6f8026d 100644 --- a/roles/jd-server/src/lib/job_declarator/message_handler.rs +++ b/roles/jd-server/src/lib/job_declarator/message_handler.rs @@ -12,14 +12,14 @@ use stratum_common::roles_logic_sv2::{ DeclareMiningJobError, DeclareMiningJobSuccess, ProvideMissingTransactions, ProvideMissingTransactionsSuccess, PushSolution, }, - parsers::JobDeclaration, + parsers_sv2::JobDeclaration, utils::Mutex, }; pub type SendTo = SendTo_, ()>; use crate::mempool::JDsMempool; use super::{signed_token, TransactionState}; -use stratum_common::roles_logic_sv2::{errors::Error, parsers::AnyMessage as AllMessages}; +use stratum_common::roles_logic_sv2::{errors::Error, parsers_sv2::AnyMessage as AllMessages}; use tracing::{debug, info}; use super::JobDeclaratorDownstream; diff --git a/roles/jd-server/src/lib/job_declarator/mod.rs b/roles/jd-server/src/lib/job_declarator/mod.rs index a317f625d7..7307fd6adf 100644 --- a/roles/jd-server/src/lib/job_declarator/mod.rs +++ b/roles/jd-server/src/lib/job_declarator/mod.rs @@ -42,7 +42,7 @@ use stratum_common::{ }, handlers::job_declaration::{ParseJobDeclarationMessagesFromDownstream, SendTo}, job_declaration_sv2::{DeclareMiningJob, PushSolution}, - parsers::{AnyMessage as JdsMessages, JobDeclaration}, + parsers_sv2::{AnyMessage as JdsMessages, JobDeclaration}, utils::{Id, Mutex}, }, }; @@ -230,7 +230,7 @@ impl JobDeclaratorDownstream { /// Wraps the message into a `StdFrame` and sends it through the established channel. pub async fn send( self_mutex: Arc>, - message: roles_logic_sv2::parsers::JobDeclaration<'static>, + message: roles_logic_sv2::parsers_sv2::JobDeclaration<'static>, ) -> Result<(), ()> { let sv2_frame: StdFrame = JdsMessages::JobDeclaration(message).try_into().unwrap(); let sender = self_mutex.safe_lock(|self_| self_.sender.clone()).unwrap(); diff --git a/roles/jd-server/src/lib/mod.rs b/roles/jd-server/src/lib/mod.rs index 00aea8cc54..d494c5a681 100644 --- a/roles/jd-server/src/lib/mod.rs +++ b/roles/jd-server/src/lib/mod.rs @@ -32,7 +32,7 @@ pub use rpc_sv2::Uri; use std::{ops::Sub, str::FromStr, sync::Arc}; use stratum_common::roles_logic_sv2::{ codec_sv2::{StandardEitherFrame, StandardSv2Frame}, - parsers::AnyMessage as JdsMessages, + parsers_sv2::AnyMessage as JdsMessages, utils::Mutex, }; use tokio::{select, task}; diff --git a/roles/jd-server/src/lib/status.rs b/roles/jd-server/src/lib/status.rs index 7562f702bf..b3ba279ad9 100644 --- a/roles/jd-server/src/lib/status.rs +++ b/roles/jd-server/src/lib/status.rs @@ -8,7 +8,7 @@ //! //! This allows for centralized, consistent error handling across the application. -use stratum_common::roles_logic_sv2::parsers::Mining; +use stratum_common::roles_logic_sv2::parsers_sv2::Mining; use super::error::JdsError; diff --git a/roles/pool/src/lib/error.rs b/roles/pool/src/lib/error.rs index cc4f165ab1..7b49d2d9eb 100644 --- a/roles/pool/src/lib/error.rs +++ b/roles/pool/src/lib/error.rs @@ -19,7 +19,7 @@ use std::{ use stratum_common::roles_logic_sv2::{ self, codec_sv2::{self, binary_sv2, noise_sv2}, - parsers::Mining, + parsers_sv2::{Mining, ParserError}, vardiff::error::VardiffError, }; @@ -53,6 +53,7 @@ pub enum PoolError { /// Error related to the SV2 protocol, including an error code and a `Mining` message. Sv2ProtocolError((u32, Mining<'static>)), Vardiff(VardiffError), + Parser(ParserError), } impl From for PoolError { @@ -61,6 +62,12 @@ impl From for PoolError { } } +impl From for PoolError { + fn from(value: ParserError) -> Self { + PoolError::Parser(value) + } +} + impl std::fmt::Display for PoolError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use PoolError::*; @@ -83,6 +90,7 @@ impl std::fmt::Display for PoolError { PoolError::Vardiff(ref e) => { write!(f, "Received Vardiff Error : {e:?}") } + Parser(ref e) => write!(f, "Parser error: `{e:?}`"), } } } diff --git a/roles/pool/src/lib/mining_pool/message_handler.rs b/roles/pool/src/lib/mining_pool/message_handler.rs index fda1fc7506..1b233e3b56 100644 --- a/roles/pool/src/lib/mining_pool/message_handler.rs +++ b/roles/pool/src/lib/mining_pool/message_handler.rs @@ -24,7 +24,7 @@ use stratum_common::roles_logic_sv2::{ errors::Error, handlers::mining::{ParseMiningMessagesFromDownstream, SendTo, SupportedChannelTypes}, mining_sv2::*, - parsers::Mining, + parsers_sv2::Mining, template_distribution_sv2::SubmitSolution, utils::Mutex, VardiffState, diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index 92d686bd2d..5ea0099569 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -54,7 +54,7 @@ use stratum_common::{ ExtendedExtranonce, SetNewPrevHash as SetNewPrevHashMp, SetTarget, Target, MAX_EXTRANONCE_LEN, }, - parsers::{AnyMessage, Mining}, + parsers_sv2::{AnyMessage, Mining}, template_distribution_sv2::{ NewTemplate, SetNewPrevHash as SetNewPrevHashTdp, SubmitSolution, }, @@ -389,7 +389,7 @@ impl Downstream { /// This method is used to send message to downstream. async fn send( self_mutex: Arc>, - message: roles_logic_sv2::parsers::Mining<'static>, + message: roles_logic_sv2::parsers_sv2::Mining<'static>, ) -> PoolResult<()> { //let message = if let Mining::NewExtendedMiningJob(job) = message { // Mining::NewExtendedMiningJob(extended_job_to_non_segwit(job, 32)?) diff --git a/roles/pool/src/lib/mining_pool/setup_connection.rs b/roles/pool/src/lib/mining_pool/setup_connection.rs index a7e66fed55..64dc7d6af2 100644 --- a/roles/pool/src/lib/mining_pool/setup_connection.rs +++ b/roles/pool/src/lib/mining_pool/setup_connection.rs @@ -15,7 +15,7 @@ use stratum_common::roles_logic_sv2::{ common_messages_sv2::{has_requires_std_job, SetupConnection, SetupConnectionSuccess}, errors::Error, handlers::common::ParseCommonMessagesFromDownstream, - parsers::{AnyMessage, CommonMessages}, + parsers_sv2::{AnyMessage, CommonMessages}, utils::Mutex, }; use tracing::{debug, error, info}; diff --git a/roles/pool/src/lib/status.rs b/roles/pool/src/lib/status.rs index 302567ace4..6e7a78927a 100644 --- a/roles/pool/src/lib/status.rs +++ b/roles/pool/src/lib/status.rs @@ -6,7 +6,7 @@ //! Centralizes and simplifies error handling across the system. /// Identifies which component sent a status update. -use stratum_common::roles_logic_sv2::{self, parsers::Mining}; +use stratum_common::roles_logic_sv2::{self, parsers_sv2::Mining}; use super::error::PoolError; @@ -170,5 +170,6 @@ pub async fn handle_error(sender: &Sender, e: PoolError) -> error_handling::Erro PoolError::Vardiff(_) => { send_status(sender, e, error_handling::ErrorBranch::Continue).await } + PoolError::Parser(_) => send_status(sender, e, error_handling::ErrorBranch::Break).await, } } diff --git a/roles/pool/src/lib/template_receiver/message_handler.rs b/roles/pool/src/lib/template_receiver/message_handler.rs index e3a326ae72..ba57446f08 100644 --- a/roles/pool/src/lib/template_receiver/message_handler.rs +++ b/roles/pool/src/lib/template_receiver/message_handler.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use stratum_common::roles_logic_sv2::{ errors::Error, handlers::template_distribution::{ParseTemplateDistributionMessagesFromServer, SendTo}, - parsers::TemplateDistribution, + parsers_sv2::TemplateDistribution, template_distribution_sv2::*, utils::Mutex, }; diff --git a/roles/pool/src/lib/template_receiver/mod.rs b/roles/pool/src/lib/template_receiver/mod.rs index 34d2a20e02..45ea1bff46 100644 --- a/roles/pool/src/lib/template_receiver/mod.rs +++ b/roles/pool/src/lib/template_receiver/mod.rs @@ -22,7 +22,7 @@ use stratum_common::{ self, codec_sv2, codec_sv2::{HandshakeRole, Initiator}, handlers::template_distribution::ParseTemplateDistributionMessagesFromServer, - parsers::{AnyMessage, TemplateDistribution}, + parsers_sv2::{AnyMessage, TemplateDistribution}, template_distribution_sv2::{ CoinbaseOutputConstraints, NewTemplate, SetNewPrevHash, SubmitSolution, }, diff --git a/roles/pool/src/lib/template_receiver/setup_connection.rs b/roles/pool/src/lib/template_receiver/setup_connection.rs index 1c176c7786..556576b06c 100644 --- a/roles/pool/src/lib/template_receiver/setup_connection.rs +++ b/roles/pool/src/lib/template_receiver/setup_connection.rs @@ -14,7 +14,7 @@ use stratum_common::roles_logic_sv2::{ common_messages_sv2::{Protocol, Reconnect, SetupConnection, SetupConnectionError}, errors::Error, handlers::common::{ParseCommonMessagesFromUpstream, SendTo}, - parsers::{AnyMessage, CommonMessages}, + parsers_sv2::{AnyMessage, CommonMessages}, utils::Mutex, }; use tracing::{error, info}; diff --git a/roles/test-utils/mining-device/src/lib/mod.rs b/roles/test-utils/mining-device/src/lib/mod.rs index 478dece20a..7769d325f7 100644 --- a/roles/test-utils/mining-device/src/lib/mod.rs +++ b/roles/test-utils/mining-device/src/lib/mod.rs @@ -26,7 +26,7 @@ use stratum_common::{ mining::{ParseMiningMessagesFromUpstream, SendTo, SupportedChannelTypes}, }, mining_sv2::*, - parsers::{Mining, MiningDeviceMessages}, + parsers_sv2::{Mining, MiningDeviceMessages}, utils::{Id, Mutex}, }, }; diff --git a/roles/translator/src/lib/error.rs b/roles/translator/src/lib/error.rs index 2e99cac40a..ab10c3c739 100644 --- a/roles/translator/src/lib/error.rs +++ b/roles/translator/src/lib/error.rs @@ -14,7 +14,7 @@ use stratum_common::roles_logic_sv2::{ self, codec_sv2::{self, binary_sv2, framing_sv2, Frame}, mining_sv2::{ExtendedExtranonce, NewExtendedMiningJob, SetCustomMiningJob}, - parsers::{AnyMessage, Mining}, + parsers_sv2::{AnyMessage, Mining, ParserError}, vardiff::error::VardiffError, }; use v1::server_to_client::{Notify, SetDifficulty}; @@ -100,6 +100,7 @@ pub enum Error<'a> { #[allow(clippy::enum_variant_names)] TargetError(roles_logic_sv2::errors::Error), Sv1MessageTooLong, + Parser(ParserError), } impl fmt::Display for Error<'_> { @@ -137,6 +138,7 @@ impl fmt::Display for Error<'_> { Sv1MessageTooLong => { write!(f, "Received an sv1 message that is longer than max len") } + Parser(ref e) => write!(f, "Parser error: `{e:?}`"), } } } @@ -147,6 +149,12 @@ impl From for Error<'_> { } } +impl From for Error<'_> { + fn from(e: ParserError) -> Self { + Error::Parser(e) + } +} + impl From for Error<'_> { fn from(e: codec_sv2::noise_sv2::Error) -> Self { Error::CodecNoise(e) diff --git a/roles/translator/src/lib/proxy/bridge.rs b/roles/translator/src/lib/proxy/bridge.rs index f45dbd8b29..5a9f32e4de 100644 --- a/roles/translator/src/lib/proxy/bridge.rs +++ b/roles/translator/src/lib/proxy/bridge.rs @@ -35,7 +35,7 @@ use stratum_common::roles_logic_sv2::{ mining_sv2::{ ExtendedExtranonce, NewExtendedMiningJob, SetNewPrevHash, SubmitSharesExtended, Target, }, - parsers::Mining, + parsers_sv2::Mining, utils::{GroupId, Mutex}, Error as RolesLogicError, }; diff --git a/roles/translator/src/lib/status.rs b/roles/translator/src/lib/status.rs index 083a161a74..74146ddbb4 100644 --- a/roles/translator/src/lib/status.rs +++ b/roles/translator/src/lib/status.rs @@ -209,7 +209,7 @@ pub async fn handle_error( Error::Sv2ProtocolError(ref inner) => { match inner { // dont notify main thread just continue - roles_logic_sv2::parsers::Mining::SubmitSharesError(_) => { + roles_logic_sv2::parsers_sv2::Mining::SubmitSharesError(_) => { error_handling::ErrorBranch::Continue } _ => send_status(sender, e, error_handling::ErrorBranch::Break).await, @@ -221,5 +221,6 @@ pub async fn handle_error( Error::Sv1MessageTooLong => { send_status(sender, e, error_handling::ErrorBranch::Break).await } + Error::Parser(_) => send_status(sender, e, error_handling::ErrorBranch::Break).await, } } diff --git a/roles/translator/src/lib/upstream_sv2/diff_management.rs b/roles/translator/src/lib/upstream_sv2/diff_management.rs index 8da5f33b60..47ede36ebd 100644 --- a/roles/translator/src/lib/upstream_sv2/diff_management.rs +++ b/roles/translator/src/lib/upstream_sv2/diff_management.rs @@ -16,7 +16,7 @@ use super::super::{ }; use std::{sync::Arc, time::Duration}; use stratum_common::roles_logic_sv2::{ - codec_sv2::binary_sv2::U256, mining_sv2::UpdateChannel, parsers::Mining, utils::Mutex, + codec_sv2::binary_sv2::U256, mining_sv2::UpdateChannel, parsers_sv2::Mining, utils::Mutex, Error as RolesLogicError, }; diff --git a/roles/translator/src/lib/upstream_sv2/mod.rs b/roles/translator/src/lib/upstream_sv2/mod.rs index 3ade09a11d..9f334238a8 100644 --- a/roles/translator/src/lib/upstream_sv2/mod.rs +++ b/roles/translator/src/lib/upstream_sv2/mod.rs @@ -10,7 +10,7 @@ use stratum_common::roles_logic_sv2::{ codec_sv2::{StandardEitherFrame, StandardSv2Frame}, - parsers::AnyMessage, + parsers_sv2::AnyMessage, }; pub mod diff_management; diff --git a/roles/translator/src/lib/upstream_sv2/upstream.rs b/roles/translator/src/lib/upstream_sv2/upstream.rs index 13ff779674..aeca7e7499 100644 --- a/roles/translator/src/lib/upstream_sv2/upstream.rs +++ b/roles/translator/src/lib/upstream_sv2/upstream.rs @@ -48,7 +48,7 @@ use stratum_common::{ ExtendedExtranonce, Extranonce, NewExtendedMiningJob, OpenExtendedMiningChannel, SetNewPrevHash, SubmitSharesExtended, }, - parsers::Mining, + parsers_sv2::Mining, utils::Mutex, Error as RolesLogicError, Error::NoUpstreamsConnected, @@ -542,7 +542,7 @@ impl Upstream { sv2_submit.job_id = handle_result!(tx_status, handle_result!(tx_status, job_id)); let message = Message::Mining( - roles_logic_sv2::parsers::Mining::SubmitSharesExtended(sv2_submit), + roles_logic_sv2::parsers_sv2::Mining::SubmitSharesExtended(sv2_submit), ); let frame: StdFrame = handle_result!(tx_status, message.try_into()); diff --git a/test/integration-tests/Cargo.lock b/test/integration-tests/Cargo.lock index 96a7cd44c1..e788b7a68e 100644 --- a/test/integration-tests/Cargo.lock +++ b/test/integration-tests/Cargo.lock @@ -1666,6 +1666,17 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "parsers_sv2" +version = "0.1.0" +dependencies = [ + "codec_sv2", + "common_messages_sv2", + "job_declaration_sv2", + "mining_sv2", + "template_distribution_sv2", +] + [[package]] name = "pathdiff" version = "0.2.3" @@ -1967,6 +1978,7 @@ dependencies = [ "job_declaration_sv2", "mining_sv2", "nohash-hasher", + "parsers_sv2", "primitive-types", "template_distribution_sv2", "tracing", diff --git a/test/integration-tests/lib/interceptor.rs b/test/integration-tests/lib/interceptor.rs index fd9686750c..7f519eb4cd 100644 --- a/test/integration-tests/lib/interceptor.rs +++ b/test/integration-tests/lib/interceptor.rs @@ -1,5 +1,5 @@ use crate::types::MsgType; -use stratum_common::roles_logic_sv2::parsers::AnyMessage; +use stratum_common::roles_logic_sv2::parsers_sv2::AnyMessage; #[derive(Debug, Clone, PartialEq, Eq)] pub enum MessageDirection { diff --git a/test/integration-tests/lib/message_aggregator.rs b/test/integration-tests/lib/message_aggregator.rs index d5b17159c9..e33a2fc792 100644 --- a/test/integration-tests/lib/message_aggregator.rs +++ b/test/integration-tests/lib/message_aggregator.rs @@ -1,5 +1,5 @@ use std::{collections::VecDeque, sync::Arc}; -use stratum_common::roles_logic_sv2::{parsers::AnyMessage, utils::Mutex}; +use stratum_common::roles_logic_sv2::{parsers_sv2::AnyMessage, utils::Mutex}; use crate::types::MsgType; diff --git a/test/integration-tests/lib/mock_roles.rs b/test/integration-tests/lib/mock_roles.rs index 59a2d1e36a..9e93550418 100644 --- a/test/integration-tests/lib/mock_roles.rs +++ b/test/integration-tests/lib/mock_roles.rs @@ -7,7 +7,7 @@ use async_channel::Sender; use std::net::SocketAddr; use stratum_common::roles_logic_sv2::{ codec_sv2::{StandardEitherFrame, Sv2Frame}, - parsers::AnyMessage, + parsers_sv2::AnyMessage, }; use tokio::net::TcpStream; @@ -113,7 +113,7 @@ mod tests { use stratum_common::roles_logic_sv2::{ codec_sv2::{StandardEitherFrame, Sv2Frame}, common_messages_sv2::{Protocol, SetupConnection, SetupConnectionSuccess, *}, - parsers::CommonMessages, + parsers_sv2::CommonMessages, }; #[tokio::test] diff --git a/test/integration-tests/lib/sniffer.rs b/test/integration-tests/lib/sniffer.rs index a742d050e5..c32ddfc7b1 100644 --- a/test/integration-tests/lib/sniffer.rs +++ b/test/integration-tests/lib/sniffer.rs @@ -8,7 +8,7 @@ use crate::{ }, }; use std::net::SocketAddr; -use stratum_common::roles_logic_sv2::parsers::AnyMessage; +use stratum_common::roles_logic_sv2::parsers_sv2::AnyMessage; use tokio::{net::TcpStream, select}; /// Allows to intercept messages sent between two roles. diff --git a/test/integration-tests/lib/types.rs b/test/integration-tests/lib/types.rs index e7ff1aa7ab..2f2459b0cc 100644 --- a/test/integration-tests/lib/types.rs +++ b/test/integration-tests/lib/types.rs @@ -1,4 +1,4 @@ -use stratum_common::roles_logic_sv2::{codec_sv2::StandardEitherFrame, parsers::AnyMessage}; +use stratum_common::roles_logic_sv2::{codec_sv2::StandardEitherFrame, parsers_sv2::AnyMessage}; pub type MessageFrame = StandardEitherFrame>; pub type MsgType = u8; diff --git a/test/integration-tests/lib/utils.rs b/test/integration-tests/lib/utils.rs index a6bdbd73f6..6b9a2ca635 100644 --- a/test/integration-tests/lib/utils.rs +++ b/test/integration-tests/lib/utils.rs @@ -20,7 +20,7 @@ use stratum_common::{ framing_sv2::framing::Frame, HandshakeRole, Initiator, Responder, StandardEitherFrame, Sv2Frame, }, - parsers::{ + parsers_sv2::{ message_type_to_name, AnyMessage, CommonMessages, IsSv2Message, JobDeclaration::{ AllocateMiningJobToken, AllocateMiningJobTokenSuccess, DeclareMiningJob, diff --git a/test/integration-tests/tests/jd_integration.rs b/test/integration-tests/tests/jd_integration.rs index abd7b5a6e8..f0c4868c0d 100644 --- a/test/integration-tests/tests/jd_integration.rs +++ b/test/integration-tests/tests/jd_integration.rs @@ -8,7 +8,7 @@ use stratum_common::roles_logic_sv2::{ codec_sv2::binary_sv2::{Seq064K, B032, U256}, common_messages_sv2::*, job_declaration_sv2::{ProvideMissingTransactionsSuccess, PushSolution, *}, - parsers::AnyMessage, + parsers_sv2::AnyMessage, }; // This test verifies that jd-server does not exit when a connected jd-client shuts down. @@ -137,7 +137,7 @@ async fn jds_receive_solution_while_processing_declared_job_test() { let submit_solution_replace = ReplaceMessage::new( MessageDirection::ToUpstream, MESSAGE_TYPE_PROVIDE_MISSING_TRANSACTIONS_SUCCESS, - AnyMessage::JobDeclaration(roles_logic_sv2::parsers::JobDeclaration::PushSolution( + AnyMessage::JobDeclaration(roles_logic_sv2::parsers_sv2::JobDeclaration::PushSolution( PushSolution { ntime: 0, nbits: 0, @@ -218,7 +218,7 @@ async fn jds_wont_exit_upon_receiving_unexpected_txids_in_provide_missing_transa MessageDirection::ToUpstream, MESSAGE_TYPE_PROVIDE_MISSING_TRANSACTIONS_SUCCESS, AnyMessage::JobDeclaration( - roles_logic_sv2::parsers::JobDeclaration::ProvideMissingTransactionsSuccess( + roles_logic_sv2::parsers_sv2::JobDeclaration::ProvideMissingTransactionsSuccess( ProvideMissingTransactionsSuccess { request_id: 1, transaction_list: Seq064K::new(Vec::new()).unwrap(), diff --git a/test/integration-tests/tests/jdc_fallback.rs b/test/integration-tests/tests/jdc_fallback.rs index 30eac6d3f6..033155a503 100644 --- a/test/integration-tests/tests/jdc_fallback.rs +++ b/test/integration-tests/tests/jdc_fallback.rs @@ -6,7 +6,7 @@ use std::convert::TryInto; use stratum_common::roles_logic_sv2::{ common_messages_sv2::*, mining_sv2::{SubmitSharesError, *}, - parsers::{AnyMessage, Mining}, + parsers_sv2::{AnyMessage, Mining}, }; // Tests whether JDC will switch to a new pool after receiving a `SubmitSharesError` message from diff --git a/test/integration-tests/tests/pool_integration.rs b/test/integration-tests/tests/pool_integration.rs index 855e4cdf78..50f92ebb5c 100644 --- a/test/integration-tests/tests/pool_integration.rs +++ b/test/integration-tests/tests/pool_integration.rs @@ -5,7 +5,7 @@ use integration_tests_sv2::{interceptor::MessageDirection, *}; use stratum_common::roles_logic_sv2::{ common_messages_sv2::{Protocol, SetupConnection, *}, mining_sv2::*, - parsers::{AnyMessage, CommonMessages, Mining, TemplateDistribution}, + parsers_sv2::{AnyMessage, CommonMessages, Mining, TemplateDistribution}, template_distribution_sv2::*, }; diff --git a/test/integration-tests/tests/sniffer_integration.rs b/test/integration-tests/tests/sniffer_integration.rs index def396bc9c..e740d4b96f 100644 --- a/test/integration-tests/tests/sniffer_integration.rs +++ b/test/integration-tests/tests/sniffer_integration.rs @@ -6,7 +6,7 @@ use integration_tests_sv2::{ use std::convert::TryInto; use stratum_common::roles_logic_sv2::{ common_messages_sv2::{Protocol, SetupConnection, SetupConnectionSuccess, *}, - parsers::{AnyMessage, CommonMessages}, + parsers_sv2::{AnyMessage, CommonMessages}, template_distribution_sv2::*, }; diff --git a/test/integration-tests/tests/translator_integration.rs b/test/integration-tests/tests/translator_integration.rs index 1c42bd4734..8da0ac01ec 100644 --- a/test/integration-tests/tests/translator_integration.rs +++ b/test/integration-tests/tests/translator_integration.rs @@ -3,7 +3,7 @@ use integration_tests_sv2::{interceptor::MessageDirection, *}; use stratum_common::roles_logic_sv2::{ common_messages_sv2::*, mining_sv2::*, - parsers::{AnyMessage, CommonMessages, Mining}, + parsers_sv2::{AnyMessage, CommonMessages, Mining}, }; // This test runs an sv2 translator between an sv1 mining device and a pool. the connection between From 8276f77287dc28e6bf76b85c2adea58746a37cdf Mon Sep 17 00:00:00 2001 From: plebhash Date: Tue, 8 Jul 2025 18:44:17 -0300 Subject: [PATCH 099/338] cover parsers_sv2 on CI --- .github/workflows/coverage-protocols.yaml | 8 ++++++++ .github/workflows/docs.yaml | 5 +++++ .github/workflows/release-libs.yaml | 5 +++++ .github/workflows/semver-check.yaml | 4 ++++ scripts/coverage-protocols.sh | 1 + 5 files changed, 23 insertions(+) diff --git a/.github/workflows/coverage-protocols.yaml b/.github/workflows/coverage-protocols.yaml index 219c69978b..2404560d07 100644 --- a/.github/workflows/coverage-protocols.yaml +++ b/.github/workflows/coverage-protocols.yaml @@ -93,6 +93,14 @@ jobs: flags: noise_sv2-coverage token: ${{ secrets.CODECOV_TOKEN }} + - name: Upload parsers_sv2-coverage to codecov.io + uses: codecov/codecov-action@v4 + with: + directory: ./protocols/target/tarpaulin-reports/parsers-sv2-coverage + file: ./protocols/target/tarpaulin-reports/parsers-sv2-coverage/cobertura.xml + flags: parsers_sv2-coverage + token: ${{ secrets.CODECOV_TOKEN }} + - name: Upload roles_logic_sv2-coverage to codecov.io uses: codecov/codecov-action@v4 with: diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index b9dcdcaeea..5ca66d708c 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -54,6 +54,11 @@ jobs: cd protocols/v2/channels-sv2 cargo doc + - name: Rust Docs crate parsers_sv2 + run: | + cd protocols/v2/parsers-sv2 + cargo doc + - name: Rust Docs crate framing_sv2 run: | cd protocols/v2/framing-sv2 diff --git a/.github/workflows/release-libs.yaml b/.github/workflows/release-libs.yaml index 368622df3a..7ab570d588 100644 --- a/.github/workflows/release-libs.yaml +++ b/.github/workflows/release-libs.yaml @@ -91,6 +91,11 @@ jobs: run: | ./scripts/release-libs.sh protocols/v2/channels-sv2 + # parsers_sv2 (depends on binary_sv2, framing_sv2, common_messages, mining, template_distribution, job_declaration) + - name: Publish crate parsers_sv2 + run: | + ./scripts/release-libs.sh protocols/v2/parsers-sv2 + # sv1_api (depends on binary_sv2) - name: Publish crate v1 run: | diff --git a/.github/workflows/semver-check.yaml b/.github/workflows/semver-check.yaml index d71ab451b9..e86eabd461 100644 --- a/.github/workflows/semver-check.yaml +++ b/.github/workflows/semver-check.yaml @@ -97,6 +97,10 @@ jobs: working-directory: protocols/v2/channels-sv2 run: cargo semver-checks + - name: Run semver checks for protocols/v2/parsers-sv2 + working-directory: protocols/v2/parsers-sv2 + run: cargo semver-checks + - name: Run semver checks for protocols/v1 working-directory: protocols/v1 run: cargo semver-checks diff --git a/scripts/coverage-protocols.sh b/scripts/coverage-protocols.sh index 1a983d8c6e..92243a56c2 100755 --- a/scripts/coverage-protocols.sh +++ b/scripts/coverage-protocols.sh @@ -24,6 +24,7 @@ crates=( "v2/subprotocols/job-declaration" "v2/sv2-ffi" "v2/roles-logic-sv2" + "v2/parsers-sv2" ) for crate in "${crates[@]}"; do From 54013edebbcc42f3a86096681ae5ee3bc0c1aedc Mon Sep 17 00:00:00 2001 From: plebhash Date: Tue, 8 Jul 2025 19:08:23 -0300 Subject: [PATCH 100/338] update Cargo.lock --- test/integration-tests/Cargo.lock | 717 ++++++++++++++++++------------ 1 file changed, 443 insertions(+), 274 deletions(-) diff --git a/test/integration-tests/Cargo.lock b/test/integration-tests/Cargo.lock index e788b7a68e..da812b8481 100644 --- a/test/integration-tests/Cargo.lock +++ b/test/integration-tests/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" @@ -58,21 +58,21 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -92,9 +92,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -107,44 +107,44 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", - "once_cell", + "once_cell_polyfill", "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arraydeque" @@ -188,18 +188,18 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] name = "async-trait" -version = "0.1.86" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] @@ -210,15 +210,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -226,7 +226,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -251,6 +251,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bech32" version = "0.11.0" @@ -274,9 +280,9 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.5" +version = "0.32.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" +checksum = "ad8929a18b8e33ea6b3c09297b687baaa71fb1b97353243a3f1029fad5c59c5b" dependencies = [ "base58ck", "base64 0.21.7", @@ -354,9 +360,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" dependencies = [ "serde", ] @@ -409,9 +415,9 @@ dependencies = [ [[package]] name = "byte-slice-cast" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "byteorder" @@ -421,24 +427,24 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.12" +version = "1.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" dependencies = [ "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "chacha20" @@ -491,9 +497,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -501,9 +507,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -513,21 +519,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "codec_sv2" @@ -543,9 +549,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "common_messages_sv2" @@ -608,7 +614,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "tiny-keccak", ] @@ -642,6 +648,22 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "corepc-client" version = "0.7.0" @@ -658,9 +680,9 @@ dependencies = [ [[package]] name = "corepc-node" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6cb0b5b9e99b8290eeac6cdccfa4f86821fb49011480d86111d85e26287d128" +checksum = "b2bcc6e09458f052024ec36e4728bd5619e248643da6175876eb3b10ca6d4d86" dependencies = [ "anyhow", "corepc-client", @@ -707,9 +729,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -777,18 +799,18 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -833,9 +855,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -918,7 +940,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] @@ -963,25 +985,25 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -1002,9 +1024,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" -version = "0.4.7" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" dependencies = [ "atomic-waker", "bytes", @@ -1035,15 +1057,15 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.11", + "ahash 0.8.12", "allocator-api2", ] [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "hashlink" @@ -1098,9 +1120,9 @@ checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" [[package]] name = "http" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -1119,12 +1141,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", + "futures-core", "http", "http-body", "pin-project-lite", @@ -1132,9 +1154,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1165,21 +1187,28 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -1199,24 +1228,24 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] name = "indexmap" -version = "2.7.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.4", ] [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] @@ -1237,7 +1266,7 @@ dependencies = [ "minreq", "once_cell", "pool_sv2", - "rand 0.9.0", + "rand 0.9.1", "stratum-common", "sv1_api", "tar", @@ -1247,6 +1276,23 @@ dependencies = [ "translator_sv2", ] +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -1255,9 +1301,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jd_client" @@ -1353,15 +1399,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" dependencies = [ "bitflags", "libc", @@ -1370,15 +1416,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.15" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -1386,9 +1432,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "matchers" @@ -1401,9 +1447,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "minimal-lexical" @@ -1423,7 +1469,7 @@ dependencies = [ "key-utils", "primitive-types", "rand 0.8.5", - "sha2 0.10.8", + "sha2 0.10.9", "stratum-common", "tokio", "tracing", @@ -1466,18 +1512,18 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "minreq" -version = "2.13.2" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0c420feb01b9fb5061f8c8f452534361dd783756dcf38ec45191ce55e7a161" +checksum = "36a8e50e917e18a37d500d27d40b7bc7d127e71c0c94fb2d83f43b4afd308390" dependencies = [ "log", "once_cell", @@ -1490,13 +1536,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -1589,9 +1635,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "opaque-debug" @@ -1617,9 +1669,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parity-scale-codec" -version = "3.7.4" +version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9fde3d0718baf5bc92f577d652001da0f8d54cd03a7974e118d04fc888dc23d" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" dependencies = [ "arrayvec", "bitvec", @@ -1633,21 +1685,21 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.7.4" +version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581c837bb6b9541ce7faa9377c20616e4fb7650f6b0f68bc93c827ee504fb7b3" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -1655,23 +1707,24 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "parsers_sv2" version = "0.1.0" dependencies = [ - "codec_sv2", + "binary_sv2", "common_messages_sv2", + "framing_sv2", "job_declaration_sv2", "mining_sv2", "template_distribution_sv2", @@ -1683,11 +1736,17 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pest" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ "memchr", "thiserror", @@ -1696,9 +1755,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" +checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" dependencies = [ "pest", "pest_generator", @@ -1706,26 +1765,26 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" +checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] name = "pest_meta" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" +checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" dependencies = [ "once_cell", "pest", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -1786,11 +1845,11 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -1806,31 +1865,37 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "radium" version = "0.7.0" @@ -1850,13 +1915,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.22", ] [[package]] @@ -1885,7 +1949,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -1894,14 +1958,14 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.3", ] [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags", ] @@ -1952,15 +2016,14 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -2022,9 +2085,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hex" @@ -2034,9 +2097,9 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustix" -version = "0.38.44" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", @@ -2069,15 +2132,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "scopeguard" @@ -2137,29 +2200,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -2169,9 +2232,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -2191,9 +2254,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -2217,44 +2280,35 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "static_assertions" version = "1.1.0" @@ -2307,15 +2361,36 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.98" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -2324,9 +2399,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -2334,11 +2409,10 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.16.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "cfg-if", "fastrand", "once_cell", "rustix", @@ -2354,32 +2428,31 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -2393,17 +2466,19 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.1" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", "tracing", @@ -2418,14 +2493,14 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -2436,9 +2511,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", @@ -2448,26 +2523,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.23" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tower-service" version = "0.3.3" @@ -2487,20 +2569,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -2568,9 +2650,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "ucd-trie" @@ -2592,9 +2674,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-segmentation" @@ -2653,15 +2735,15 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -2703,13 +2785,48 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2718,7 +2835,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", ] [[package]] @@ -2727,14 +2853,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -2743,62 +2885,110 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" -version = "0.7.1" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] @@ -2825,43 +3015,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09612fda0b63f7cb9e0af7e5916fe5a1f8cdcb066829f10f36883207628a4872" -dependencies = [ - "zerocopy-derive 0.8.22", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.22" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f81d38d7a2ed52d8f034e62c568e111df9bf8aba2f7cf19ddc5bf7bd89d520" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] From cb250ac1feaf847a55b6c20c4915b9c0529b7465 Mon Sep 17 00:00:00 2001 From: plebhash Date: Thu, 10 Jul 2025 18:45:06 -0300 Subject: [PATCH 101/338] bump roles_logic_sv2 MAJOR to 4.0.0 --- common/Cargo.lock | 2 +- common/Cargo.toml | 2 +- protocols/v2/roles-logic-sv2/Cargo.toml | 2 +- roles/Cargo.lock | 2 +- test/integration-tests/Cargo.lock | 2 +- utils/Cargo.lock | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/Cargo.lock b/common/Cargo.lock index acb9cc2c65..66519658df 100644 --- a/common/Cargo.lock +++ b/common/Cargo.lock @@ -949,7 +949,7 @@ dependencies = [ [[package]] name = "roles_logic_sv2" -version = "3.0.0" +version = "4.0.0" dependencies = [ "bitcoin", "chacha20poly1305", diff --git a/common/Cargo.toml b/common/Cargo.toml index 1058706cdc..21dbf8ea3e 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/stratum-mining/stratum" [dependencies] -roles_logic_sv2 = { path = "../protocols/v2/roles-logic-sv2", version = "3.0.0" } +roles_logic_sv2 = { path = "../protocols/v2/roles-logic-sv2", version = "4.0.0" } network_helpers_sv2 = { path = "../roles/roles-utils/network-helpers", version = "4.0.0", features = ["with_buffer_pool"], optional = true } [features] diff --git a/protocols/v2/roles-logic-sv2/Cargo.toml b/protocols/v2/roles-logic-sv2/Cargo.toml index 0c4ca7b8bd..15826acc37 100644 --- a/protocols/v2/roles-logic-sv2/Cargo.toml +++ b/protocols/v2/roles-logic-sv2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "roles_logic_sv2" -version = "3.0.0" +version = "4.0.0" authors = ["The Stratum V2 Developers"] edition = "2018" description = "Common handlers for use within SV2 roles" diff --git a/roles/Cargo.lock b/roles/Cargo.lock index eea43b3e75..1503264be7 100644 --- a/roles/Cargo.lock +++ b/roles/Cargo.lock @@ -2203,7 +2203,7 @@ dependencies = [ [[package]] name = "roles_logic_sv2" -version = "3.0.0" +version = "4.0.0" dependencies = [ "bitcoin", "chacha20poly1305", diff --git a/test/integration-tests/Cargo.lock b/test/integration-tests/Cargo.lock index da812b8481..e2191a37c4 100644 --- a/test/integration-tests/Cargo.lock +++ b/test/integration-tests/Cargo.lock @@ -2030,7 +2030,7 @@ dependencies = [ [[package]] name = "roles_logic_sv2" -version = "3.0.0" +version = "4.0.0" dependencies = [ "bitcoin", "chacha20poly1305", diff --git a/utils/Cargo.lock b/utils/Cargo.lock index d0a47ff27f..88519c07bb 100644 --- a/utils/Cargo.lock +++ b/utils/Cargo.lock @@ -1031,7 +1031,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "roles_logic_sv2" -version = "3.0.0" +version = "4.0.0" dependencies = [ "bitcoin", "chacha20poly1305", From b0b1e76dfeaad5e4ce4ffbe730dba9bb43cf0d62 Mon Sep 17 00:00:00 2001 From: plebhash Date: Thu, 10 Jul 2025 19:23:11 -0300 Subject: [PATCH 102/338] add checks for coinbase outputs sum on JobFactory --- .../v2/channels-sv2/src/server/jobs/factory.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/protocols/v2/channels-sv2/src/server/jobs/factory.rs b/protocols/v2/channels-sv2/src/server/jobs/factory.rs index a2c8ae4703..8f8e681b9d 100644 --- a/protocols/v2/channels-sv2/src/server/jobs/factory.rs +++ b/protocols/v2/channels-sv2/src/server/jobs/factory.rs @@ -75,6 +75,14 @@ impl JobFactory { template: NewTemplate<'a>, additional_coinbase_outputs: Vec, ) -> Result, JobFactoryError> { + let coinbase_outputs_sum = additional_coinbase_outputs + .iter() + .map(|o| o.value.to_sat()) + .sum::(); + if coinbase_outputs_sum != template.coinbase_tx_value_remaining { + return Err(JobFactoryError::InvalidCoinbaseOutputsSum); + } + let job_id = self.job_id_factory.next(); let version = template.version; @@ -145,6 +153,14 @@ impl JobFactory { template: NewTemplate<'a>, additional_coinbase_outputs: Vec, ) -> Result, JobFactoryError> { + let coinbase_outputs_sum = additional_coinbase_outputs + .iter() + .map(|o| o.value.to_sat()) + .sum::(); + if coinbase_outputs_sum != template.coinbase_tx_value_remaining { + return Err(JobFactoryError::InvalidCoinbaseOutputsSum); + } + let job_id = self.job_id_factory.next(); let version = template.version; From 218df6e9d1baf435f2aa822464ab063ad49e93ae Mon Sep 17 00:00:00 2001 From: plebhash Date: Sun, 22 Jun 2025 20:36:09 -0300 Subject: [PATCH 103/338] introduce difficulty modes into integration_test_sv2 --- roles/pool/src/lib/mod.rs | 4 +- .../high_diff_chain/blocks/.lock | 0 .../high_diff_chain/blocks/blk00000.dat | Bin 0 -> 16777216 bytes .../high_diff_chain/blocks/index/000005.ldb | Bin 0 -> 3080200 bytes .../high_diff_chain/blocks/index/000255.ldb | Bin 0 -> 3452584 bytes .../high_diff_chain/blocks/index/000257.ldb | Bin 0 -> 376 bytes .../high_diff_chain/blocks/index/000260.ldb | Bin 0 -> 2946 bytes .../high_diff_chain/blocks/index/000261.log | Bin 0 -> 751 bytes .../high_diff_chain/blocks/index/CURRENT | 1 + .../high_diff_chain/blocks/index/LOCK | 0 .../blocks/index/MANIFEST-000259 | Bin 0 -> 301 bytes .../high_diff_chain/blocks/rev00000.dat | Bin 0 -> 2097152 bytes .../high_diff_chain/blocks/xor.dat | 1 + .../high_diff_chain/chainstate/000289.log | Bin 0 -> 580 bytes .../high_diff_chain/chainstate/000290.ldb | Bin 0 -> 4070295 bytes .../high_diff_chain/chainstate/CURRENT | 1 + .../high_diff_chain/chainstate/LOCK | 0 .../chainstate/MANIFEST-000287 | Bin 0 -> 281 bytes test/integration-tests/lib/mock_roles.rs | 4 +- test/integration-tests/lib/mod.rs | 7 +- .../lib/template_provider.rs | 68 ++++++++++++++++-- test/integration-tests/lib/utils.rs | 24 +++++++ .../integration-tests/tests/jd_integration.rs | 15 ++-- .../tests/jd_provide_missing_transaction.rs | 6 +- .../tests/jd_tproxy_integration.rs | 4 +- .../tests/jdc_block_propogation.rs | 3 +- test/integration-tests/tests/jdc_fallback.rs | 3 +- .../jdc_receives_submit_shares_success.rs | 4 +- .../tests/jds_block_propogation.rs | 3 +- .../tests/pool_integration.rs | 8 +-- .../tests/sniffer_integration.rs | 5 +- test/integration-tests/tests/sv1.rs | 4 +- .../tests/sv2_mining_device.rs | 4 +- .../tests/translator_integration.rs | 4 +- 34 files changed, 133 insertions(+), 40 deletions(-) create mode 100644 test/integration-tests/high_diff_chain/blocks/.lock create mode 100644 test/integration-tests/high_diff_chain/blocks/blk00000.dat create mode 100644 test/integration-tests/high_diff_chain/blocks/index/000005.ldb create mode 100644 test/integration-tests/high_diff_chain/blocks/index/000255.ldb create mode 100644 test/integration-tests/high_diff_chain/blocks/index/000257.ldb create mode 100644 test/integration-tests/high_diff_chain/blocks/index/000260.ldb create mode 100644 test/integration-tests/high_diff_chain/blocks/index/000261.log create mode 100644 test/integration-tests/high_diff_chain/blocks/index/CURRENT create mode 100644 test/integration-tests/high_diff_chain/blocks/index/LOCK create mode 100644 test/integration-tests/high_diff_chain/blocks/index/MANIFEST-000259 create mode 100644 test/integration-tests/high_diff_chain/blocks/rev00000.dat create mode 100644 test/integration-tests/high_diff_chain/blocks/xor.dat create mode 100644 test/integration-tests/high_diff_chain/chainstate/000289.log create mode 100644 test/integration-tests/high_diff_chain/chainstate/000290.ldb create mode 100644 test/integration-tests/high_diff_chain/chainstate/CURRENT create mode 100644 test/integration-tests/high_diff_chain/chainstate/LOCK create mode 100644 test/integration-tests/high_diff_chain/chainstate/MANIFEST-000287 diff --git a/roles/pool/src/lib/mod.rs b/roles/pool/src/lib/mod.rs index e2562930f6..86e8df4e2e 100644 --- a/roles/pool/src/lib/mod.rs +++ b/roles/pool/src/lib/mod.rs @@ -209,10 +209,12 @@ impl PoolSv2 { mod tests { use super::*; use ext_config::{Config, File, FileFormat}; + use integration_tests_sv2::template_provider::DifficultyLevel; #[tokio::test] async fn shutdown_pool() { - let template_provider = integration_tests_sv2::start_template_provider(None); + let template_provider = + integration_tests_sv2::start_template_provider(None, DifficultyLevel::Low); let config_path = "config-examples/pool-config-local-tp-example.toml"; let mut config: PoolConfig = match Config::builder() .add_source(File::new(config_path, FileFormat::Toml)) diff --git a/test/integration-tests/high_diff_chain/blocks/.lock b/test/integration-tests/high_diff_chain/blocks/.lock new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/integration-tests/high_diff_chain/blocks/blk00000.dat b/test/integration-tests/high_diff_chain/blocks/blk00000.dat new file mode 100644 index 0000000000000000000000000000000000000000..63f072fe0e6be83b029cca2fb736d1406d064017 GIT binary patch literal 16777216 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

    KB}UKM-m~W{FW;-H_3eE* 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 z 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

    kw-r8P8AQ+O?=m4C6aSAyg(K`C#6@MVW$=HB<4 zt5unSD@C(P@P-;BVnT*qIH}i3`~i8*I*OooRj#6Y7>!Sd>Q%F;b@S^w z0pH{H6-l!BZ;jL%K47mwuu3ff;x+O%M$px74j1pS7Mx|-nbgN-kk#?fluiS?r{B?e1P-JeDg|WAEi?Ul{+YJk^@GaeMh7P z5#lxZ;oHExMj1N12C|>Ym7n0%;9l3ZT=B{4h9cWJ=0y8+8t&=TxD(RyT9DT?2@7U4 zraPnSp~bMLe4ZDW8u00}T@H-mfWSeCf3Nfcv>aiw>HIo_z37qiOd=@cMyD z=jK4Ud5U=ym@mlN5dhx@WC9(0f%Ka-INISJE&bs+zNCw!ooIzg`)sBUH~34*oT0b- z35YLASfe+mWtI3FQQ@$OEF7zv={h0Uq2()xO>mxKKH7vMV{%>BKEtNLiGo39 z=l6Y6HnblwW#kJsAHjL^Q@o=HV0rUH=*XKPeLo|DUa3Xg@L zbyF{C|N63k_+k_tCu%^0$ULN5r@%6cq}C8tnc-w-x^1MlP$ewWP(}~tOJMB?4-mf8 zsO#VRzMpzxrwiGWFB{?Vs6Cje+1jc+<4H$@Sr4>=J&*lCUNclPT_})#1`c00biwfhkshRiDiuot7*RUmp90Bnfm!b&h>ibnG1TBs6Ss_x^r20QS+hI%U|IAF)#1-p(z>O zBh<@@V>dD{$J2r(liM@n2k$|A(Kwn)ZzgyjBc6n#6^i@pi+UO(7Q(xLKDza6yNLFm zq$^;)@ZgA-1LF%SbnyjY>ETK2?**?nzvEK9_^(wy!OrkSStLFh>X^Oe1o1@%QLnin zMtSt6)iEwZ_=)^$;TvzC=!o-P`v8LGtD?!{ph# z?a7p^#pCr+$t;0M*uCRqHk78Gj7E>Ued?}MmyZJ+KEwD2FP`zAb^S`s_OT2dlfm56 z>_tP(@O~K#&YRWI=mEXInH&KfI`U@7`DQjH8V}lw12K!7&g<5N7=e|m5ZvX54*q3m zC5oeM9owsQpa*#$#PGinq!nKNow_Kp4BJk#HiI!%vKal&fm2kwO}?ASwX zPe^L~abGbjEx)4GD;8%1d(CuXP&6>F*?}&vk?OLA6yKk4A{*CowF~Su6cW^Yz`TYP zI=lu_mv#K`l3mBpR>nVehYQ|A0}pxQ;mYgmhUxbx56T5VUURE7#y5WGqvtuF+DbG& z@pb04u$*a*CFvW)eFiV$pj=~!*IZal0P&g*bz|tN%gQ`Vxsgw^RavB?BamzC51d_g zY}TD5$i?z)7ODgB<&gK7JlZX~$eC{BfimJ(#8;?%Z4~_?1HvCdYT!B>Vd@}!A=jV) zR+mMCF22O+N6{c=td9)1e^S5w@ENI!Fx6+m>5IXK@=lCX5MN$>)0@Pc`@+DYgXUQl z&s_Q|oGY&9*sd2f zUsj-tFB{_V9yficBEnTQPnZ#VQqlV2Y#g~`uJ@&F^In4ZVm&dYD;p5ss_?!dBqBpG zFDh>Ja*K8#{H5EiOh6j!%ruxUlz(N?f$>EUI`{&q%a$tWqYxmCvl8ScGPHJ?m~Ll( zM>{arc)hUmERO)hm%#5l%Npwkdl@Et+E?j11NB&J%@+rKsp^p^N1GP!zPtzXC5Y!a z84$kIs9W9JycyDOPOjr>d~@j=bG4ZS4@2>cD8sdRl0~L;JZ&8QAPkf@FL|bqbbLs> zXsE7a>-d2s*;My2QL*{sOB$|QEDPQhmJx8?%<<>A7f9ZWUet2$`^^*%c%^ik2c2)z zBc3K(hBQ=<^DSr0o0MoGF6{_|;*Ox+^S8sL=FsXV^WW~;rVoq}E7ozxQ+o=oQB%+Q z%Um2FUUMZj1|;q})Zapv*DMki5c5XS8$7Eht$!+U%^6AQ>-{Puys>B-&gpKBPJS0b zqloHuhE#ahXp3xD`h6OF@h{=#uJOS3j??zfRQX`9QGKgV2Fz<OGJL+5;(_ka^jY04DyK!?{r^5(B9dNN*fQi1Cg@kqZZR_hk0n=7wNF5B!|Tzl{G zX3FG?Z|UQ^QVtHkc%mJVIlA%Ix_#@Z6bQspO_y2)+&?M)JKr3dWrzrjFV4`#m*>c$ zm{LRqEQ?@1qS{Jm{y z9RTqqs5tV;c+;9^{K46CC*|KPfv2e#Ga;ANG{Uk6S1(hBaLxbiXF`TYR075qk$cM* z$egx8=JEb24DGz~!$K_UEG2XAEajhrx2dLc4Y;jDAih-jBzk!!Uv)ZpxI0+CmZ)x6 zQ}$PVdGw_2P}Nh-Y)x+n%$G^OCoMquvO{roZ|Afj{Qv|NHa^ktpO=&&fTKiRr@ia~_G}JbS5iRca(Om@ze$5SZdIyjC zHEMLhd36z$CVkeDEEyGvkav;7{Gt~echg=k;eo_m3dId{cn#$J&ASdI1;*yc@Lg7?2i0O)%LvG#&SdgD|MtF!y#ho3D832&5mf#PoEPl!hjZuYYIP*$lhlF&KH!3R$XX(6=h zTl#utiw)(6;JBku3VRA9?ks|tq06U{db9RD6t~@k&FXXcj1*&hb~3+`JZ@eCowqHx zo8Jtsp!7YV+rpCT!>5+Br##S}XZ!Zj1VeZ~jz2`at0%4j?9)LR^tr%%+8w%lx@yup zXp5Q$V>6-6btGBtyKL}R7FFsNX@qNk$tI9bx0qHX4ae_wT$+d@UIntw;?B)x@&|MV z8PJZ-ND{RT8$x`VWxWTOPZL6yPn(E5#98eP)KgB?w*0!t7QQgiJ9d$P<`j+nbbA`) zH7{!(Ofawp>{TZtaf-oxdZ1bTA^?ZzgK9(JN(oPvVuCQlYgV6^1M`|!_ttA5K0W*s zY51WN2c@q^{0}{2+?11Ez887n4|F&}({k_LAAe;SLpP(L18bFO*fT53&g(7CG*hr% zv=;R$J^prD6lW9cHKSxwuYh>X4#nxc_h~ev#l*wM2`G*hW^xCQ(AhCTC05-^#FXDIdDL%6mBDd`rWI$8`N z1yTN1*2I~|1BFB#U)b>TB0t!tt)&7ef%!Dcz4vLxKg_+jk|*33O&3Fi?N^^sV(Smp zTgwTtT3Pb$=DL_IF~onSI*7lUds@yp?r1F%mAzR*Je;?p(ie)qQT4%vf<+FVrZ8d55!`+oJN z^Ku#YdhF6CVJ_a$8Lr^lUEiI8+}=CHLH>)(lo9LTz#h*I0qbMTZWv*-mUIlBO{|z1fsk5RtU^A z$Jx8qvfZGG{tW7&IjTD!#mwcS@%}KiFU{CYuR9rsC^y)uL3!NS?0_s9vPgt&_|@!_Yq1D}^8nP*ze8f2 zhxByM0(1f?!FimT4>>NdJZ>90@;HdskOgRrp*u8gvT5fFeoQo>a8}8@m2ArNBs{`) zzN=5;2d!1Sc}y>{&*<_wbg8uW)X^Cor6lih$992Mnkwk>n)otX zd0$orj2}nJ6R+wmsF*ELZQ}h03>d3|H0VKIv(fi?)0=Oe%-h+rbS+}SHJ`6T$X((a zIyT0prDbMY5+2xVEctFjfO!qwz4aPMecEQhxDIL2e2Q5w*+$VX_9d;sLvNP6qm(&z zEi`A4*LcBf(zlz0b8oO;dRC4}oJ}~>>rK_`$5Q`&O5wx$S1t=upN>!R0OB=|gBR~@ zeVUmavjT7PxzUZLHH=H!o$x3(eelU4{&byAMBm#DBVs{dsdK zq_VGLC#Fn*@{`m-<&zmIcds6IYU>U-?#xjX;DN*)JsS;l?5}}%4cthupn6Y>kSAw4 zd5tU2dyZMmefm!JONqlAs;?lgF&POpe~4V8Af=Mn*{PsjV;b&`ZtN~IqBDlU`gJ2J ztQG7vY{{M(z`RBey1eG?CC2;~q2%GZQX?kIxa`j`T%~}kfw^Nj3a7sgAg>9?F2KDt zNifeq@IDYxz+yfdQyTNO8Hqj9k;~s}X@#Q%drc|PX*DpfDY>^^1KGcP9Gj}Fr*nxD2rK8aA;VNI}UC|6zJ z@p3e)8|*cz@yg{uyyh*!>b>8~_sncIu2^4?H_u_;hmUNIUaS0<@kO?*{?&o4jyNdp zZtC*iYt6R&{ec*ZB2V&k6=7=n>afOhGjso&&{Nu;VSaGj6(qD=0g1a`(?!s=U*Jq_ zz4*Zh#b`?a-kQQ#UaRiuq@+v@|&)4E9Tij zX5-Npg&kD4YB4!T-@Q~f(0!>`%WK_N*pO54T z+qul4x|{%+?|cHT*F=nm@d2yX#NFF^4dk3eBaA5n%p--A=7*w3m}@WE zA9**IrIJ)&4s$Mug@e4Nt8;y z1Q9t-lpxXwzqX%XGPRamj1SdI6E4Bf(O7@M2I;%+1?B^byD#@P?jSzBG#2k0xf#<` zI3Dj+EHuzk7jJ~Dz>EAL_4xeH-TQ6Hw_K8Na|qOjJ_mN+co9bjn*1V}ZY&;hgYEf+ z(h|E7AK(( z@(RyIZV1q+DDXoSNk^zh4p2E5Kk2N{rssitT5Nv6*>9UTgoN*#!$*P0>h6}|OQ#1m zW+$$i1@jUeuRlZTgeiopKzy1U;R3qqgoA|-|5h3SOMdo*Hw1y(1_g04ED zel5OsbRjpn_-B0<*t7kv428ULebUyx-zW;@te|?$QO6&9^p*vKYo0>Qo`S3aQrUQg zpBKx1-4s{pzh=z}^TG8RE0iiPAoUt$Htu^{CxrMkTieJxBno}bqH-Q7f8?)~rxN(@ zNq;bfz3?XQvjq7x=IX28;^&km6HcwiN`XOxsaPo`CKJPCWWT5jUN-9}Y(VOSSGJhI ze0m7Fe0msd&J`(8ebCz}8fmb;{Lf5QzWnax-(#{ar70|s*El1!>2Bg;veA`an_~@J z9bxviyW%7Vuq>q^>|7YX-Bf_|KRTO^wuDWe3;l<#~|5BG-# ziTM5Lmj-L{L0%KB81@ydsnQ$~t|z%qwi*tW9kvc;XPrZq##Pi-eM|WS>^1BRC2PRE zMgcm!=3oNE3nVLCt=Ye~QOz_2rYvN`CH{l^yB$6QDdcr>N3WAU*@3))F3Y37@?^7E=2iz!ULkbvqne9AEM@8Hg<(Uger1%Yr zQ)OK;#f?^;fP$IX$E9)dU&t~F->vo#>%*TG|4{2H(fkhf8W_{Khrqn17`nVB&A{t9 zLBM1CR^0q0uPlqG%P(l7Q4(K0vC2>V7(n0m?TNa2#`R*P+*1DIV>JQ+J~zn(nq6lL zDHT1Z$gT40>poqLU~$C#)i*K*uEQ2>21Faw~c8E zc?I&C?A58;Lp8^ipC9{<_d6}J#8vSgj*PtXseH-qT2bMQ)dTh#>S^zNAYSu0_~hRA zKbcHV3e6cvOb*|mnV$4$jb_2Jd*s(OnvpQX#y$td9Y1m%KL6^~RYU_XR$!dON!-qX z+Tm(o8BquFQeXF2mM_Go)zAch_%uBmCUo>aA$3CW4lbN_|ko#8gmw`R#BHQm-i%+W_J< z=ta)=woV8+@7_^!xE2jhO#W(EhGhBW_xcDoqO!Tv`~#$w&Qm^+Pp_d@)qkt}W6{Fq zI~{{|n=hq~%ltZ&)b6~yzRu-gV7wuuKCLTQ48*59)U%=Me9pqL!foee%T@&1yDIUJ z1lg<6<^$TLw*=@Z6apEbdX4aihnZ_UI$^s}hh|3E@EeC3OD#J%Wv&-i*u`!#8Qa;A z{%6ik0I+&ZFLcyvAm@%Xiqf^|PU02_8I$H!3`aiHMmui1lTm14ldR5OfxISjTYTub z34kr_a|#v=F_I_fnL zzSJ9z_zD+W$i>6pgeEJzagmC7f%Hf8A~_&2T|P*1jL5gB4xI)hf^S0xZO*)G-)@f)$7yjQq6}57#KHdpBmfR%?y1O4AD^@a`++f_U6TxG(;N(P`&0NjYPUA@ef7` z9Z@r%KobhqlPK28-|i%36t_=D%*G`%NB_;M7x0(J0jt*pLRY;8A>bNCSjduaarH)2 zSo-(*7rsYa*NTBi#;gm?cY8>mxQMjPQ}8}#%`2vDmjf>jD&x>mE=Vd(^DZF94gJvKedYl)_+q*qJ z4>2WaF$`ES35q-Ad20KB=Pg|0hoACT9XtlFu+G@M zI%PkeAZpXMoZzwx-}u_5w!0?tG0Nz{+oZiCJ|b}3!99Sh2Nrk9_crbz zKCREe*+aoPbX#P8Azs&bI@nZNE*EmHp+Rhg6h{Y&JBo+IyAmHo;fu|su}r7-tgH|7 zbc{$lH*_8oznmv|yn6(WyRk;9bRcn;LO~B*J}v9Gp*7*9`OLdtTFRzxD}X&T%nknF z;q2wB=tr_3pT=UV!=2OVTG;nr88(yrE!MnsLI1WjK`2{%+EeS;7zQ)!-+n@>saiW= zKD`DVJ`I`cno@M5o&SdE+%JxNG*6V_`4bZsj=%btoM7ebnjhrT6PxvEw(siaBe;l} z_VIyhpVxtTQyt3*WUa7r_*ewb|mpPo3KmN09(pV z+fem>&6UY;f(SnC@+455Fev`(ieRLnAX&qk@5OQU*TiauJ58y`5=pgkCo&fOqNIiY z&VRB6bQc1t6S@?cL0A8C$_AaNDCc~ErF8Asn%e0pv7ON$%V#=xPm8s1UxIwvj7-11 zm*!OmhXKmAO~ykmtJpP`N6X|w9$DVl7X}C;sbHTjX+|^w=F?Ho<c0x*s4PSMcsrW9nOmwHC+e{4_TjznFRPbI zd5_j1KD}Th41_N`6ax3&YfS4{T7PKL(TMFAqrMzHKK&l_W>XpsK_Dkr1G5|CHI}Oy zu;Cf4nli&{tLygaFk7}Kha&tGcG^wiQO~BRa)rTOgWQZ+3(RZ$@4eR)w>Sl?t(Bt3 z;#DUqjHF9{pHIadX!|~?|9hkJuJ5kfgZVU|aX~jn>~=XLv9xMHPp7{aUFT0?WBhNj zay8FQaNI46F;oJHyA%o`=<*trBuwQbQt=-fj|%*pS*lN9&#N3u4uftV95@!cgS=)w zHp1?vK6K#&Ijv2t{*F_CQ-*bVdDWD6g<7uD9=Dn$*lUx(i%}?m?8c3aRzu3Lm z{PBqpVi$>Bj{~>y?60uuT)tt>sc853yZ1??o*jo7x-`-$>U!q%`?#=@%WIFq(!pxD zK1M>Ajv@a&4)L1VML>HkI@DXBt4_FMcIaL0tYJ0^|MiRChpNa-S(vXFRB`#R-+i_2 z_QcHxz<66HJASIvCn6|C4mImz-Hf%m{^b6Y*?nBC-+2-fTqjiYJ6-`+C!DdgA766Y$!<*B|qm%l|R`i<3+m94KS~XhYqiS)Eh*Sv~ zen`MI<2*-7{qtV9Z zfX{{>f;lGigEtd<1?CF{{&#O+eA$F9z9{#`HyvbkCK4p@SfzH~GBmrTxQDk~_XK!7VJZq-VQ?MNsCb`^at^!Df{uy0P=DXs~p2Vp#^pY zJ59yCN{e@W=J0@H(Uz{U&s;0p<~VgvcD#toHyDtze}+ao!6XHhB}Kj zC1}B0RbC}yo0)MOl)pG-_AMKPdCyVrjw_Xp+O7L_kgqeC6TM!clk0zV#9ZV7&R>uu ziv57(FY-2_&@sOW*&`xA&ZzQT)H$+U`RAi0gl@P!*VVrH@1>nZC8qLs=TaKrj$lHJTyLd)Z z+SB~rBvYguTp!~gR~G_SAH##L`WRzfI(-7Z(%S-#ct`Z=s=+~uH~lduoJv`#AAU%J zJo301{r$ul%n&y}LFgd8>pRCmhs))jHdYIUU93L?p9_gyg5>h+ueP2y4mc40`f>}v-kDbvvDK|x_+Wot+&k;6#Zyt zoVFjQd%XWf4)m`mgFSN6p`9OyNA6I_-rM>ZgfEiZg6$)xjlD9f{cQf9m;d_aQ0?WD zrk2$jzAL-y`;{Jrz|A!X3@)xmUTEAdEa6{IZl2kye7A|Oe(`Ts9BBtg3z}Ab|Zz{6h zT^NpmEIz(1>8alYnj4vafcausObc*Nl_hlW1u~~S)g~>g1^4BN!AANzXL=OA%f~kP zp&U_4`~(vsZxCOmm5Aot{oJ(h%epP!4AI~(<{1lb_m&611t+QGgFJ)z;`ns9l?zU8(K~D1W(Oi!Lbr@F3wYL*5zz zcENmLuGNGiQvEa6s;?&60xVHj;QYm#8%Gyd{?Y~=`3q!@j(QaHrzT+?efDaApj^6U ztSBRwV11WIAGgy!d@d+|c_)A6KgFe>(TeRD-KqtPAF2EPk^Fq$eBdvUy!>~^wP%q0 zMb>NrNdEE`A?x1P$7cQ-_oS&ng!lO`KFYeM5oN$KozEfqBgiba)M*Vll?OMECN72N9_ZqMyhh8WB|N^jL3p=#0h?8Z^Q}`hOaB4F`{r&8+nQ9H z?GZbO*C_970`Zy@3Nh%akB$B!i}5}W(Xp7>djBb-x>nnf(}e7iOK|TpU)tUIGsfCf zlfD@f)+i5F!gm$*=Z~VIR?cUT7V(+`TgB6!tpr2nO3J53fYrxRp{qXTHeh>lKD?TA z;C?p!W$?30%B;RXpBo0j(H2}c1BfpvNFz>H^Au0T30Ho4BrH*z=fu-1Vm8exkk`HV zvG8Jz63myd_^V)GeCdZSzW5H72t^B8ZCj9^$k5upvANOoNuT6@@Th1?oFN;;7hg(+ zoBVb}h4LCM?^Epf8)HUYQo@-OMHCi~VogdWmo+e7G@fha1LI5jz2!>+_+0%0{|c8? zEqz(^hgfWVW%w`E37XzGn>2r?hTrAf&6S{EnlqgHes-6mzC1#m9-@|S{Dl}_N~yj3 zfwIh+Qf`(Q%$JO}C(1zh5;?tlZ}$a3ye4q*C!>sB3?`wgWM|Bq^7A~^X2Yt63gqyn zC%IXmxZ`k@wrV>Qt-j4Qco@Jk%*HQZ(B+x5-{@QVLe@#qU)2}V_gl^e^nR$f2$}ab z?jZXd65_5EzvCXrO(|-@Z}_Y)B?UP~bz+?Cq{F^nlmf+Fa+PUMXA;A^saMPd+}~H} zVA4C8JxwLb^Ldy1TneI-~^z5s@xw z1VkF75$O;F>5vwXloSc+Jd4k@-hKAo<2z$KW1Q~?>(?{neHlE=buq8F?|IKnzjc}S zlInH1HO6v``a1--^Edkr#b0-kKY3u(XE42?^Cm;>=n1u}$0}FWKvIVL*1AJ0O9G!3`1!4-z^Fi8=zjrP`xfc@pvmjnB>x~e` zI8sY0Q5yGhb}v7hZ^z+>sqqnY3!f!xzKlmDkLbr)jwhJBRZT^a|)& z&tI0#siVk!FHJ!cOr?wfMBCMG>u6mj<{BB{|;W{dz?0a?c=ox)L7`h@SqWGj`|uw9!7d{QGJ?8oy-gNe>t{Xi-I_%JH!-JOgRQ-V%Q@xF|FQ z>6$C1Mc+jF?7Y2Bh9!vuv%!iTE+GPyrV1NQ?yrLn)-+4T{+@d*HADIaOxGMim#$&S zq5R%0yB3|;C4f%l@AF`F>SV01`V#SqWy_Qpq-!epmxtdLd}fbO@YvaS3I;iU=N@55AUPVoe`81Olu%9;XTx+eP0 z>l%f5_JmoAG(qYxy=6Vgbi7Ba+82&7cs1_$7>9x&U4z-gnU}dpJQSDnXjViYKeg*y zwjXuVuxn;(uf(dWQm+~B-#!Ox#FQCey2b;#bj?WE2sNel?hE*@THLc@C>mtW^#Uou zQOOIh?jPoY_(e`D$ypsk?=)=V(CCVlm1kn5n$ogW+b8C`XsWwsz91eyke~OP4TxMGRU7;8YPQ!;2awWgLKVLG-=}rGojOqit{TKb`F;l=b}MHU7?Xh$>>X0;X%e-+5i*pZ#&0c%`((Z7Ih_tUmAB zg1%LUJ42xMa35E)6ok7&gsyaA!4;n&SXAD0`vz=RL%VVe+ws9^D}G_=51BPMVBGQ7 zH>&`_olhwWbm$sLPMFqspuIoZe8K?RtC}jX|5BAx@^w$)<$krh#H0d9*EILP!fgE+ zBEOa~*ijhaBW4_luV%!jzU6|U*!+>1*ZMVB*OZz3i36f*;-)U|d`|e>-TRuo`1f`1 zJc0h~Jj~@AY#Q25Lp^Frav%Qppqy}oB%cU-@k!Wel-Eumse+)i$yYY7(8e!@FrmHv zE>f0$Fn{NWXfXM|0+vtL-1(gF=IAEw`ObFybcJDxixw5eObTs=WPYrX2*JLo9td|O z&zklf1C)imE){;&Mf7P{x0YS;pFdT9rV&pt%`@@n4H$Rj>1B36aQAbn1G@Bd7njB( z`O2eA>W7GZrw;2IAA>MtOr@H!+F^{DZgav`#TP7KX0PE>-Qj{ZLdfE=U^t5xPidiZtL7j!3lU+XjXo>+l= z_}4E%x<eh%iBk`7XMAp8g<7_^ABP_wu3182AQd6RxKJ}tr%X=9BoIt=Ba zqJMHNPoWH>H~C!W2MLR@F;5>vPxmOv1H)a{oyT3gDsFKSn_YviJI*^bTCFoP8iX33 zgbu=xfv0H&Alw}Xjm%6a{nVIK4M~ewcq_pXbeN5Nnbns$Y zp3Ll}Ma7baSBP`^SVrE{4lwRY302vE;jaJA;tp~SFTt}zx?xb_d}U~1w^{Au8p%@n z;5FPHDr%InTq6i~8reNRi8Bjux@>8e1Lql5U#>L~#PCV~{LU0Lo8vanLI=j3&bKvj zAh@GIcnuxr@E~*fre9{}%fFQRYT^)_snL9;^|9x1Q+05Nu%Tz((-Ba;c;ANmc_bn8 z2iY&)Vc%_&KBTNT{LZ45Nmy^l5u_nMig&gDJC_eR(xev1ISB#h={rA1q|SKKKTyDr z!WCoDp~#Rf?d)kyy&Rq%ox-dm;qAQPso2jB-wN6L6e{_4lQ@v>{r390dR#L`F7{x0 zTQ}urcLmtTeVg;u2j=6fpu@*O?uXiPlG=*8E_*RC5GUA70`uY*_Q4a};VYBIS(9(d zARm`44ZnXa@rJ~MZmv4$Nj%fn+KMNsPReb(M{`tYcG-LH!9I>bCU6sokF$$tfUZ7P zVRPhD@r%OT{zN?XI3}(J`Y#8Lr0?Zcc{YM31wi?9ugR(02&R^QIj)S(gnRVU>e1QU z8o?iP*YXJ17GLAVVK!no#Ef$lflYCv*S4%=!Wo1TGa^>Ok)s zWV1Jm&mGo1&Bw8QW-&D4vLg8)dV0#h6Ns+)IrR;?a>9Wd)Dp$upuoDV=|4YA-`6gu zMod(v@mTdpC6V3E8@_j#6_l0lVb|KJ_w6zNkg2_fq!!ug*8R46nrrp_S3>e&ewk2> ztOv#~&!CH6epRD;Y5Oh8OtYnaFHU0lrH0eO({`r&p7Oo7r5%W0uDe`=zZuGJQiir7 z^hj$Q1s9Q&J4T~n?oCfTQfY6gZv*qoLuFH3VEj@59sC06>q1Dico0dZieS zEXK_Ab>}rqYC7r^?hP@BUySgb;YtSxF$Jmk)Bm_)z}bqwsr>K?c{l$$xfkZ~{98gW zzl4+IWCP)sPdZ0;zW$lma5nskyu}(_ElSkjg%e3kFxn`g4gYx#Z|m0W{e$}O3}~q4 z)Wl)K-BVOM_oePhYomFoWOEID(K1yi#B~%8Qy){`za*) z`eyNx*B6EI=X-x93W>LhBH|!^iG`is_#EXK+|oX@5_PSG>0h48|6*lfuI&QR4hK*5 z5fYeR%7Z<`f$&S*)YP5z&5&~vx}U0gheVJa12ywi%OBfXAZ_DppNqEprJf$cM}u?? zxs})6WY%1zUPgvzSs$%$uf0gnmoT)qnLoR`r|o~_UO{p~p2T!ux+eY3>KaH+Xe7w4 zEAg@=Vqyf*k<|e);gmPahIOzv5xs5&^LDOULIwtRQQ3bvVgkYqu)gUuBQaNEwAw?cYy;iN>xh$?|ZT`^j}Y`v&G zH7|D&avzMl!kz$GV7QyTv$%uggv^qSeM2grEaR6@Y8}z?M0+k3?(xgK5X0AqHTeL- z-3_NCX9gnZ!U=fm%AWEx$cmIBN39&e`=re_+57(6o|=ZozA#t6lyWQEF?qYNGi0n+c09gG0X$9X`PkINY3 z2-l|OsLOC%0iA*UIo9-(m5*_;|6Bx*OF7cv=8&V2eiY!X9}Td=W$e{ zj|v4~1s;7GXWWPQxVZ8^zu&+rF(0Feqk86EM zKt9grU1#c^6ncZ)?+!%otIcNdu$^3Y#0K{dMc;*veR|%QKzv+34hJwFcY0@i9Au8q z*oV>kn))lgq=oXw*yv~PBXMR@HkrcU*6817y#e{SbP)!GgW)#}@`YjQZY0A!M~Jvu zd-lCcs(7s%`XhmFwjlk}-8WM}d>m#e)1B>~hRnGZUoS^RR)$^p>R24{xFPv9vZNBc zfhVo5Odf(W9td}O-^D9mBjN}J+>_e9USL>K)%0Rgu444 zvOs%)oFMtKD+4*z;LNbWRd&FVM(q8P0rO(ZD2|Kg)GQB+{_E#Ke(wK%o+B&sLUax1 zx6D{}Ce-|c%8g&w4@{n^Ehi+EUb0NDOdoEwf!>QX$lKcNSlEGQCYtHX%eQ+jlw?v$ zZ|ym9Kdlci9sB>Oh3IKF{6S#4#)+n!wgr3V_|Hf9Bb`NF7bT6pT-h~B_Xj%jM{tMM zbVP=8m6E?aq#r-|@9XHu1^zeW$qiaZZyls8zE`*N4{#jmrFoLHl9RI2vboGDg<@-} zjtQAOAnW4hD3=!NKWv{vqA3xGzEF4dt|sp5Yu#~Sn4b##b;u2FX8Y?$~)IM$X?%IE`k`Pc># zZ@5Zp=aCz+cz*$05d*)HY^Y zl#Br;OrKJu=wTR%@U>64Fnor^O1apRlZh z+NVEgqLWi2wusUh(;()f%r&Pik<47BdXa~(G2ZtUwH;myCD-d0f7k6F9Q>WT!Cek{ zPJq_&%$eJ01SK4_zO?0^d+k=fmuFi9vbYeI!#6(F^;IxBhpdZ&encQVRl{mJdwkv> zonpbRlBZsZvs4C%y!53_TZ_@5+o=+c2xZ7h|6@OaeE)xY&b_u9h4d5V)DgX&kM331 zK%H5~P7yrcPSSHf_I@+!=$V3h`?;TfPVXmX!UzhBr~d`JkY7uZ~)Rw7bAMK?2%-w zILNx(|FR!ee6=0ZRlTLDA910X&W{$fgzU~?P-J9?%U8WV?X^VZ_g_Ene|(Pr z;E_5EW*j~SQim!$zC>WV7Q9z^P^{7EG>7+moXuwBN=$`K!9gzo^j;VX@i#%UE{YXQ z7C}E9`G*)?bzuFGmk}#5-oJZtfy-$K_Ra6p-|hkN%|EB;-)VvNM?%m(c?Q}KhnwLx zIb2bVzh|rM(9be5R9G0AT8f7k7w-{%&0x9b9VhJn^`pmbs+Ish5;^-k)H zw)NRvN15N$221R3KO0@4VSKR~hOA3i8xpl=MbM~TFZo%>bUX&K(iO9?HnGdsu@C!$ zR@3#;nD`VV5lLJz|JQo`kDvcvpQ}cz5gue-*WMR3XrKH5+7CY}7V%n;=(NArJ?HdP!uz%g+BZE=HTsUsr(yrD+duDri@V!( zfjqT9>!?0O?W@jm%jY~{Vmr~V+z6|ru2CXgks3efPMVYpX+YNHk6h_Beb*1Ahaz>f zQ`JN9{!O|kE}}9A%`~vA@32B?Mb2A04-KA#9A*d7{;UR4y3ewt>* z{&nHXnnN4=_xva2H=UP2{0muxCa&WsufZV4EFGC?f1P>}pQV!FaDl?oBf6MkY{>5o zewKot|CV1k8}?*gZP1oVrR@@9hSn2AoqeN)tp9(EU$!sb>X5*BiEcc=$(bW^Ztzr6 zdU-~Wl3{h|D8mC<|D%BWB978BOmx`|LI__f!_s%|6HL|%P1@#HY-M!Q?3wZWJ^%Rx z{V63dekp)1e$la$eY;PWGS^gaxD&i=sK~>jQpfWXU8dW*fI1q4U)Lmr-Gc0AipMQK zh&XseiCng}GCJf41g#oPEIuNJo--H!J^#spBgX}dU*@2TU#d{ip0+6&qY#R2W((~osKK9{&= zzKr!1o-{Xob*2Gl$<zu!pe(z*bwCEVQyxD^h z6Bf)b52cLO`ssm1utb7k1bS3LU#j1b;M zSavN{PM72#Pjv8Z+0opJ#Er~HG@A`qjF}HSQYSI#RkMhvZqpry`+xBBKksAX-}nxa zQxqBFA>r$@ZAx#wMJ#OXEnWUH9cxp7zgWj8|BDgyUVmI#e;XaUyY1MpqkiiXw4;0+ zfn+;h&qs5zRI@#zh?Bz2Kl*bt zsa(4+%7J7~{aAJ4BTtF{(2rm?2k`yrHMLj3a*D^$l~b@4lC%5?2yR)CYu!b#eBAX~ zO;lxQY*JwyL-LFkv`=uSXKt8Tv}Si|KHO_AlCZ*A`kbzIWw~mbf+A3%9BC+d&C?T>G7J4J^8uU z*+dO`EY9l=3MsR4+_ZJ|)5*zE9axBt)YTOQrX#;VhmM5wmHfn~7fH_bY>P}9@jj%K z3tT)o7spoWlgK*YNt6QVNS{i@;rwB_p?$L&I@7J@-*#)rKWd4LoS)TFb@ZgLAEtqI zWX0g;BOp35Zi?fb*6K;KxKmjXjXkRR{E^s>hcl*Fce7H_r;NeBybSSmNY3DYL-O$d zm4i?5mT{ns;5g^puXYJ;iypW4jMaqj?td%?9}{R%5oY;ep81J>tH*xEMurOZYk?y9 zJzNj$v)g{AfIcn37~_*Tr(!vWALT4hxGtyy>)|Ky9_|x?pI=5RbyCj8HO;JUyyO^_kr+>fHQr=L_6N>=6G{VzRC`tr;>XAk-{du zGoo<}e?8+rI`J!ga-BzCQfdu?+MJKk2X7_Nu)BGcKYRuGpOR#^Trs%B!;$O%j zB<_4Z_SKB!{*r?Qx$sVtpUEO?2>Fu1HnrINQ%`o@fm>a3oXXXaXP^9;tApy*kytd9 zgHabjW0PAijpAS=87iL@8(7!a@8jhF(=`pyrEAz>*wiLa1k^?H?#YW(!)v9#dEbf+ zKi$Zq%Z8Ky+JC(5<&_pTYjJB_+b#Isl;K4hr+Nx5m?~a!@RK)W+jTty>l)7LE>d8+ zW)M1b4W$0EtX3&cLM=_%UuHwAFHS*24hv&-QO`n|Atg_Ho6GYuZf0Rjy+N95-l}~! zb29!bnIcjJy<*8y&kMisjaJ7BSl5(O5|jbaH9x0>pd%lH+yf`@o|(bPq{C&nBx@Fv zKIip&Q_tTWO%w6cKa@3aLHyE-EvGT@IHYw zA^Z{->I{Tmwkh98BE=L@JxWLR@Pg49D1h7Iq(jh{Z&6(HSUr9N;Z^XH6a4(Q^?d~u zgY?OlzMP`Y(p{!I>yal|K4%bq|Bu!8;V@DkAt$e*`MleUeoi^5uiv#O696B1JZEGv z)(z4%9YysRbvv-GP1k-^QI-kIj1q_R4=gF(k*An_x-qwo(1z%megqF7x<=WV0XlrM z-rvuYPR3N6J(QZpEzY;-!@4f556{mpO`l{?n-WPMy+jA`%O5Fm(Rp4VTwjT};j-)!$Smm^$z#FK9Tjcdb zFlaw&zjyRk4?=uK*zG+`w>bQ_b-(2k#b(`tES;1ot(70W@V`v!>=r=(|G#kf|Gn-9 z$z=np)waXKBV^UZzpS~~J$m*V)h1^^YOL7odB@J}`v#5$Af&xm4EFNTRk0zBzj*1y zU}evM*!&BbslV>Ap_m_7*U+98Vgk`Mb`kQ>mCKg)*Y!;0S6Q)3nf_v@dZePV4F5PN z#o+Nkw!e`O=yUhp>1O$ez$Jjub^Z0+I*_X*=e?%Y+U{=;g(Ne3cKqcnNG=-&_Z?U+ zO9WlHtg3H)Z*uy3QloK&c_&XCx2HmUT%@H9N>`$}jkon*#7}$GhAdh3wyjMPbV8xi ztu`iA^-Gc%A#^Ko9lht5{9xa#<4460%s0D3hi``TkG(hXrO}Uot+H>4SO|~J9d0^J z-d^YRunI}td^ro+SDq8%Bs~iq6cPHIgEFq+1m`vFNik5 z6~Oof4?6e-(m&?^=>2jLmdZ_r-JC@9u*R&60HaZy&x^q8Pun3Pp#AyYMN_D>An(b6 zHi4u-YIcQuE_(A@avFa(M$-oaZ87jj5Pp%Q3kSk4^qjHK3o0PU0;8xlzmn@!X2$Av`mh``g;-Mvu`Ome{XBcD6oz$3qyZ;R*d)^L?gZ z|H4byw++m{oIsa<(RYlL`xtsqc^~IBQPNzBvGV6;jThdfZqGH6LT~4tayGM;BsRbs zyofYlYosHKqZ?F#A4OCwVnco7)Aq%K;yc*C6zJw;0`o7h(BWSo{bR!>E;MCzjq>%j z>wbkfyN^lYKE&p1Ajz;l+nc-9YouZQ%CPI&dcSU74T6iO_BH|f3ugq?hewC zk%*sNjdzAajycl{dJ_jprb8T>XnlVoyk47PU_~XUHip#q8yz!%>By(hr6XGtP8(1+ zsb)H8=}1}hb|fV>j3nSR-fN;Dv&r1vPh+*k)-%c+Y;d@G^C3?6+RH11sNeNq5dAEv z$*Si{mkkx7BmGfYfayp|=+Kdn^W+5h_s6hWotbq+JCakh*eKr8{CQSxAtu9YGmDr5 z(vi%#fhaOcjpK?py59%o4@Op{yv_!x8d%Jyek7O)Hq1;ybfkRcOCUPZ9zl_&oWB9N zsdWYKM}zDZJsp-#OpD~jV;49)CDq`6{NUew;r5>LTc7oB`;#4q0|Kg;Iw^^F&8Y;W zP0~kWjmZDyBSGKy|GhsMQuhng2pLJVtJq;|PrVS?CGbs@JuB6FzL1iR)=cRSdS3y( z=Xk*;$pxWtZ+eA$>y+n=v)~_Zqc9r0B6f!lKDA{0r|zeU3WQ(crqJ$e-4BAh<5)er zMf~_#hHyKo_wL2c&Uxz8DP$e5gQb5D#(;2#e04}hBQeZhGI?J8sWH>dl0F^}ui^duMS6sRwYoJ}w$rd{sMD%{MM~VE;ZUi0j?Ts8Gr)B2BW} zxh*k6@h%Q{*u$L}nd2OKWW7|QD0wD4eDXs1KV7=TzZXzJ>H(yiTR?F4a|#Q(^fYg~ zIljs)KLY$$%~i53)a(231}F=^t3N@=xNAY zFexsJvyXI@J(^Nd{>x1^?f9by|@bllE_v^-E!T()XKUKrct!Oh%>*S}2<$bFgZ@=$9cHS>7fh8rB zBFrFOU%Uawz*-O&C6Los$!-+GiJdzR^xmI2+kUW6%g!ba^2q3^RpPu1;}2cmDZKho zrMf^OBK9g3oY!zO=BEP7YkopkUc;ii(?g}3)P>xa|5*`km@-vC>P>?JW_IHYCjIT_ zKgp_SoohYP)!HuY20z^uC-v|bzGo+A{iPbpjsu=FCLEYwN|r?Ufbk0+bnpwL-poQ( zOdA^Fhr<&^Qr@RB7|mfD$YY4NK5aVqB_mc&+p$Zfq@P^@^iq3tj0fJ;v{;xQv zFWKG%gZ9PCun3<-^)Jp>`nbd0C}Y?9;z5W~>F^~AD}#z<4&AnYApBCG0`NTfB6RT! z0p{^TBFFo1tXtX4sPp>Md~>Lk6<6xBPO%{kr=QUdMO8MNL`DQY@HpI%F>D z-V1|!_NyfvPApgWC8MdfwXyKcU zp?*fr1mc&n{U{u}pCe;k;m!3(Y5PiIXTsR*(x8i2B9Ip`(hoKW!2I%^qm2&;zhqIi zL5F{V^vmWe(3z*9Y#RO%rib6GKFiUfA~PgFF#WYgct-aWq+6FOkektInxmtC1YvP{ zUyRYy?H?`mz4MYr?@$u|?k;=`_AfM+!!$tr3t5EYo$Z%}^fNWfR-6bTrfC^gFg%Lg zNZ$VTUQqk}iNC&CR;DHlh+mMu$dOM5e_HK&P6Qi|$bTI*K3GbmOW;t;MwRgc?T=Lq zm|q+pybA=vFB+UFcfOyAF;6X}pJCd}z3^B8$(qNwFw07|)VkK=Y5xnPKoIWkD{?x2 zql-#I3uu#*zDMn@V+H^CzRDUI9{Xm);$odwJH$7C(}MwmJG%&H=iXwsY6ko>j3f1ojStMQHO%e!xA}{f0neG-?8_*p^xyAB)LC#V`3J*7KW)SoDaF~w`zedvK+L3#Lb%OJ z?cFqs$XowHP)r|!MA$YT!}!F#AWb@tCefFRr|Nhgn=%>Z`|vx}REU3RqcH};FZ7&o z(B)qygqJ!tKf~;{%u&QPa?l*Ug8!YVQBhXX(g+(|2GZSCqS}0wJs4O)n?Hr968BDN zl6H^MW!f%&+~>Xs(3Bh*2KyJDd%6R_^06)G@Gp>hbF$v?F-3v<{(P%xPIpmw^h#H3 zde)q-wQH@*J!lpYDSd(igI&zsuV?KYtLLL-cNq#Z-Qzy<%jG^I`t)ypjs$ z<4GX?C2q>%&e#10;HjsDVp%m*G2a9wDR#x0Iw@brKd^{BLYT>+sQ%Uugq7JTl~?!c4GzRBTsG+W zTid|6D-Vt{1cJMtQ)bZRo0t6HF;OcahuhRsAC+10hLmF-A z>F>^-@1<&6$b?_~ox?^wWnKWxzu-WZe?e}IM8nyI!I+h>YQ5)gORp1PQo1vq#d5SD zXUz`sFBhA0wW8*}Z(wNfe@LEsa7%<%yU*v$Sgl6sQ=c8?;yb=% zPs>vOEYD#iVrHMSB{0n&9UFVJ#LT=aPr&9hR^E!9K7Ud+}wt#i4q3pm(tD+X<+>x>nG zr+oe{{)(hVif%rGHKvQ}63j0Xo$S%T_@y1X_$6W5&zoScHuL9R_nz&wC{bjUm`-ZQR^ZSSB*bz&nm56m8*wRrT{6Y_d4UAvN zp^IOL$H^n4{m*4f)X|yxE@dP>qK2ddHCD}GmFXE%gZO0(UC|?v!_=>4!>`eOjwkb@ z6&8C&ps_yk!DLdOE*(lPm|vpEEEa(AixPD33nU*Kl^o#+rU}$Ds+;Rgyq>Q_Ly#|D zYDJKi|9}!60^*mv06`&L-mbG8mte+>#P_2+^rv|{FQwB1(=TGq=`sSW!2F^$nEL~W zf1yC2hpv3At?KQ!SK~_ujY~-xg7#ZU+*`0htA4YY^($-KhoF3nh^M&DRWeh$ujJx0 zoC0;X{i5X@Y!DB@$6_m#(7F&?ek<@Cwr&64!1A#`=*Y()^_TVI^d1IrcfBc7t7(+u z?xoO1I!m{OA}ly~-b+MKK8BK*u2h@6Kjn66C!Iu=o0Y}Dw7uQ?Vt;E__^aP8^A~-g zzw>;ZR=)25$;UJ}SMGfMB{d-p*GIJz^G%NBo=~Q7c8FN_xf9NrwbhE$tJ}WC!bi&R z8J6+>Sn}nB1K~2dCu2_uZ>n1*l}gk1CkLN!l7RVzzOkwV7{8D}7r(Tw@6$b-Fg84k ztM4hyeAY9KHEx4bV#T%(bH;Le4(Jh!oyQmR&$sF8& z;??>I%rBAo{$#-TMIO5N<+Z53y0i>;*8U&4<@3#leuF8a4%l&OlJ}n3=th9}<naOY8i*}Opy_$wOpXlM3WN9sM*El2 zPua+bv3tsGWDBxL*-1%>ArWq=)u)kf%s%GyF@gETIc0nr7{6TJd48F~L;IbnP+0do zo0EU#x5^xaU>JkO!1w43|BFIT5Wnn@&gSsY6N)lR${^vI;C76fr^!xr^T2#z`Z6uG zWqNK0<`=5R_YHvY3o>-^OFRBk*H+oj`{ppoE4zWdZ+7vx$0Fr1>odZp81X^;Vk&oE zI@wI4(UGwCVJv;*bX^M7Iag?|ZL|LkHe)hN=`@&M!ue$Y-gl(|UHmd~_RWY&*d3|f z{%uOc9Dbk?+DYV#vfaRC)hOxPInvVtm}9bCRO|=&e^xEF_0h!UN0!H3SuN44e$VO_ z<#ZT)oi1+{!gO5g*6`Dat?a6d7IWR2;R0!+eC zxI_I^0*N!CA^F%5b_lS1Yymp*F-YH{^J;JEQ+w%>cK zDTDYWwwO*Fei>KTe-lZZ$T#Ue96mezD+H2WSZ@=ADS@#SKQO=04Xrx>;TN(9j62`A zczI0!Sz^XbEzGN4xWD_XyQ$_^Yj3-(szUnc(8QT6N(Pe4#?RUS!yVS0 z#T{fm(=Rlc^M_SsBCA|g`VlK%i!v$~RbgHpxOY~?`5}RDXTG5#_?9DbXb_$pM{ViX zwX;%u*Mh@+%}xK~HZ=xsA5<{z?0M@Xf#8mw^9VZnnIQMa%2Y}d3r?x$(+s}6?5CN> z`8n|U6HN0$sQ1Cli`(-U7e<@wS%Sf;gm68^8sAHu3-+Yfu_L0EkiQGRh&3*9u^H zjUjaCX-K_!#JXR1imJ(vovE~4ZF<1Vt9&or#k=*i;MC6JBk1$zZdY{5Q>78^utw*# z$$jGYsGgWUAk8eTb}z6nM@oZ+1+1sFYcAdc(bM(_Tz9_SoS@O;@WSYWr0k5i@qW&$ z53bEdOausz&$r*N_T0{GNx;%LyTqkG=`!2qe*T-)`(F9*A9k}wCAfvmLCx$ZufBon z&3QY2?22hj_r<_)FLVk;jzEDKTjOK~5V$@S#0y z>L!pt%?xWBP2?SY)`U*Z)Etn}s4@_aZ4B1abMo1Z!1VM6bm(bFKa<~s^AH@GL^VbJ z*GA%uU9#42NnPy|{H)yZ1Yvfd{k-+;@QY&{z4S!`+p}NjSVHwb5*)=)(6rvd{&cND zzTaF0)-|*nC5=FIja>vUbmcYO1yZ^iI*tpMN7mI+=h8h?6h$a5Uqgk5_Snw7kxH_(DH`^BTlMSb+1(b)l;s zaC1<}ILkN5w_(CfQf^!Pd$&56flgxnbVd^)2aZ?E%iFaBs^Wu!K=@V18kep{xVO zFI>>UFOWGoC`u+wC7UT-u?>{e;y!Ys=@S>`N(5d~_f1T@7(n}?ZEVgg1&?l6%ED%f z!y{OHUCyVIF@1)1&ke(uYPlvnc)rQ!h(2wTboE5_bR84u*^?tr6Hj&SOM=&;jCK;-(bu>^UHat_dpB&y^gf z^GXrfX+a&H*n7|Rtf6RfX{>5oZTR*a5V0CM0qecG>r-0(ohQN7t=54bcapdU@jQ$; z5#Q>FA7+7djc%}jJ1|{?1Rc5tQir0~>|5)0@+G3cUT#Po3NP#rsh1XUuCOW*#MnFm z=^7q%VV8(Q)&?GOa=(CeR#onl%reTL2m3#oNoP2)PG))_eGXDuroie@dUv)C1)0y} zfbe>8nZF|{AgTz9=Q|w}yfV+DlYYF1jCi$4R3P6h?V9U+1-Ip{B5!cug?)*^-!iO1q60mO`kFtOV;+wZASMU6Mrgdvp17@Bsrq67mxm@T9(UfR^rZfq( zua$X&ZiYd)yY@uo9#D&KYF4%1nX&Y;hT0KdYYcI9lGiPvh#-Ody9!g=gNHwl#_lU z0&m_iX7w%_C2}Ns-`;bHp^gye;j(^y;c{#g^*Qyx%*Y)pZq0j(yf`QzdTB#u60B=D z`@gpU(KVQ*jd#8tV0oQ$x`g$cZ(OO?TJBZ0aDVkHY8sfiUb%Yzdz2tO?Z)s@@54ki zTp5phBVqf4{xXNNk_|)gM4XoEUZ3TM@)nT(ntg>QK=gE{ju&+0HGMC0QQr2Aw-#a) zbY%Ei)e58$St0K$TfWY7XSzLqHZAzeHhoTX`HD!L<(<0UKn$4xD}AF)z>&6sQyGZsW7WJuHBm;=AEs~fn&aNp zr~Z?CXESB7N&>bhBt_x!xw0gq>Z!K(ucCP_tq&pnHA(?W!1zTRI`{>0K4A3fTd@Xt zu2sO-GRqyeO{Oda*}S=Ii=XZxIWxEC15^mCE1Y?Da49X(r^|OY7Mw;fq8D(@*iD#s z@%@X8+tMI;O)Ew%5Ps3%oWJvV4P6XJJYOX<=`y*QlUQfC3%_;;Y;7!Unh7Tbk~t`^ z8Kc)^q0Mox;k$u#%puYtCa-iq4g09YP!zAD9&AKRGY-ycn4M5BfaNum(2>_b<}9DEbaIt-E^&p+ssKdCE4PI_1tJ%$o-kyIyrIn>KFMWcf%AbZHGp*MW5IRj_ltb zoqLdKIjz>eGI4|(|2^OQ@??t#7{9nf2fsk-%}OGzLsbuFwm+LbFYdURyfkv+qr{T2 zL~Zu#DGUYiOQ`l$id~Vx{h-Rt*cO&+gycm8o)S|TtS(Yr z&r5@Gr$a1B3JiDncOG~4-iJyN7|Z){qAgv;on=ggy?RPnu&E@GJP1}2w|zp|MA!IF zKU*|v(P^o1(2Zipk^QEM3x0G-{kvMBm`T40jJwh-BspNXGrY67gXDyjJYBQTaV1_p zb=}R6DRGIO(eqMqv9D=VR;M%ZR}uHB4nP8vb7) zTMoJ^ko}ILvj_<8C=dvsLr+6;LK*xoHgfQf?u9-c3oWcUQAgf>-zB>#@vQ|jjpp{e zZ9!Y{SYXxeu3fT-`L<=b3D$THy#UGOOGSSZ_~1iJ8FK5t=WUUNS1f?&X#r=sJDU?i z=In|Q(L`S*_45d9lkkePv+*kW*S1M2SCKGISl*`x{r#>8VmF4^cGPln4;ha&Ke0Yq zYxg5{@1>gdQ;OSqLy6tb{C8f^yx{N+keo1!@(?=uYao5Y*i^n<*5{Go+U#70Oy(sc zdmovVBSL>8vMeGMs(^H0S7n^3KHQ=_>QNzEfs68T&c=yMC)WJZQ%hK*+PEZI#31mz zpw)UgAbOfCg7MDw4MTh!|8M*i-ioswYf8IwERotVvXF0G-J3j78jllInLs}7QqNG% zb&&Ui=b$h%%*M+qPwK}mB`vAMRn?Z_i1p)5Qjh+g^LFf89suU!M((VSgW%5P`|7GP zej^qf;_k&NM=@a={4>kvTa-%O8_$cLfN%%vCBcT?r%ZXIbvl%n{}Z=PXDe3&EY6iXSlCvE5nRLHa$CuCHC< z;X@5+Sria{^!b{bF-Q!)i+Li*Tyq$9n;$HRQZ+e5>if>JC0sTfeb`0YFQX+4itc7O z8VfzQ$!EfX;7$qt5fI$jMNB|fejuzns@47o-!qFJ_D86&qqxGM@Bvm-z0`AdLn01P zeh?Hbd4k+l^-RC>iQ8OL*Mmu-&*=H@iq~2Ej>XI11ZeZY`9ai^??b@ygICazA3*f< z&O|4Q(!|0exQ+re0m9{a%=dxz=w=OjI1j$8-|A@;@4-)=S7!sXrAc2U2FnhpsPtC% zo_(B%m+#BDmXgbO0`YN4qP4*E^e%MiX%3UG=Zv*5&s63cMvSGDY~VJx8C=3#tay3? z!*B1!N?OzP&r6V;d(HC1<@Hzoa=299anE!sTs+y(ksp>WvD^?n4Z8IQn4Ye?vw9j* zcL&X)64zS$PHLD%1!7+JYcZttWm%(~^lwWWNh@Rf2#Jsb0cZiScY8C&fIB2nvNhku#*_oHaQOWUm z@IfPi2A^MB4+wWb2X=@3tRgh!_xnUYL{a_ei65jgBRt7wROr6KsYVPH1mg|~tzHTk z?rQHW?jSzSG^ifdoE6zAe|M-Pmdzo%GxHMrPt^~6BpF-2+xxhVE{cdZ7G{VjMClL* zx>v%{_Q@6W&X$dfo(brdVdX6+fpIs6cA5hOcUhF!(Br%_SQ4;Il+r%?S9f z&RiZHT09pxCagQZ)zjNly8UQy6IZb;*UXPsZm6YUUbFv+vOZw9n5~N^)IhQZ>zapg z4|aj*8nTGFJFBN5{T?txXmnEQuvkawN78SI_1lr#1*ixK9~hRsbC87t>1p-r(s!F& zLnM(fRaP8R0=jSIuDF8vH&1@nx2y$I{H1_DnL3#3rXpk z%mbc+UHN5$d*8O~U?h7rc2h#k3z$UJ^Ap-vpni{QSF>;vD-Y|@d2!jnzS^mkuHJr>7uMRRKW7- z;yas9L+T>%wN9TCW(9nNd@Zpar^(4V{nW}@cVLG}KJO_4`I0cFi9l6%!GZSN%ZgWQ zLn#f6dqhJzA>TNwFXK=-U!T{aK=SDlF~fgM?0GV#`#HlCtm#H%b10 zao75!tsV&Oeoj?D*ZEH!9g?sycBb(!3#>d}H#1+nJT&&JI-gQ!1?d`Z4&ul7 z89jn~j9<{AlFaQ9I0c$fbU4sN2x(1Th_WW{UXERq~>)DYty{0v9qfv5bf2##|65GYGRCEc>Yr<4^^MK_w$;k{Ct?g&ov$=aNy6rwtZF9Gq3l;QKK*Bg}_60n#Tu|vIsLa~II3~$Q& z45@jvyFL7wsKET9Emg+>gkLBSuAn2Yf%Fq928MQq!qDMgAoFWp_zU_iFxET7lZi?3s;}q6 zUR3?|>_6!@tni|`J%@+W1+zkH`Sy)8%gyoINB8v@Q@AcEGF#vsMz}J0-N_4;ApYfQ z4FQOM!7LTOv;FFj^PgAjPti;Kb+4W=B#2=QqdVa|5vwe|PN<(EiXq|!@e4matK}Dr zd-LS^a;~2d{_vI2(s+GrrU>elRsVC-7-?_<=9dUR4Lut77cxiqub*m|e8DI%H*A-9bN^j1QF% zggY<$k5n&(*L5PX(8L!};JGLGB8>QHO@mmdVcd{if+j3= zGYir+Y~dw7j5p3InF|?o;}Y9jpA*sIUVOJu?O%~XTK2|!@di?d$|27JrfW)|L)Sq1 zYr-#Y*asF+Bdtx6E^rJ8Pua{>-$=(IX=1Sair@w5nm>)EOFG=!jrR%VX&DBW8D$ZX zL*?UiEclGX$2pDs&CbEP#);{MABe8e;N-dUeGXn`zkhu|Smbccgo$2yNGm7#L(Kep z@!)E-y?Ue#$TxG>It*yVPQUf?^IlJt{lhSE&##t54qXvhIqCY-cTcrONS}j`6u^7n zhM~hZLv+pAyJ7==*3HHiS4Y*hvdP2_19*rm3x0GKSoYzFc1*0*t1%k@F)W7 z8eX%gw?K4_PpKSq)B_;7YUVisfSB>BSGlH#Z7bk7yi=YUJf4J4P{rX;+xeGZVk<|RzpNo67#8`48l6Iqmy5zNBld93jYzo8c@ zWd|T#BOPAKELylNem*e3{BSErI=#9jyvFdQmuRag)#Awu`y!C8xv$qIkF-eA2u#-` zK$or=w$J2rzb6+yOOHxN4g)v~d2K&zInpyIQRm)roh5~OMLPlxsbvX(u zl**0gH)>D*4|i`FRaGCYebXf%B}hp~gGiS_cS$$W-5?;{5|RQ+NJ%%+As`LX4N6G2 zGy;O}=D62+$2rgQyzf|JoaY1k(;D3OK=)o=dtbk6&iS8f>;vTUJ0aU;HtY=_uCj!?GIE4+a0dn)W4l{ZIaco(m z1(#cIQ|S8(A0_)lFYIv+{|`@zAzULtXCqZdyN+6bzDb4Bmmtzvq!h8sHX=))>GMm+ z=Y1(3glmq*mjuJu6G1-THV4LXb6&p>=&Lm2avYghgSp|IUxG}Z+@3jFj$F<5tC$)LtwD0LUD>?^wW?(kaJjh$wsLdQ zm?qoAQzPSg4I{*qmX98-kuzUkGo+I6mFpmMK>S5;`5Ot>elcn_-rEOoGB^*u*QK@4 zZFt1UnIVZ?W)7o%2K>b)GZvh`B*KQj0R4dUMLGBbLFa_lL5FG;+)ZvMc{31f8G+B-m>w`w6y}IYe zr)``bIXjD1%;spdOjMN!*((XjFH)@Rj+|e|DZu3yC)mm_bqrnQ=uOWbqcMovk=0;N zA4EuzVP4qTBIH&;c+iGZn9!ZHd^RHAj{EfqiNI@z>K|8^yJVqQC=RCbW&<-7_>lY( z@1?_9YN*@;F297sMt%Y2WxuTPOL+ZJzf|A~GC2r__w8y>8LF0E?QR+jn!1_O_QR-g z3??@4;G&`I%Msl-X_<7n3N>86)J5nCRS|ySWev$MU4%%L^A+2RVDd{EB^7MUl>q0@ zLP-6%vDS>#N~K~lRYVONSd2}jN|yDQS%{LOZk_`-9rH8Q;`v7en*9$JTwFu}4R{ut z%b$B_3S+27?l53B?*SZ%dwmFoBgw+AZ|%G+P?uFU<@Icc;G|Q;aILSTi)2RAV?}-c zKrCCqGWmp zYExB|N~7P4-N(5e&dns@e_#5m@eryzx`;$9aJu__Yjp>34Nbn;%4K*}(fB=yFZBV2 z+q>&x6NmYiA`z4har_Y7eQ^Ghk7z-p@b|O6Z+KLO)D{N!=mu|ud)&6E3;n|pqbfjm z=Do3CbVtvQ2wPm^kw>h2j7qSfkJ$b!;1z1Y@(VJ!uDExNUd7rjkmtVHuq-~E;_eb- z;KpT@e~h()vH~C9u>|+JHMOv&VnC|0dK1bu98-F&;JC&UHn;}ZUz0cHC9L^T{5}dx z;UQiIQMo2pUk|5^#Y4D+tZAyV#-w)`T zFKt59ne*YTX?rPy+M^2Rp2j$h==P$nl?Q$vQm=VBYf5^@g_#rGy>He+DE{-+U>j<_ zV;YXWYFwQN>*?wtv|fW~nJNyhUgHN_^_uTiI$e~D8pbi?S*+D{d4use=4BrtO0z5UxqZa&z~{k3!)1wA43j)%b`Y?wO`v zVX^#Fh>=wnMa1?HP_GG3Nd?C>2C&67_?9tD1`6zn_#f2|3L5)~&FcrflyJYu3*?jN zt3dqao(rq1teERij^|slfK$p>^XUqzYcgN4MFKsTFo(LgRG|J+xRD(I#$RHl;%`0I zEM`AgS>9>wsfr(dqKo_9lq#UYhSnBquM_g*xV} zZ|B-!lA%4vZfS|Lc#`&DoPf>AKpU#N7ucLH!03(wX#zI-P{2Nih#-bd(xe?jzU<4S zb5tzOeW!pwlz09lFL<8(?S*g+0ck5i>90v-b=K`DOb-_dKXNwbv{ELi74X&nfX(PfaCG)=A@%oTpuNF2O@%Bvvc!XNkkBy z&OEEP*9pdNHTAPlGxJT9ajYR;mS5leXw(u`Oe)yz*~;**K0P+zNd}ImId45r<1RlY z@cXF#w{zv`qmhKIfhm=N{nzsj10^DD=A#hZ3701#*W1Baa*91gS6x_LOe!jl;yfgI ze{G=~WZ=hNL<`j&%7dPCFuIesi-s+pe*a5FP6);1;r6>vO_4_iFl ze02XVC)cj@|r4(%RKY?@gTBU^QIgNf|IYK|C zEEvjC+B>~l*)01qG$fe@93XwD`vw`Z-zLZ0vI!h_0~Jr*+6tb@0r}*LEUby$E)OexzwOdu%2?Dl#6>|2Ib$f$&2P-1&fxTYEkL zIQMA84u@X4LM&-*o&F<1$?a%Y2CpKKszmyaNVCYz^PviR3P0sB*&zC~V|2U<*$&B> zkJGdBak5B`LYfwCo-A?){G|{H0gS(36%*e2bC1>*SmgA{@6Y#gtzJ%EXccez6+Nry z?w}f-jS{4V=x)&WT84&kpObl^hhd#zgKJLAqzsR?j-hQBqvA}8`%WiRccFqS6<~DN zuHFV4^P9lFAbkD!6Pw_@y!k-6mA^%50l#VV+x;u!GMLI=@Mc1CGcm7H2X7_*DFJ%7 zDtg7Q7pBWuAL&RvqzTbqJF}VW+mb?av#R2e8JOH`g;a2B>oow^NN!vzVvFEn`l_XM zZ+mDQSyjs`WoD6 z(@!YZ6tlh#2FEoku)#GzZkC-3-N^k)8HiYIkT4kouRRjYi<^fvoo-|HFduRsOYeN) z^L}@f@)udXoUg`aZPiSedCRkNcL|Ne)~N_>A1wsnn&ei0aJgCW*5+oQUW0VvU)RI_ z&dwFtfbI3WB{6lD+#Lq|U(H}?#9eXDU^W@_=KaZZ`D?m7DqOE^lqU2H@$t?=iYrgT!aBkx=W)JhAp0s zAF*-CM3j)Q)MQWA7dbm`R#SKzW*)41EC{Pp82KPXp&tL^!h~4ebM-RuwW;=pMCMHgv|T9IL{-IBgX5Y3 z*x(wVA3*0Uk*_fpQ3hYsSQzxqj3xdmM(?*m_rp zcnDexBY(9w>&@}m+9+Pm&f^FA0na7X!Eg-)QU+}F1Audnp_D;qJh==}O=S^}ds=37 zeZJ}}q>~);WSx?s&qDm=v%b8RcM3# znj0$xSC_qpt-35W+rk^;htdP`Z)$vh$CV0GWQv;Ld6*r)3iAGh2&v147~d5=+%f(o zT2XR=DO|u`5XW)!2r0A;%g#5voKdiZ7+ROLi`uRNSC=(_t-36wv3kL%P6NB9+krrh zrn#e&bOtW7fmM*j+;VCrBsZ&=Q?boO9KuF^0XPy-!f}|6^YX16 zUL^m5N&XKz+0D{7K}UVR;GXNkLpajxR{{qIQM`CjJW8!!<0C~|%QW&xEfOx`w0qzE zUcJpO1M-Wc6v%z2TCl~Da8vh>bCpq-y~*zIz|myb>mDX znRH5-FCxDhzF?0wB){dLf2G+ezApI!)yy@yDhtYy%x|k)!Es~=Y;j}}yD!7Lg+lc< zpJ*e$j`?W5)=23lX-QElYN=&L2uEVMwY*NecY2bA>`mdG-J0ETtQZgPaI`PVcXcMg zDLan<<;XC^f^cvgIRP6S3Dn1Y^04!e?st9k9TYh`3~-X#e(*?M&wMBIdq&L5JP1do zV=M6Kn##GxUCHipIIrU@ZTD)+Q0SJ%I!6y7g^d!-037MIVh4sJt&r+&eSPdW%XtOa zUCO;C+pp85tgrgBWxuk~=e6y-0WGf`ApTM`I^Y^I_~0YuMZ6b#4#Ncwb9diyf=*ZX z5MRPec%BPBpgtymjtS0R&S1k|fcn@&UV$CNYO0k|M>YTZO0|rA?&GhAiAx>zKEGkU zIrsSWx8L3pm+f}Rem_+(p{=*YLg+fNr{bMK5e02i^MfNrsJ~1FDv5)sQ*2Y}-`e^Z zkY89V6#_(G-N{3&FYu~>PqS7(Y5IBxd0B zrYkk<(!MM1TiYB>)IG-}XnsM6o(EMQleb%et^6W3hiyOhB%)Ge?Chjmd9H^4!B>sz zv*gac2Uv+uAo=CZtfJn7&C`hS%aD!{E!B|;pMWX1&F@_$Ti1~X8ns=$(EMUbVg3bN zesP73`~vj-tdQ#^rx@(Wfm=B@4f0sZDLHT%rlg~&ZcLRV{| z!b{RSR=@KN%F5mu6T{;$K)B{3>5+OX%MpLotjODp*g#ARmZ!2x&*b~I@G8YEdoCXG z0e!y=ias!0Q=|Ul*7lo$eGYFD_%7*Hj`gco7ZhugY*^iET)q)r;OrJW7G8M|(VgHc zqm4wv?E<+9%JT8H3zQ#I%EQv$h52z@pWHC{OGEvkx?}Iup#q~j_hM|=$j!i5gf+JaKqMOLNnyDb6zX61BTFg#6xQ~?`x zS)lL7qx+Fp@+#osNc0%%w<7^XIXSkcl~2na#wZ>huK?<@5sDaK>au)xySKLQ2k`VL`awBi#79aD43=;ARr%u? zN=0c#He3G0o}dskhK`qxPvFe&B8dE*~o!!0B%6*6R*l*GnvfT0qVsd3x4PUVa*j zvoT+KhB4$p{ow#IM0Zqz^8_|-#(^e`2^LPl_2`eMJ#)N?yz1}iAm{yAM88J_=&pIz z4xH{RZ@uomTC;xKGU}IJvQ-b8Qn3ouSESR_4L4qA33N8qP%kb zGZaxZ4^@+_P%@XtP2ZB=)DA~}h3XD@jSmf+?nZ8{?tuFA5Sgu#%QM}qd{1dR;o(jOKJ1PrEg49C8Z-Gz?R%YN z)=IwU*FOt)^%dFPS|_E6$kUF#boIX?cyk2I4NGI)0au?chpqaw7nR%HtsR^`sely? z??ueRPD=!_0>#VUcMIsyNFf|J@CRMzh5F^6s@Zx`c=Jojr$0=wI7FI9MRn*zBRMLT zbb$KwB&P+K9M`VC3>)=nV9p^%?pOa|eed%)Lc)BnB_f2AJ2jQ`)&ak}-bo$jKsZog zz=m|JjUMl8l3%G0`AoBrT}5d5*R52PSGM;nuc3>s`YW2dg6k1@%jLmXsX@ zvC*KX%h{nbP=A3p=kNvNFY2G^5>4T>x;tGJ*~!`QIz#A{0Kf5W+sKjox~f9+&AF6|s?Afz1x{JBeC@?rHm={8 zjTJp`B^j;vW87_*qtNR5fH^vW#b7Z0LKgnz*6&9XG+pK=NZ{X)9;SHQyS>9yB^nt` z$U1#;;u)8L0@0nD_+|LqJ7=k19M`(Uul8G>(|-6g{iy5r5W|XpvZH!=2%x*ip$K4f zN6#(>8}(_RPAKY0C{_$dQ}t*so>kJQ`glpEa5j6r>dvfb$w4}#PAF~NWAge<)Ju!V zOj7Yt-aX7np3f1%ztP4r&Sx~)s3)d@I^jwJJh(dHbJ(a80_Owf4jF0F&eyE5o@T+v zt~nbfA9n9!_?R9FCGJezoDV4Bkrk!!-M@=v6irtfC{(cQ>l+$Th~a{_>ZVxDmLebo ztrM1D%z@gEwmLNk8~slpzhoqxBVr*$FYxG)k6A=p>MtJ-ggP>+XJezXQ9OX;m-~Tv zJCzeeQQY@=kc!D!X?f-l?CN-AHs`Nd6ZhS;E9rpTd>~o_F29(iA!!2wr zuV=l|^sDm%k`1*Q+R zIyDAc{?ef5#ZKgs<8s+{D0DL3Qftz@Vv?o!Q?1=h$>|=%Uut~Bm$`^&;l8~w!3fJc z3X#uyHbOy7WR4;kyx%FazA(c0Z*R*)Mv4JAfAN3~e*yM8f0TVJGRK)1CfPE+tfb%{ zjj{O?F0yaEoksk}ngPULa@3vU%ncRZ!0b`p`(NB%_Jvve+SR=|A5dWN!R6Dt z=u^3$7(AiTitllrIXq5GWD6|}3O>*4-u3n>y_v63pk%d4YTfBYzZ}Bm!|BAluFZBvxKb?qlAmO3 zv6m%~^KWkp-tZ_Vxcov1Tloby{`A-WXf-VY4y81+m|2&odl#ofWx?=s)3@r55PvZs z9(cBynW{WHWaDL)LumD9eAPE5xG3T?(H+8Go_a+;sK4Z%`S61CmvPwgmp4TE#!X@$ zqU`z)Xym1j|5~vKtaU6uuM}G6(Yx8Rn^avn(o7s#+)t>^mN_V=*e$GmDBz^d4WzV{qn^jui^ZqTx?%@L;S_U zxZLO~qvOdl0lx{M&DohlJi&t(Rv1#1!!HIBo_}{HgZc~AcZxl5{!)Bv{RODYo}XCP z1$I43aGM(WeN6e|o&?_S>LX1@)F+5I95;2@*-u&1oG;Td*vX_Mv51d~wGBDm3>?1Q zxcKchEtGqAoB{BcwLijO{6(2vo=x<3HSoghKhdzEr zd0HkAP12$iQCn1Zx#&~J%YXZVqP%Cs!R6*F*vQR5->>)Bu%xolBUE{403pnFFXH2~ z4b?SaM1k*~YFZ7D+-y%EvficsAbwWI`Y``JB>sc@uYmBKD8av_hZgu=|>o465rB& zNf$k()phUkLw{LsJKUc`f3fzDR87j}(0!-1KDyxgeuJ>p_dC`$nM}I>`N_p&{e_Hf zC!ShIpIupt#@5egP0H{Pf6?yXL2Wh->WLJWOA^d*C)`G7YD1kF{7!k#hU=w{+R_uK zzu5Er$pGgsFK(^B0J$0c-R?nEfFNN}Pole0HqCNA2LUR}u4qBV%ObOzxe}?%rKj5h zlqJmf(gUVe6snQjqK0eZL=7+oY{CznV^+O@+^nN=4~)N%g%{m=uE_`~&MzT)>aoi* zklG!>FN;h!L$N1v zdc8SU{U87R{9V7&6t#yEi!J15u27++GubAM+%)y~Q>x4Jy@)$6Yb_Um-#?NnzNYV- zmwWiUmUim%V60c8#+kjK#98hC|BmE09{nUUEqB~ zuf@>o&HniRct1>YZbe8oUyJ@eg#7Hm*}z`EFA=msTP&9Jix}%o9pT&aQCi?WQ=$+d z9Y>hf>^opCApLn|U3RjA)#P}EW^dInB`5TBVYdpIKLv8Ij_$wyd;PEX?dJcxYCmZN zp2u}=D)gN&n_fUSFhoeE>9UZsp}JW3w;A!@IFl_}$m=@Up*SaDnt1q!9LH;OV0t|m zQTBYnHEMz_bmg<-yDl*_sO~InD9FI*j-I`EfZF|=|3o{xa&$hW9hP=fmBcl>#eIn% z<_aAA=p;$gyly^Md+U?7(%2ot!L=LjS_b}P0Io}f{68+v6MzPc_ z8siB6`F-)f*WZ8qdFEaX>A-m?DeTmRtFiA@wOsgox(PqE9(&s+`UYZ5w{S#k6G2|r zsRnLfeCi^;^3!qKW&~aRQ5?gBRE+|?g-qEeV=DG6M^K)Q5aNCVhNo-P5nMRs6iUo@ z!sJBT%7rdnOwjSWs+1O=S;&oWxd87QdYyt^|Bt%s!e=4atF4==H+pMz2kb|yeCRuJt{5fnuf+65vj3=>0tI6P~C-29fIt; zW4mzb%jB?UCvB%iWy)+mqr=jeE|>d5<$^90b)sMI;)Epptx6)FNS*(``QHuS-F*K6 zR~v|L%!R8STN2;!j0lSRw4%|qOm|<`GwHzc>kAGMH z{q;>?ZWysPEK0~7nXca+8hjvGL`<`}UtY05}@Bmi~91e%>w^+VS@<()Z49e=JvbSRxnw z{$mg8&7bo>&eK4B`uPvmPFi~()h%7skw+WWkrHs){VLHye)~(fv2>98PVn@7YRz&l z6ODYt^B)aHbtWZ0L$4l~Gnd6U49A-mkaR+M+UAc-0T`ZEX3x3xIc_KrY2?!`F^z-6 zgav}H=A-HNCy2Eb7u}(}4-ieCNxJyoWw8`ibW8i5(%r+2d$nhAWyTn$~ zR<#_B!Zpu0w=s%1@+$-!d;;C_ufeA=PhItR3g>|Of%oeRaJu_+Yjp?oMVR0@#Fdp0 z^EnWTBMk5u&+ko=nh$-!SCc~|tOi#eM3w?_3VI%5j_r~-UQE`Fds zRDtjfN##HN|EK zZ87Ow@_oxmyhSfCqS-s5MYbh8qM%dqPrixCF6#=a#VT#iG(wLA^Xt8@1b(1jVIE2#DU;(1~iWJvVns~-xkKxZNv zO?45(|HZkMA4zO9#n9WJ)H&t~Z)~uh-m538;?d^E|NE|E-uwc}(|pf`*}?F%dvT{g zD1-;YAzVlc;Xmc>6VV-NV+6_Xd6JhdbT|kQPP||@N5kI@cKfgVFmY42y3r+YrH0gD z-m#a`(`Vh(2kRzyjH@fk51tW+hE$4o=+a|k3@2j8{L?Rr&ApC;(1NS&sLsz@Dq7)} zD#^SU{y$Nj*|hU@b#Qq;uTQ%!S9eYSwZ;E$`~>{|f4RbqBSr!9>P)+o<>5Rw84J7G zmr-N9%esoR`+XbpRL83OK0XbTGU zt1^Gcr;YqPcsdpnRql%UnHsTu_gK-vfXQ^7I2Hi~Nq7QB)PLhA;P?N_Rk>LgAJ{K2 z`OND*zQoHH{Z3!Y`hQ}yyy|wnSg=I3-g}gN_h#NhVztl9qU`CtKgrmSx&1qR67O!A zZ{`@!C?~yQ8Xs~p!vf|B{|uslnfKVHOqN55%BK=YMs;z8Q}543*kY$cQl9%FyXLZT z8uwpbFNzI2^ymL0Ps=E1Cr<`=unXFWcfQk@zyZ{?;Cmz|Nqln z`9}gO7A3Cm&M$X-a-J8kVa@S~0QdiYTz9}ZiQsQ;lKl@{^JHVXHRc%w9V$J3>;9Z~ z%nuB+E5F$jH!JZw;Cv)cpnigr9;x&pDgt%b(g}^%i6F-pHOk3NGIT(9#I3rfw+l%3)NV}InPVBYjzpN3->xVf;;T@%GjjZFUt2G{)KW4&HgGP zIIa^540heNmy%jgsL5I zvsY?~4i86O(kdJS++A~$ z)06iOQBn6U5lNHljn>|TSB88a#iFU}WmMH){d9atb1*}Nf6s4oYgM(bR7dk2GWtc{LvbkAl=2U> zfZ>{$DS;T(m9r*c+md`5D|Dr~6QNxfJ1n8DCb|5Y)B)gqL$7ww>;JJ%IK^GUhB1s| zmv!&6ePB!ExYes@72y8=U#k-aCik8a3Gq=?luBXJ_y;M+1s}*T_&p&QXy)_}gXoSw z<~RnY%JxHH!YP0MOH7OycOG}Wtftxez1G;+*^T*nsO~g!E6>2_jsocwY}5$>t})Tr zFVWa&VQ5yP!)&l?O5e&CG4)tftMH&5v+jU!O-^pdg>6ZxRv>0m9`gu=j0sDACLG^v znJ@`)5vf}|%NdkwQ0U$gfTEzA+8vB{}@H$LIC&au#(PQN4$484HJEc!?Ub%?Z26HBQ;m`wIvxoc{9M`;qEv`xI z&Pv|ga)WPKHAnjVVK1S-va@}3I0}WNSA<#~^8JJVca?P8foN?%;&&1g>yg5*5ntj@ z`O`mDx*(1%(-t=Z(&N4dmy#0DUR(RH`F#d(_IlStisv}UZ(Fwr?$2Fv|!8Jf1 zYN>p*{kT$R+Cg(k`sD!;7V%EaW>pkDUK#?yvl@uMETN;8<<6CSOX3hU#W5L@TaD_O zphnl9tXcSo=xW58SPJ!*7w;MJ!1;?4Z23!NtDWe6lZN&1@A@E-$aVtwQ@hD`QeDrY z&x~byA^w7FZ{^zc_pKi@Ap&bfDw4%a4#Q;mndVEkp9GXK`rYk>Y|ba$4vJV(qv&)+|1wb?|g z+U!GQm7U4>B{p430}$Os)0lE3aD`IlDL$nwoHlUi<90?}gGeiHDy=t1-YSuQ^|9dDH)tCZ`#l z3wuw!XDtcerRXyS^RIpX6&kyks$Q^=WK{0m_8Ie`&-zUCj|NdWQ>K* zBUbt34i1yQiyrfBHB+4YZp8fjPSIuOI0lkaDNbqbM+okJ!U$Wj7)3vdNBOgvQMzgT zVSqw0AcmYLnL6v=`A?1>ktlHefK%A&2Vm~FkJ(FXYmy{IWZCVtGh+~wcYQs$lT^IM z;vfde&G*Fml|~~yl=-M-NRT~X>o+tw*H?K~i zz(zj+sLSF^qlL6*(q8Kqqa>6Rb=F{Wclfu@4Nn3FxINxGRvEk8Ori*z*=@DNjddlC(oo-M8qn7ShmDXko(MANP1eMc6y4Pdm!&!8gJ0Q z{Q~rf;wND83t70+t)Fu^K7an;-FFWYsn-_bnJ(ir3WD!GlwyuE^=&m`@j(3LVoI6u zL2%=5kGG^EZfmckQ@870B1yfSM&X-(;>+=v#2M-@cGd*aVEjdyJ?z%bIRLuj-(1h- zWqIV7j-vMIPuApF!-==)3aR5GMl#lFdx-9E$n7?Ru0|g6y}GMx|931Z-*GTLH#?#J z2lZc#cNeTiC{W!|$5j@9)1B9?)g8b!RErz|Lutck@fx!q9ub6VJjRJ+@1W*>fX&`V z!wAt`Wfiyoz@3InwI$y=ElF`lp@VNfJjl$XJ%;GcbFzu~qyVmoW1k13J9_pvu)#IJ zISIknJ&d^gc$P;d&+tt12p>>=94py)sje>lppffkFCS{z8Ry+`=Cd(kz1pgw$@bwF zsJ+F#DmmR>$E`JyXvn8eq{(>EEwvT4< z2UMq}wK?jAi|?2q^_nM@J9d43tddKjJ?bKTmpLPn|&%6&9gdL zj>W(8Fr@?xYGgPaQ|9rcdK!c>soD~(1ere|0z4gSy9G{n=eJgOK>zbF(~qW=U70$G zM{=~?Hb2Crg|FGEH*w(az?$N7i0+Q-bjbJpP}Ix)decKV${N@Iw$|ZOme1&(tqYN+*ezJT(`j&4oty9@$U$ z48niixip$0qJ9Rg*P!GPs(`82@Y#{w+BzYS<1!}u1Ea*LehUdz3|Uz4^d%u$jAw1XTw~K#?jM^4fEYiuMKF98%spk0+-|J zU@ONVJ{l$N=)(K)S_SvKvD@eIxyH2zSC`F*vm08vF@&d)B9}SEsqW8K*Z)tBPe~oRUY3zyuT=0(nx09SctkQodD_g3NC6yA zr@{tL19igZS_==yTGEx;(~VV5v$z$soij8H9bO7kye%aUfN)Jf*+!7hn~|cQK~I)r zP=;QW%*CF(BKGl%zskjYCX(x80OcBXk_U=lxW>JB4YulpoR%YdE`lC&sBTSz1H8+v z79Y5Z8q(dq==wxhYC`IS>^O)e&QAA{^WH?Ebf-}7znfhOVMFy4SotxvT7cid_ZwO# zEWG}42(C^@2wQc+y=;OJP7zB#jY{%L*$EcsSu5ZDJ#6?$;Z0RGf*XH9{TNeEMYEyP zwR{oh*0_vA`}p`v)EQ%Lq1MslP&{T7(1*(E8wBSsUtq&ufcZ_o8eEPjg*Js3Cq65j zd<*xraN**Au$XCLXe*<&2Yvxb$3d&e`#K+oTQrl=Sih8yXy&00LqvI*p7ymzm`kf3#v;S7;k^ojQ{hsR6=#9!9>wilk^j`QH?TEOMJnIK0B7>JVNd!ne@ zKyz%GX8!&K;4i^Rso?x&7dHF_nBOEBea)ZE{hZY8N-ARvDL2b8>4$!5p;s8?W$J(e z#9v4vTn*N%2n4Om;(HVcH*%amrLA3hq=&<05+L3aN&D3U^_SwU(j74V5;H}5>*qHK z)h^v)JkEr7^kt$~+c}@Oc^vvgnJR>^U6Y^Ortgv_pWqG! zAmF?j6w5uw)chp~&CN>YDo?=WW_8%e&A^<4-b{Zsi)F^jCoZCs4?Z@@O0upC0d~#} zYcm+%rXjicO7OXIXVu~FW%>5BDH6kCQ(i~aIHJ6dd)NsXiLafr+@QG`ad+7SOm3F9 zLxYVu2jIMI8zJV!-xS^K1o_M?8|Q}vRrA{v3*4{6Cb4MD86f$^7LH3yT_e7uz=60q z>h6$_RvsMvJM}0_7OaZrMsXCDq(C2P6KMoYeoPwsncuxcP?%F9?F5u)#IJd0V6r?l!h^fiSA&_TIlM(GCy7Lmym6Wqz=?)^X5{Ji=3DVrsI zwDI;D<~6-}p-f28goQj_Bac;n1tBCiOP$YGoem0^zokpzPD@YLYMW%Uk)I;d$flG~ z9}-?DQ-xwe)1f#n&%5Sh$uPKplt{puOwo^spD*4U2F?@DZnzGj4 zUfnsEd+Y+?8Ua`88lQ!nu%eI2cf*Kk194AZaYei*vlvnPlHFE+UR?m?8Y*IR5^(hz z9N4JW0R83&+In0AUx~rMdd#`8?+t7_Vo6FmnHZ{>r27SrAoZH&YEi_=pAw;wre`=g zq9=d*@Nm^s_^MJFkh+xSJKc#;fqKn?)&ns0ns#+j*y=auhJLR96B%)h>>8!Es2uE* zWA$8kMnvWdf=`l3IV3l$(|Ox8DepGtyYPR0E`>|MUH7)wp33VJn(3q^veD&RAdr>fcX#y*psuU^CJ=JvO# zAJQH{bF(?VAp^MFyaF4!8JMF(3`xqNq31LGvznx~@lt{uU(nEV?#+|h-!{SLH~oOf zr2vElw8iL0O42DtwJs7E@GP|swXJfocRPshd)tMlLvu4{73wya+>BM6cI)Tp=AH-! z#CU}7BYSXtFR~9OR9zoXunsa8jnD~O!h>+lff@4nBkqN;uV&mI$RE7texu;sJ#xKN z#IC@$z(P)EL<8lTaN}lXa9kq;8(ahI5xI(+C_2gR8aL_WDJ2hftX^VVQnIF?c~)UP zFWC&?8f$Nj9UT0X0jERqMBSXj}u(uSVJEZaNNF4zd4>Mo&Nqw48|2!?B{kf?6` z9+B3}u`#slQGyD3-@`rPNw)*fwXh}9PuOZr<9kAodJXbGzeixp>2(~5uCy1<;u%^~ zZMNi~L`}|LJBhq{(lQF5UK5{152judGi7sY`%r-H$PY*r5@TmN=(66;C0q)(Jnqm! zboX{bX7q4)Q47)Cu}U1O_cqtepnRoG!rB`3y9t9LTK03@`*sz~X~pR~t5DskX{^?Q z)7`^cuRGba03^bAqE~1nb#OruZ9fQ08|KsgG=BM1!)WpnqPz9v0yC@-FZiFvUg8-t zardn?vmXk~$Z^Izd5hy7u&PS})m=&IsTVliIo*2Qv2ujJX|9=@O4zyQJq7<@#k=2~ zH>dMBz2wJt|0;;?@{JoxNSyml&&X-xSGhCOAl;=(G7`dygtyiv?#W2lzku&MhZDv@O_uxE(}I@t5XiJQJ)6phUsy`uJiFO*7;LXV@+t6 zt@dlFPkG8v+`<;K?Z_ZG&aPO)T@WQs*G&qwgXh&yoo*YltA0a?g;sn9ew7qwxdo8p z-o8=-m*c*}MvepKx@w&@znQflnI|Dzdhrvw>cPjUqK)d9$Mf*;Bbh*Q9BF8U=UPAO zPX+CIB}zp4MN3kHhFWDE9F~AzdPv&dZ&HCAH>Y3&CdXNZ!@*XaP!NCmk$HqGOO;aI zpE(R?adzy8E$jd|lFiGn<7pMx;Avogjg7+fWwdD^k%0J%9!U`vLq8n(6&5#f)M$-auLy|0prLYg z`FfHMG!R=7ll+qA;Sg&xg(LJt3L{>Z7Ql-=7?)O=o-*oI1YjNMnDOYQK4of=*6}`ZjNi?TTs?j(@Xa>!rASss-f`%)D9&@4{$v}$$>3PA?y+EA@P zn33viLu@PHFCTLA!1)UnZ21dfKdYM4+Y6m}qTQ5Oa&xwlUy|Di_OIznAK(3R^PHZ@ z$Wgp6?LzQH_a;@_Mv;T16L>S-n$7M^opows>E%mXL;WR6^3Er4{-O(8{?a9*LN=me zBzHhXqw;k>jV7&+Isa|O2A5gS{WHjY42^Ce4RpzBV2LrudMnnfvWPIrHn=DaU8SDyxigM5#7&Vx{KbY^vXbeMl_e#^I)|$0hh4^+eJ|->?jnwQ zQDOGI&nBp#{xTUgOb*UpCSc27rVd(otR$=)X3LT8ywn+RNRh3Z?4;gy2`kikb8{|* z_*9w}HBK9jO3>x|qw~%1jIz%yS*pqM3=iLg42Z76%>r`{PN5*rrC`98zt{~MU!eqf zW+0_7ZwPae7srIID#@Dv{XB^g{`tmVLU9okykF%98ykmoD?YIHFA8fhC8jZG=5&DT zob?gydj<8E&=aFMaQ>nNTmC{bH!elxUmq?lU_IdeY;JCx6z*EB$E}*Iivg|?avwj$ z2Ak#X%+CE)x86E^$>I3JMGWc&kP z>eJ>uS-<;h_S!3yyNOJH1H*GlR2m$wA^svti0bU9wB9}O?F1=lxa2q1yw2q;DX#Z` zb5~|^`j>eW!+(1ak(q|B!1#;2oic2k4*>QB`L2C4Xsa0En)uDTmF0sn)Dh5D`|yuC z^7pb9J8nq7`IqFh;BvoHa7dil1>W&&oqjlCQFZl^IN!`jS@n-sMi$SZdl2^tHo*0p z`(dNs44gl6TR-})&C^)nY<*IkEKlV0K5_B4InCwBpu+@JpKL1f1+um{s`b}z1htvT%j!AD1O&#i{=J0Hgg zYAAzhz}*GK-MWZV2dNEufBZ?CdFJHAT_(nbISho1!;xFN#S$Qn#U{rdk~H! zvKPr7MU^qtuQ)*Z^)nIeX}>LB^_YL)q#ylRT(F5)n%2L)U=gO}hu}EU5Vkl{Gx8KO z>)SUOkv(LqH0K!R!||%fpt-K^?_GzbZtCCybbOzmp2*^cWfBObH~BhNqFy3Xjo0|g zJ!>{#jDt^Cxbts6liD)pLvS341X~*;3JJfZ>;8jcD6;2B5E6>^IA}IJ6fL74jZbHD z{}F^Ee?2*$F!@ALZ{3^lLH*gQIs39FiV5AvQUxW#_YqUtP&c3)IZoQG0gfYAVT&VI za~~3Y^F1YrxknhLmEeI|9z+g#Oh`szlHOtA&GUd52ZfgfAEt0Wx4CACKz^zwonH{z z5r_bP8pnI`yp&Fd9LkY2I=rBs8*Ujc09zbsMsklNG|obGUd}m*^`1`MxUrC)#K-ta zzva@8k0Bg6RfzMb>YDLaZKMchY$QHw5L#P_H6da2#m~ zTO66~%wbS+mov)PxL@iC2Kz3N{io*P!B?*>uD5j}Ask5~`B)&u29aX2zaKa6m*M^4 zSHUKNJ{n{(HGhIQ`fG5>pd6{1+QkfxBgtTkBMX|xE@Jmcg7rjAWmI@y9e$;>eHcKp zJa78p^zJOA9@!f>amQ;G;S7#XHBuz&i|{vma;$OyN1p7mg5yXL z*y70bXK}JFWfR#d$UMyh_ZeJYe!SZG>6VUs*SlN$W^V+ucb2Ny|HIu|MpYHQ?Y?wK zcSv_P0@B?eEnP}UmkJ2d-6=>(OShDiG*Xh%A|R!7Nu0&swca!Ke)m3S%rW-AI?2>7?spSlaNK#IBzRGB)iAkVI7>CL7({T#lSk!@wf}%{+9*l z(vd@EDTNeBii%UB)tp*9zFs3CIL%S!kx|wib@{;{9ckjmf5~4!!d+2S)tNgTwBmX$ zt2&@jb-;7vF5RlyOYj`5BZVUo!+`0?Lg>(uka<}66&Am)$Da#nroKGYc@#6fOX_Yc>EVY zbY$F&?!8?P4q2}^+lMiAbVUpsw{Rn#AhslPG?HP@sX2yEMyo(o1mcU=5Y}|~qdeaf zyPIvLr;TwvFjlHI9D7T`$Z^!Dt3O_gg88Dm`{e)_Ur3;fFGPQ1<_qer`dm(Z(M!HC zp49fTRfrD3@;OYP3m${`5>+i<&RHw*EAjb+=^vM8O%2;cY&Uh7f7Y+JY>M(;IK2S# z1!=e{1{hytp^Gm>ht5gm{tx@4X*LCiH*jWEqWKcUuQqmu54nd}KzxB;!u^e)tmE$5 z8BSiGh8LxKuz^Inx-VO${!k(#iE9)N%olr-el}n^#T<0;WpQ7morz8PdpxD6T<(ni zGTa5$`lm&AY@%yr%_IA&s(cM7S<&s5k-uwOgMx`2> z7|z&Fa@O=VIrO@g4LLtVtmH^9f3@Iq;{)+UNSt!8+@{H}Vt;DEk13zuFZfg1?{7rH zOD`N={)n10UW2Ts2z)vRj4u<=#TTsA_!cM0-yJht9$hA8EUH&OUTRS$sJt|FX)S*T z;tR{zKEK!CoC~)bTQ5@^eDkq)nFv|zXdWwaF6kvQQh7P_!{EMo$ z(Ms)mc-bOfg~eb?eOE_xf_!Y6QO@}=V zd8;8nd&-6;O#T?Mo?`IvB@n*E%^2N#|6(|nndBOI;2 z07VVRzjO!u^z+;B)=-T+*&=@J-Wo-xmeBmYk7NilRD*F4Zmt9DUnHI=dC{4tP!VXHyaS6Pl-zwnxT`_YJ>6g6kh zo6>BPntLl5u}pM*u^i%GPz(l#^aeM-u+Qn(pG5DV@DY zFvy|?@nzU17}kT4z4=I!4hdmqwU}rAEzM7p=Xt7?j+~OS(s>;a|57!q1B@@6(8ZT| zg1ydM*@shcgmm~Ss+vQ$J0BB&SzmtGe`odW?%d+kWc{$8t$QIbrABZmyoDaYqc7Ae z=lQbEX@A3g)ZMqt(+)B1il#^5lTUkZM* zc9AxP6o@Z_KYPqx&J2joCNJY+{Al2y)Ka5=n-HtNxtLhzC74zP1LjLvgkvBOzO2vW zLWh5W%vGiPA$e8B6jIgJVJU8YJ|v`Ga9)#kC=p(Ic{~CK@-LsqsnZ z1pR%LrUCIEbIWuVp8cJHN&Io}O2T^t%on(wN*iE&X@V}kP~BF-Qj~Vd^N59siRyF^ zIoVpZy0Z-*qZnQa(1Q5#uA8zX_nYJnk_(0ZdY>U}f9D!}F`p;?liM}M(74CmxnRCn zdCd9&;|o1>@uloTh)ksYY?Lv4eM2FNF6W4CZ|0g*TCLecU1=tWFNv^t&$@mXfBbtW zOWd{m)e>8KPj5@=i#pnVrA1g%Z2C5sFGA>22j@ukS4U!_E0o9$e&WtRB5llJ{E_1OMV&O{V$_owqg~2i} zV}Cbgbrop|aNU)YM&ClWm zymGXH9~t86dxxX95M9INPzFrbe1I-plNolXQPcFctXD%Z!UB54ZztL^plYRv=n2*RuR8r{x{6TrI0!tA9rFkK@89l8cG zw|k!G5h8JNIGLMAoNq%Y&!<%$iZ%+ezI}xLmlnAoT{C5Hc$~X*q+;i%adRETFZEEk zom5v;f%Qw>Wve|=cb;g@zkSqP+e*klbPYzC+`XUM{lzwd|2uE z@5abTwC#B8m$5t`DL+g}+j zg#ZU^U3>y`t&6Wf-$bvGeVIapoCJ3akzf=f}o>ZKrg+(EhGAGPKP~Tq2tfy5AagoCIi_@tc_K@u zvVZl#I^ja794P38Ntm`H`ys^_3gSyE+~BEC#VxO%=Q@@!r|;*thm{ZrWi28b`JYS>=sMQf3ZpV{oDC}b2`eLXu5&;m-U$z=*nf2 z$)3r^65DP#*~VFD{Xo1<_j-+0(_G%GO?6$12+Cz)ZL%3j*2@LcnvYH7Tb$@Xtf$9TS^BCuQ*_ul8S7pvNeR>TT!VosKWo!JPA-|}v?*W>66 zFCJPc-mNR?aohi-^VkgqYhWE?8-w7f>|#>8Mo6AsFG{#<%hw`#5inm6JY;==@ue8L z`0_JJTBETYH#Dr`X;>{g=>zz$?4zO<55AHm4=r$k_<~lt$IRayM9C%TcjktxBi@9x z{;b9?q2Skcx&N7V#cvHTUmW<_@qqD#2fFyez+KhRWP8-{OC?VuD}eqJLij0KgS2GZ zBzFIkyLIu3!FuL_^N1n@9W-5U`0#ZtefX0{-E)vk>oOFS$^;V7!F=J3d*%m>FAC7b zm!f|03z}?gp<|>*bWZA7fq8U|@>CST^mr+SS>snB*SG1&Nig`D*64;B7 z2kU;vm-<^F31JJXSFC$39gLgC_DOJ)UOU9I-_SP3tZ&Hs8qmXkGjc716!t zi_SYJR1I!Gw7qSrl@|{Kq7+(^hi!YMWZA_|XE#cZ5c-|{Te}?NubH_crUbbxz zUhW8h_=59CX}xoG6Ay{@4aR_M>^YyzCsa7mFk}H&3DiekkKvfXd@-{!oC3xdEa>73 zZ$FhoFxk_SwWYnp@647XZbfl60Vx+p?`2sQvq5~Z?*D#J&@O7p_x`IazUC6Ao3WXr zX!Wll6GJk$3{n(pGYDUPGK>M^%S-6s3#9+@bmOyej93RL#aOx4ukx2Rt-l^rzcoSi z*t5w}xbrXX1wCW@kKwrs`CAqQe@2@S*5X)9ANjD}R<@gW1!_-Rg85PjH_ZWrFL5*e z_rCvP`WP=3mT#0vry!Kb@sN)cemk{Lz+W%$W*YA5 z2mWf$=2; zy7=-3S?el6;!E&~F1h7z|5nH2UkI21SIWqi=wI?gL3~l&!XjO!M;^=asR}?IcxEg@ zPHisSE#|f zF23k|keTG%KrHUM6rOlcAb`PBqq!_6SMtWkVm<9{UHq)G0&kQRx7XpLFO4;wbwB5A z#$s}gkJ+_DXnJm6R`M@_`SNiy92FQ}BJMq3G8$ zl_Fy92KBMR!mNF4gfzEU1nL4x9ugUpDiQiP;o?>cX4b;?wc~0&V+Zp^8*Zow7+-pz zj4!WvD>aKeUtWa-;_+dvexJuzN8 zh)CD4Hu(6xU}De8lbwlF+Ig#PE%L+byai96I10p19)A-(i+F)A0C=VG?mSZJkh z_ubip>xAj%OZv0=svvyP5A6fSm)Fq67vh?qo9f>uIYV{v21}ly`$kJelc2?E2=(_I za^Lag!%qwEFEfQF6B9q!QzL>y=$9*~w!Dtmri@E{3NT|5s37@Rtu-?+zQo>pz6fh* zedxsA8)%^^VJ)~8jqdXMy>520*%8e$@y8#;7y932O6fz=s8KS06@l-O3ZEG|H!RlJ zp7rsOzQlJW5}$|g1v?(#zTPhA;0uofXbuETn0pLg=`?QZ4my!Bti zlbArBA3Xgaz7Uzq>_ojEU4eVLpVZK$A%x|`461hc$Tptj~Zc=YhHT$Ul>C!>MZ%9rF9lh+|5sR!N- zA^+y{Qz>_-0hw1~8?g>ub5*zPmI`N{tUjxd-T8A;D;6dAx5~*Tpk*mKWDJiT^m*s! z(pKvC5nbeHP$cI1Li8^<#C=v3?A)E}oJa z-Iz)eu7jq!L3dZ0o~9^UB$rRY%MnG^TRj-`+?>;Q6B*wzJ?o23|E`>&$6=HuBct`& zV4>|>eQ>V(G5ZOGFG(YUK>Ujx!t%Xc7Y|ui;+@97h_iBtu4ty`uxQL%YC?7r6rLoM ztn^H7QXiyiCbL5xBkT5|y4Q?fSm7T#s*}?kW!8VK!<9P3H1SA;ifqx@LVQ z1v>hEkaN&$_I%wA&()i9vtglQZ1%uPnGEMBJQt29m@kV$Mf1S;A_`r6F`YtURkOAHEzdAX;`^w{D(7!T ze2-n@zy(G%u?vVVaq?>Ga6Y~txq4O4@+H0DNzEqlmw)T-p!)qDIJ9sVhynA3hUlCN z7+-!s2VYw6`Y9Dta5PNwZqoSIs16?GWqphA&1j^T=_YO~4|&{^pD}~@^2ve8DI4?p zjEietN#KDP+ZEx=smuDr^GBBZa7B90x9GuqiA*-c1j3gF-D-DU6}58f-EbA@t}2PY z?$1#1dh0Y+L~T^Y`P@M|^*`?qe7(!v@4mjvNB;Bu#h0ZQj=V-=DB+;5=_@*W@!0Tx zH-4I_<>ghjo~3v2*1~*w_y4>5_e%N^e(=;yKhrtm^N(~(gnB-@cq-3R8zAyCmbGlJ zM2GIAN!cTmBddV_J>=*9m)C_5+i}Rc_+Bn;l+_ZcqBqu8eaxA45+e`gb57U955G2b zHr%aSl#%|k8p96bujiYYLs(BIpF!brWY=WDB3(Xyrj&X6Bn0f6xoF#kfcR#NGKYJ+ zE*?^MC*-tn{AoIE%f7SKCodY(^s~|pk3XSf4VnLf;Q{^Kk}h*-NFuqA3w{@=7X$@4-xSNR(I|9w(cP7W%W<=D*0J*l7V+uwugPOyoxFM-ax>Z2p>Y^JUc)#l3ilc z-`=NHN}<zbKzZCzlxh7US)4P?$|lEw8+%JDV(*w=Euyt7{hys9+GYOF`012-9Ym zc-`>r7A`s8%o6jP)Snpj6@v+L@_yOR82#Dr@8;yR_e=dDh!_l(AIZ}*eyoqCeKpWM$V@rJA5R`@*-3_4Ca za8LfDS0K8DCQKLn^*{Xu`u;`Yu*M9Hmj>Hie#aLk@#9RnRj)8hH+4pPZD|00*R;`g z`9C@a^f@|GD7#_oSyG`DYowp48Hx94(K~Yzmpg2wgJsXg45b&l{M@auGFW-?|GwTo zz4X8OT5z?~29no2B6Mi6`uWJxs7zR2phKu)+miToG|!;8Mysdb-F(?^GT*)~U-h9% z!YlSV+ECB5GW>KTWz#&x`6BkDV>-dI6QZXhZ+w8%oo&Ppbm-{;u%5n&2^46QW>#uP z*4!Nl!Ws7rsJHBWCN23OU(o079GvhqPxjh=n>S%4AK9BHw{?vk8gr?Uq>lG*eu~BO zk(9Rw>zbe$IX@t}rdzicI`SIGesa_K1!6{yBIm9z_8}q%d@(pv7p!EsViFtdw4n2z zX~JIaMlDwbqiIhTa%xk#I&X~pWu7b0Z{^5rz;US0LD($>=QVmBOAmqMHIxYD(9sWo z%wgkW5-2{yLhjU(`+cq1o%>02>2Ku$%?Gb{99#dHlOq#BU&n#adlY$GC)c_}F*`LH z>nG(@t+vx$q1aRWMPnb_4?t$ac?YB)pz3%4UHxWKssP0-mY_XMVtyK_&#ib&D(Q$v z*ka$yB=wuQL427o<5wO3T8D>RnpNdh>L6gBxvuqL6I2cPzX&x`xqfJ^!t4k87he6DXkh*Y2fF+Vt)pRZ0)LK3b>$Li*BgeTuJV4o z4`Svg%dEL>cjqZSnjgI_KkuGOtPq}?C2Sv{-QHA78sx<@ocJz^y#rUJ1@SMj6N|w7 z%O-UA7s#B?)&}@zU+9RF7X#a(ahD!x;#82AD1X1QPnSr_Aq43zbCkXS@B>?N6*S;78gI!A*Fh=19kQoFbNF(CcsvY;F`UNM{*4VoFT zH?o2W-1dy6KVQGuobk(ibvNfTN6@G%Tt_Y^k@Pni>c~d2r^4R^V*A!%W(N|{Cpoze z@Rs2Hl)czo~s&9gg^Yrj1&xY)22GeaqC1hu`VedT;QkDsQRN8i=mm&yMKvcUdtpXrqL1TB!d%c7Ei4&My%FAk`< z`N?{2!C!f&R?)>zBWDAWWzp+8NLnKL?43aVCER^l<3kFL0*5%twc%{G z_nQOcUjic{x_id|3L?JGS#IN*&>ctmC64i4<;R9Vwi2Ro5=)@rzj-BLrgrSW{L3(O z_!mgO*%h74hC3MRmWXFH-A{clZj#s5qG48~}4ep;!X9t?;*P8x(TwJ-0_gU;bI#`&s85Zhm3}&j^QZYYs2SH%mv( zlYd3Iju;=CE=-kvf!fIkV>o?K@NzLJ2;DSJ@7Pb|-<%vrjLQljzFCvo{NCrXNwLTa zf`;;jU3^q<1=7d!TxfA33kH5<{9pUU)IfFDWVKDGdzhYkb@nUDSQGWB_4-e^FsJ?~ zVRm$Uls8mUkHK|EoiCLNr0#4Zf}lg!K>E$QI*xEPN(_M?5)%K{$x`;T)4QevJEukp{setp*W>27~>Rm0zK)}N+@IT)Rr58DGJ zgQy&ZF(hSXMtyZ&Mql@I5as@x|Hs|N3~2t}t1@Kh%4=jlC`#k#X|!E__`N7Gy6N&F z|E(DM%apIxxVW0mAYCJfS|N+Ryh>X7&Oz;Eh^mKwum}EPa18j|cTkV*3*w8V=fz~>NAixT&DigyC3JsYw03TsdM+&w?nVUUzm>GW z2J?l=-FFQbU;LqiFOa@pb)Nz}qC=PY%^yCp9@g}uG?O<81N@BBSC8ew@Ay(@qx6*m zbq{8EXy@|m3ZBL^>66tU9UGa~_9y+EpfvB#V7^3f`ZNLIOSkSAbou7!)I@AQmnZ9S zMFE0LF(Wmr4~id--@5OGTlMGEfqe6-)+u5L^`f|J$^%=D7s;Ei{ibH$&#n$XAESs# zYzg6-GlITv$+e?g3fb8HcrQ8GZ7*53l5H*1D>YZH(PaeU z%gH-7#wbRV@=xkqZ>U|NV=Ll%M96V>-rR8;hlM?1qcfbtblYI% zM#KPTalwWgp2MXz_XEG_oU2T*u2~8<@d2W1cBr)OZ66A<&Vds#vliat=an+%5vJi~ zD)uz0aIMhZzRU=^mBQUzt#i1U7tq^m4vxX6)~gL74bVlG9-ye<3=Eao4;p0z+%|rB@C=3{QFHO#@7y+p>GTc_A%rAUa9(4-$P93>`D=SD8q#LkXM>}&Y+k%I2h!cF&eWqizP99LY`q>oGRpupmFg~a?`U+OA7f* zC$I3FYJ+_YDY)))LZ6?kTu}w;9pJx+dt^#0rdLCLb4K*ZqMtj|eS>`iTzApMO0R&`-R8Zm zJIKBi)tY)-{y);AUtT(;6@S9*+^Uo=^y5*8bNg*c?+>awAMB1%xAXZuInwV*B}kYK zpVw+t;TE`Sx#7ld)YFbk6~T4K^~WU!NZq|E6M(Lq@b8lf{D8-?=04Q4BU>Pq=q>GtD05ogBHDgheQ%r160!BO>Of_u_K0Tmp)s(J`|vCL zFAyKMKGO+ZIpLqlT13%b&tZ=P@%uqNxocw+A66^+ju%7$ES|Ef;qZk~f7&L!-v`D~kK3%AYV3Q_X*H5>}2<1vgUi+O^T;j;+U zkp8ED&@`}|uo}8@LUhbas&&Db2Zh0^JxC0G@X{)iIe*pmm{Hdrj0%DHQX1-B_<-G_ zYfWCNLe_f?$tT57@bN&i%{6*4eiVzj;Tf$*hUcLlm~LJR~k-JrB))z*TC z?lqLTLu3jkW4Ec_&~fXdyYJ?mTB(MH!85V?8BqtM7hTnoBCrO3*UuxzM|bjMWU&OYw#=vy^I2q6>|m!)<-ENb`=m` zN}82rtySO9sjrCD%Br`>a*$B?$E2v_bQymbdcJ+ZikkQA{GwerO?|DyH81^RqFG># z4vpo{yXBW)zD!ZqzXHY=bLirWPqb|2k3Vl^GB%2Gl*7-G7Tr#caPuunMX8?4DBtnL zC3oZX*5B*OLxNH<=gnXaA(6djcr(GPL9p{L5d38Q!F-`5tZWCym(S3}m(wQw=CT)> z8+dI29GCV*g`0h2#UCR7N;yt1I52?tqPbBJ1c>VG~2g7Xj?mW|>lt+Kw3uh?~XE&!G8laF}i&u9F zFc9DTQDj*9?M5Cq3F!w!8NdR|WtX5UmkoennGccFb)|SJC0$qM^{RJJRckU|Cr*LZ z7zPHE%krg1L@Kv`DnC0Vkn*%o%C3xiYLRLfr2I5QG6EAa>_kIsvyHI>n?D`PP0hST`*t5pPoko z;Y)+=>Ala#B$_+do^uk(Az3f2{ed}pG<)$!6hld98Ghn0~&#ZT`b$j;g4f5j>w*1+(o}!lVfcuouCq=R zh=1Xz&jI3J;%3I~{W^yXg&Ycw`WHv(`Jt>4d?=%DAAhB=db}c=Yb#sT4$?L4Dk<;} zdmIkEi%ZKWD@L@gQCe}EyiklaFs0eVvm$${z`91;uvi_KuE~ZDT?1L?@Lj%YsCMw} z>n9ZtCGrhm4FrYNC!^d(mlcq6q+$0*Sj~wy2 z*-V9sXx1RQMhUwKh^`TI?7O$?93cBr6rxV6UD^nO@M|l5Nt4iY2P{@m6I;5BM}EFP zKnMBeywPFj=hK$k+@<}BS#D4Ak+hj_dEHVq3UVBt9K!eU@j`OhcJ(u0zWEJw`Q|{l z5o@`nk*?l$Dwr-@&*8T?%Sm~Z?F_F!AJm(HeDmQ~#b%Tf?u)R5(3{?u`I%yoVcdwi z!`p?A9}G?1I%E!meKV`u#0W6o`~fk zKJA>J?{tmx;I+&ojs{Jtg%Sn9x0marDREZviu*`DqQL_W*3wii5Z~-|^b?q_Ik>mF z2GVa{A!FE>M64k|tR%GDleB#vdOcRo5=wA0SzDQV*Z1p7Dx?ng#webIXN7z0v!)xD zu|lVAJ)72jEN>U)+HN=q(KX)*0)Xfmj579ntEVCBhG7I5>u>A#>}Z{8c7FQ6;i`nJFY2t~`>gR&3Uvn|42&94r^dqwg6-7cttnwtAGq};5|vXt;F9{P*!?9&;683R``27{)0H6j zfaf`=rAh+RHU0Nq*Yu3s_zC>hVUR=AG5#?UlAou~bNEH**$z2RkH+16=VC(I$~0$8 z^KlI335h4aXo*6Z4X~>BlUO*lIA^PkWX5>@&2t!|SBVCuYuuqr*T`cUZYEH4XR8k~ zqB4px<>#v;rrd;26y;O;g<61g&2!?!xp^s>8FMCwt?3ZPcg?15l|pY~{5c}h_bJU_ z1ew#o=LdMI>;ls@CD5g7WO7T=rcdMdhAr3`WD@wYxURRF`+~98?Boyw1wnjC^%`!U zq4%+A(&Tt*tP;6D)VoiL-el%DdAQm91!*j77W>81 z1^z*KxdwFhKBmwA_C4}EtjhzUYhIPHKv!OK_2ePOXROG69m<<^RJLxXp!K*n=86s! zQPte*rl7ngC{?(S%;tk(zjDiK(=`O7RLsfuotpz{nclm(M??vnwLSmlIh5JyumHgVo3D1suVZ8{|b7JfOrOa{eM?S@b%w0I-u_lYp^Uv zXdl~>RD%AU6w<31{&KDdW7v)#2x^=H+oLD#JYuyVKVKn6(~FBN*x{=nME=8>iJbcD z+^E5Ij_;~wEWyWN^AgJ_uD@?+*l3IY-`9h@?ti~tBCGO4`cUnH(!$bygFOvp-Tgm&kE-MySfp2_5#89kgb_Rp_W>Z&=Sn3Sgz8`Cer zx<+Gs&>NVpabT&SZ^PO&gqdk1utRvK|H9&2PuHXO!hj)=o*zd6&auP9+>$TYXBz;tshluJ#J= zVtq#KK9Fij`JpX0UT4?F^@Zq7io&cb2{fj_w=WswujtQ*yZ_hU?Z4l@|9U_E`4_hn9^Qj236jqui3MOu-2?wRX2 ziJ)A}$~>&HwoKD5Q7s!-*T5bB*a4zzR2{h+rzVKLwZAW8eg$_WVvUG5TCKhJ*p)cXwYP*9y>mEJd*IEWM=#ecy|>S*%+;?91&f z!hFxrmF@a2TlC7?V#s}=LLx#qj(py*?~J~TaOLr$@?;mI)#Vz+$*y@uMM7kGuMSZt zqrydp;s4|Q{nz{PG!SlK2hZ6pS`O~n?Zwp$rLccM`TbSfa)Bc<1U`B_13#<=SK1}m!$bYJv zTQjzp$`QjLrcUmL+lE+fp-Q9J-OCv7d)t#eehU@no45b&+ke#oAoCvDTM2=fiz~z<kmu-7m+mSet1&C(z;mR?K zekU5FCIe3_;%VCTCgJ{5=l}Kh`mef!{GR_@t$muaA$d(3-PC&6mU|5x*5h0TBQu|m z`KC9CBRbC{CacM~LBB_HtQ<+`V~Krk#jIWV)~mw98gyNCEADXxZh^`q$ z$N{En;63i^xfKvygR6IXLfD<2)TeW97!ZjW>t#{Xm$^M}?+7a`7!9hs6{4brk9x<8 zS>r?2A{6B#$1}S#i6OtL+=b1Qk=c-(y})(H%~m}Dr0!moeG?ALf4381qEtZd?uww< zcOtz@V+kieUnKw6odFvzCRgc_+ZqLLFZkvER1bI0ySw^>Txme(eXBuAb=83=Nmxj&>=z@(2RUOYm~mj9+Y@{_2rIeXiRr1?*3ysotCcSBz<(9ljmx0e z$PnjI&E~AAtpncwPyPMp_rXlU$3Xf}M8Q=1`vdJ6L}Jx*KPhKyo1-1bJ1 z2gwRH)j;p-DUpfB%E!hR!8cD~Y?_1Pe$ZiT+q!l=C$Y6OB=i03w+7ZVmEWuYor@GV zLx1me&0EhOF*|5R`vP-Pyf~63UfihaO=#>f`*2@UXIep@f4|8gu8j|_swvmW39@4; zMCL>yy!cp5Mh_u*OBk+m&-{&Cf=7kUrEQj7-w^NIzuGi@00AiESaA?li}0wU=pg*=F`dJ7#4I#~an4(Sz-l zBHxb8<25+1p}{Yq1(Mg0MVM0BkMkRR<(g+8GaYKwD1KzAVmMl;wECDKrUVP}{{~<0 z{L+)> z=~Z`Y6xpE`-D?I*O1zj7Jdi)3&>gLY8$!_f_q>CAe*a5H|8Mnl=mZJs%EPoWi^;t= zs<4qkdILsNxD(PsgZQ1q0F3-MT0qydXWu4bp{K zApN%`%7CFaTcvVE?GBn+d1BH8(utBob#lc-xk8juuwo1#-Brjp@r2BKWzZG^V#N2pKqTcnPxKw^$ zJLDO#j-Cut1>9niw;%}{&{qHDFg!f0Ed7eWC=Av$+9(0ez;ulzbmKXa@_eoKM$;HrejXO1JN}*RPR)gVv4CB zrz5+2!srec!fkUiAZRYODQ~#{JWYV;Iq>z)r{8@ZA$j3{%L$d#4AQ3qytu_3WqVBb zHY3k4UtK_+qyN`(!k{KZmB&g%3q%ht1T3Y)qTULGR*2wMkm#ue?$3hW?@h&OE?OCZ zJn=IuLyFAoM^3?iGB|E`>QCP_v+4LVuzEx44kwx)NZqMAQbSixcp*x;sY{4@{<%$F z#_ohx%X!!5Q!hb`R;n|VA~WdkE|Qs}#$S$MQ@cumys`FUn{FY|kO1ND4|X zpk1)8agdVg0HSLcx!a&4CxqlRN#t1llMiLrj~;YUJa{S7>W0C_yiFMkbJvGDEzdnZp}UflHdP&zOIXT ze}%ykLoX)9brJG>179J28FCH#pM7)t=KLRfx!*JftDG5~M%NF}zdRd-JZJx}`Q`<~ z5d|@NzK30`!T!9mvAcB+%_}c1iuLd{Y*TK`Son$H(^KQ~ej~XqMTp2oN?0D>7K8l@L-IQgVE#oK zy8Mg9iyIXx?20D?Bx2C?;qRN1gxf*&&Q0h3h;hj?W-t=RDpw2<`>@7fs4IDPaD^ z13LT*q~EN7soNWl`PIFCzwRvV>VKz z%ur#G14Cz_UTV(NAaM?bK^k>7q~Bb9Sq@A`GDDY+M7{hfMzdy4mh2hP+!_^PPtle4 zGRiW*)`@Qe?QY-W`?k%VixCNa#4vS6=A^;;E&Fl=>ObP@I|bFw;%q3TM_?Uk&+*v{ zn2xN64jl>gty=|Un{NAkg#)&kLy7-a+Zw%-iN#}wrTJ^B~05!s)| z*QByc($n22ItR0jo0QPR^0E zoFJGlVY>7^!1(eIy7kW?VtS#_kt-9T)yX7*+E#h%cW-4M&pY z$7RAO|J)ROYa$p}m?Z7dc9RvDEz@JlB3EAr^F@k5_BSxTxI!0SP`{+L#j}tJFq2xs zN*g<1wdjZvz6#0{6p{0{xCQaWqB;<*#fsf*;8V6%;+E;$ zFqkj!KKeSq_`(KVe4&l>?e9^Ei9syU^l3aF=RH;^U`%E<>cl21Z@J?OX}nFqHrn%D zGQD_(Z9>y^wVp>W!j4v_J#h0wX1nIhkR9ntx%m{J1 zHH1|?dYLHK_WRF}^YXGle3@^T9`LlqKcTwltBGZMn4Vm^QweuaEWmYSa$4D2C-@W0 z7v30tcVK)uzW03b7s$=oQIkzD7U3S*){V{ZVz1Y=&LC{atlVv+0`UcgWnGwFbL}^? z-KzAYkxkA|;Uk3lN>O<&_Nep)aYfitV?+VG}$Wm8C~*p zHzsDEsC=sX;8qoFcz13%tU%EA#bWlwXQZQ-Ip1OIUk$A8)17BG45I7Wl=z(Y$AbBy z#f1rwFY3_6m->p|RL-;P-bfR>Pn?U~1-5@%5b_@QzmZ;?d~pTh3z~uOXgGgtXZjeb zy4VMTh85j+UVZLMS37OxZ0OYT{Oe%8z(&P^*jqcSBE5(VaXuJg$aW*__X^4N}fmiKchG1iG z4qjnxBmpc3M})2%yvW`_>iI4y%t5sG=vNjW<+P4*e`CU+GPWZuzq|7q#@?y@7>k1| z_YQ^IJqkLzqQcl^TQ|M-AEUM?!7_EC1>je zi)$8kgtFQ43Vl4h=$6wKl!N!OB^;{@u)y)KrU`bM;Y#9|Zq$6)pb3e*+ykIAmS?+lt9uo8@3BSsCVr6t!EXeRJa23X=tuANAt;cjq^^NBiGc zxyUQb-ZZ-`!!MOKEz_=C5;{sZF^qLDS?&>&?ru0{j2n)2{@ zE!fHKg9YAy`^jfE#z%nhr2xA45_a8y@;pvG3B%6aOzoxqI*d?wiL=79(JTC_)4P42 z!^3twA~mHq_I*70!%8UqH3I|;xA+KVzm0?wrA>_zqg+y5s3WAR z!s{Ysi2~&mG-;ImDfN`7DLy0Hb+jK%TM0h z2_LlaaYdY7x;Ovkl_2?ZT?5N0;Gip~2#am+v-DghL;9liu~a-@@CeQP z(SSZ2u{PUq=p#^00gtNt9hF?~o5pdl#M?#ZdJ`C=@Gd2vl!0_6LA0L<9~Ef+?d!E? z?fw8Pr+5!tIYl%gzP=~*;IeNW2a?96atV$z(~#{rOU*9n)ay);j;z6R%_`VvpWO?i zk)QT3nJOd5{&8t1-IlJ8Jt9xJi9!h0kvxOe?||vZ4d~F3ko}eHLpKk0XBU2v&bcNU zvNb&J*^%v%9*36%CY!LECrtDbyyx*8o2ICEa!`X)gAu5tvkrPlF2>9+1`vRl0#1c z^K&*uEL-`VGPy6y0_HbjO>aSecg~%eQoY^dg(EfGL{Z6?s*Le{e@A^j7u}?D#p&W% z$KrzPPFrSy3rO8jA{auKuEC_;-H=)@BHoI0xsl!t_>OurLJ!|HnA}#Y@%c{Ixb@-H z45cW+sRY{SPHd({v5WwHVbeA?eW~!(?8O%H*h^=TH6hb?E-jXTvtr2XZ>z&2QgLYHFOTAZZ9x z^~@{3^huxx>lz1`{y8AJMw2`B{_n3mH*&#kSN0Uo80j8IYT9(*+6}qh{M(~+l<4CP zdJcbivVRq?y4c`tJltgZ1IB=pvN}~ana7%pGMSl0AY0NF?3-yDI*oz(W=H7q&5c+N z_^7QX9U+p!)5ZOzHgyU`y}g&;vnV%H_3rjR<#zh}My`6PW#f1s3)XYFge283+4VO@ ziy4qPM4NTu^+0^{2nnD$Y~8w|(B+%w{zkfv9QC%f_3dW!ZM@|U`s6cz;*1o~?0Hjo zr)!8L=gJ9DXBrgvx?d-oAajkPrF}V=`=CWS&su-^?D;Y&gfF+t0M{o!fiAwd9hPx; zmgNWc{ScxTWsRb3FgJQBuQS5ne@(#H3*w8gcdZvk+b65pETs0YJRX%ih97-8Td)rL z-M^ibs&FalLVPpl>04lY!GsRJK;~fyKl18ZrJj@GH+o@Kx6YTa5W5~et9CpT{c*Sq z3B;FIk%@2F>YgR}vcerPpor7Wnd~nes*M*dly!(AxbVe^g85>%)@~1kFO1xA(B+#q z@oiXI&1j!pnNlaOIq>p!l8Ph}B`c%#Qw1|7f&9Yz<8Pr>XO>ENIs;8bS+HU;MZ2{0 z=i`D@@6W-4 zj0JS{Ul^qrHfIxGc^>9bWSQbkh;*JVi4^wQOzXeJX2AvdSdyk`Q&&22-hfqoX}(Cj z8XhL?f-b@WsSoV9Kb(@=bi%;>mnjO;GGP6e0qE+#tcsgClza$WHNKSZxBDLQaIY)+ z+n>!j>&(eAawm|E%n)JWy83#S5hvACw0)Rv@3V^b|8Vz~QC0Qr+BaPSN=bL4DBU3- zDcy~LbP7lcNGP2G(h|}LNOwso-6<&z(x~LK*w>o(e)he`e%>+1*zX6{r#ZaF@M1YR z*F1j5d7S@q9`E}HYmt~$*8Mi?aeocvNSdd7>fku?K5TI$&!k6GMclLvYq@vKy=DU5 z_kR>gX}<9MUFeJVpoVZH7vl~*hE$P_s+EoG@H*m}?$3oDDW7z1<3VrcTp^Lmr+2@fcz>y2~iQxJaOt8g~sU6;N!o93(C>;$)=^3Q` zw*2NZORsg8uZ~@@PaqsQ{j&>~@o3ka$61)0to;M_D=GJ!?Dkdb>_44P!sd96tf3r< zEHVdjUxF)aa3s*Dh()F4AJ5_<9Ls%9Dd1}QF|vwHf(GG8V>wEUwP7W} zvrJKW8+5s$10ALio7kmXNll3XhIHKka-dIfSPyDnf-3vQt?yGvH4JjstVuXCRQ6M~ zPBnRMV7P;i(PnNdW#2VzZ7~f*Uy{-@4dU?{ zPU;>6AI$837^=xiF*rz$^+EVU`P#bu$>IT2UqTopF2U)GB5ddjkiS%@p|2qhji?jn zO2`)1VMxH0P2OE#EYi-YUT^;d(HFb?Ir+0kO66z0n!lv|4%A8Jcvi|SxV5sDg&!=s zpK(2g>WfC^+BY!zVvR(9Yx5Vtzl{6Z86}VBRyiKl*^?`9uyR(d`t;Oc()+a6VtPXS z%UXS^WY#^2k5kfSy>UmUjfqA`M93TW+P+_BV&LJ-BMn3SODPiH2{`{^2^;SM=D$g67Bl4B(VDppo)9+0u?Q%~r zF+M5q_m`-X-+wr$4)~Xb>X6XT1N#%z zudA&YduiKuA7-}F7(1Ldpj=~qcYhCDe{2Od`eVTPeRG`}G^yP*402Svh#qbI1k;bG z2zui=X7nfT7Xl&uv7?ZhaQF(>J_OY=x(5VC18+^n1%Ge_DxL^*W-WSaUu6SxC2xf$ z!1Twkic@a=`ARx7(Y-x`vfs^eJ=^(#_KyRCPuag(onRTCFnHrZxCY0lswK}K9(!p% z^QT&>?&CQy_WwQW5kvV=*w?DMKJopXK!+A_geycyU(BA zo4I3qzq$iczCwiXJ-SIV{RXlojbgq>(;zp5Yp8`2#Rt%RdvY)~GxHRkm#`X*ae`SX zjXGL3tg@`~=3Sv&Gs@l_3x;dbsb*lSFYB#4A%zfpbdpRSRvxvVUGK=ejA&VZ=V)zZlkeyE<=H6hICd`E1l0dQUWV$6=AJM}-^_n& zeKRmG`#yQ@FVXM{Q`Co*qw8Al1iOUu>Oq#?p%N_W*FF&6ym>A$2mAm7T1g56yBIhs(Dxg3wahKd{iD^}I_`!0XNQuy~JTTz~!)|NElt&l^C$rtR7p z9M=fK7T55#)>>@1e7dJ=qArQZGs#t6QklN?-X#3pb&U4{gljS$Xb-jEzhhJAD=RqH zpjr*%Bbco!2{*JnVzT>!&J|Y+aE;TSW^i0%bL+Wg>tPuy+Dm&O`>|IqIF!p?{OoA; z&kOe&=fY9Qe*obc76c7}3uGe+dciWo8tHmBJ7#YhYXjl$dR8l*2N_;u_)xByFcY`} z$2G&S#WmH8RL`I44;-zw3vU^@>ScMxaJH5Cp2E$~N+&;saLvSW+#h&+|D7@+nci1_ zYd*f7aE@xiwI7K}OPOE2oAzxT$~6y8I%mOg4K{3Xjc*yFQWplC(L#}eFb2(e-p0?L zy}Qp)b(tE`=;$C^L(dR=$T>*jRV_l``2{6=`~xxR=?=OsV$To;+FgAW6m2NiMBek^ z1jjW7u*EebsO3o_+0T$R&G7aPs#n)#DKPDNno-0q3_gb4yzlC1)_HQPFM(o?tXy}R z*-ZPCc2v7$=le#Pa{^ivYMqX7sJ^HZ+Npuj7i%Q8Tgx>-9^kfIv0tB@`w%|8iDEk`Yw0qX;D=5aZ|A8X`f_IJys%({ zL~a^nG|O?Ms!K0ArTdfM&%i)mvUR;Gg(4b|2P9Dpg3*_4s)<{l2cUHN4F$2{DAGBn zT1Mg^?wtrHqAROnEgp#^)ZK865)N*t`&B2aYZ^WaZn2?=M_?g3IT^a|_-kKjOJNz5 zX(-nql_#)(;Ti>dTiD72`p^8Oe8mjhI{E(wdaLXl!wjCSQ8HuHOM-sd^q8U@V*Y#J~Pz~uo0u$2e!&&pE7?6G%d zkl1=C(rz;BZO94Kk6vomeXMM|+2@N>W|pQTz{q?0J)ygb#h^3huUT-StrSneGHct?w+-IXixM%=1FHM)uoT zSMQ68kLe{3%HT-ZHk9rvk2s(mJa~rRJ6t!1uVe=0n&7AVTHv_m1#EB)kO$z)L@60O z6dX>VHun=dEl|`aN$s@l`HV&@zMFJ2N7q3*T;poRG=G+O?<(MF$*$(afXS(l-<_Sn zWZOR~Zua$nZ#J~~4Tftdk?z8VZwB_opQ$&Vc%zjLDSUrpgmgU7<*Ko2-A0wK$4X)6 za`PTff-E5U|PNDjOY$0a_PG7WOLtlV7 zI<(y&&WVwP7Rl!_Xs+MO#Om`+?&PP|c{WN4*fBu#MU!44*_n(K*~?hiSIHYlRxO5+C=W4vW?#V?y=A1s?)@;LDZJ2uBzXC|S(nSrjQ z3XE?i3+1@=@A0&+c=Wn(%A_X@H6uz$PXzVC?nj7Nvsk3Xfq$qdr0!Oqm+fH+IP%Co`mR7A*`fCysJp#fVQ_W#?bg@bwGZjVFG59bJ)Y0ll-V9b zMf_?(vRvxvNGnlE;*h$NKesj_S?`(_ad?dzgq|eRBr_D=S0K*NBjl5L`12hLP4Ykc znW$ z%UjwT(yvL@wEWS}>_W!j{FCb=YiF{RgRiG*OCfwtyzi!o8pfSGb3o_+r)cV1@41*JKc0 z^m)yZ`%?%AdH;m)G|G704&RB^fjHIMjEJD~Jwyw+Nb8w5z8Wu1Pzf3SPUHDN_kwx8 zAOgqJhOoiYfW8!?1%EcPpQ@_Rc=N^O2=|f@Px^iD*RJ*Nj7I)9{TiYCnJBNS&#zGy zpZSEk?8Ssl#F${bYTDlYusGD89UJWl)fe+O%)Q|Br4_dHg`@ES`s903qc9gjQ|khC zPkDo}dRB*D6<_!BNTMP7k}kc4YpjsJUMXcIh0bl0P=vl092D+7R4B&8P@uHXiv#G3 z!VW4peW`~neL>OmEPITlRO6i^^x{QGz_!oxttX`)3VVt8-{RctXF?cPGBJ7OjR`O0 z;ZvovS#e~V9JQC}nK2$bOHN$&E;jzsnomvpMXu$4D|Ad8Uoaimk0^MSw8df#uf5a&N#_f+c@n#f1Pmk|GgLwBwF z%N4;X{C8s?;Uv+syK9{uZs*->+h-gv44nQLV*vgI*WM3|f3XT>fUUfFVz zCyGSjVRLWi*RYRL#y6TB|*t z>B}YBLsr!1_r8kX+nf{d_!83->6tcwXNbK6A9Dn`kK=YC+9Hn(^3ypMcTV?7?`C~z zKiZOblwoGB9M6_HZRk%5)fbuo@jP()LJM2^vi)?B;rGO|h>x37a#Fm5D;j~VwE`7X zbregC!6XoU`8z-^DZ_X|@|iTc#Qz?*&L`)nud~mO3}tId-grLwoLvspmtbKDC2;!U z0~`7R~9pqKNXG^b9 zIa6r~bdpCauuAC|?Kio7?(s=^MApiZI zEu$n?ym~@>Mp6ccF_mCc#g|h;78l^}JK4%4MZF%h&LUA0_r0@FbNR6%y8E5hm@wR` z9B)kmV#~blr$wFF5SRb_dBD&8?^nP5^-Uo6L;tqdzJB~hi&-yIhbY~iK2hv4FWVpG zk0ATF&Lre>Su5oVHWgpHXSt|NeX%I#T*Y$S=j*zXBcEh4e_!-$PypJeK>bcG0Bw^0L`)t&AyPZuB*|6Sxe-w9JA|D?sEG1v0ei9jqWL4L9y~prOH9%-Q;aw9i zZ-OzPFaAh>!RZSVZ0QSnl9Zi@$01rDUFYv12j`jhVoaE7*xK%_YBjZNkmnDgpM|tL z!cW&E`8i%oW3D|HAEoJRvwc;TQnlHMUt{%sP<;tCZqWg!FJ7>rFMxmXC?|IPBOyL1 z{xkJXc%@yj=|e{&`^vN_YxhNoV90ZPu|d&M%|_4QWKez4T*q$(qc7G-xVPTFP!fDHz>_BM)o?dE|3$j-;U2F|#wP}*TsR?!zF>1#@c33~<-M7No5t&sO75m;y^Do3 zS@H)n5ntqm;?x3EU&56UlfdXpwPxV0f4|?dMdMDJ$4`8%Kf*=}O>5(e?<*cg*OJpO z$DNKvK{$va{OaSh+tifD-@^I=%lkpJTANbB>#3Dh)|OFnX^6ha3uj0@(_4YJpILsE+h92_ zS~NZ}=9$pk{p2T3YT=~WAXHyI50>FUKUn5RJcfQ$> zujgJ^&-RF;+8Q&`vKo%3g=r)$o%Cj2mTxw@OC)Ts+py@H^oCxZc6SdW$K$jWIF#WM zI9)UvP6VJ|qugZ)u3y7=Yx_08oc8&5H|IH2Lo%(fvFz!`xP~VUUReUq+*COt;VpfdV0fA=bpF=Q%bKm} zj*0Cj(qU0<3XkSe&v;7y-jlQ()$Hn} z{1E}anhiOT_kC#HS$-RC08@92?DViP&jjqFyMLH8x@+|=|AE(j6mpwh!NVZBL!+Q> zhopt2H+~SFPTm`3tt`H570pD=h*SJl_?clc?1(#H%k%MA{`(iL-W5=u=9bTj0K?N* z#X7h4{2AcmRIT7`zt*0G!QB(c9bdtM8r>vMdT2(}S^M|Vrws3^@)WCu0!5>^uA6N3(_AeM8w>tS5w)%u3Zzrb3Xh<#1 z$5Y>DaGkSFoRz3^{?2C9xHx>y2;u4bCxp_Gd35bO!CW$k^$`i@6Keix9p9)& zZ-XdCxqKjXx8(QrPtM~tEEcN0NV}*73CF{*p;zCYVLyv~R>l5EO6(o9?&ywR1cRwN ztI%%P>Q6skxYBu*$tXa#cSOOkzJ1t_jz|$8ix^#69Q}j|!Zkm?RbU)eYLct6UDltM zeo-R87`s|9$U4hvGr5!0BBntHd>}#ZPW6Pk+0~GfT!1_FdlmjWCU5P;x7o2|@ER{^IbRz2BWGhqk+>g91`_154ja z{W_G{mf0&;WAqdo6A}S4-y8Egb4O_W8CIgAaq!I<< z&w?bn+`fBvXz5VCW@kinV4OV<%ea}JlJ`)nL5RX9bY^x?@}2SjI~Sn9l9D&tT$G<2 z*ja+U1N5iW&d|X0r>&9BZ*A@lNI%AF{Rd&PtE)Q@za9^c{E--1GK_4wtzxN-H~zD*0XrZa;u$Q%=8{pn1zJm&pzpN z>qn`dYJIINkh=@5JFRh^Ct&K166pjsauHyjaEBi)mP06?;t^`Mohi1g@^R{t^ii;0 zbH9)f!A&m0JF>^ORxC-U{TcJ`)wINYbs`p3v3M*zgg@Qc%ZARlwb1@FBBAyvxc+nj zZ1ksrbB{?GLY1mX+r;aA#jRtcijBKgbp_R5seQ{TJ?tU(5k+1e>B};FHJJ*&h{4n^ z8ZjsL9CPAN$9;Xg8DUC}1hn~3o_-M~tO$mu8QHmEt52xJrBpsTI_S?X+$h4=I+rU{ zI)K>mm6w@!(Ad}?!Zo7Cb|^#UkAH6wwghoB2!uVJo47yU3r`_)Xi^Y^Q&V#ZaE#`(_cQ*aO<3D=p{o6R471u1FXye4#FIYU=j-a}hm5QiY*7H$ z$SRG1)0aco(ihZC$JaKkA$;oasHLI@>Y8^C+J)ZY5Gv3sY{1>j(Z{wSxA*NiA3bs= z{%d{h)*5g8Asjctx=SMtx2ShW24x1wKY4x|gVUE{*wUA8rzktktEAh}sWW!P%jl~~ zPO~0K6X=rLPBJ=75PiW&VwC7FVAjWMKxR(d@me-&)M`N#GhBwdn2Sd~d7}9R=-2#w zhyzYvuwhGIp1nG{GnX7Ym3r0|&`!?QQE1ss6eS*)75C)W`{rEzehqQ#MsI{Fd&0Zr znZlEe@^W(-Z_;PplI`<4j#KEzJAl4e<(`1k7nfVFFP{7qfd(Cc#*u#%T9ChetQ|2n zPT>?DS*t*9l;MHs%e{QG+#^D@P*&83PE=obT}r9c^qnUh!zxPx zGZJml`v#I{^^=X2x?h`%ThatqL-j?a=?v6+D=3i$VMAYlxvpW1Oi>=1W5b6z3k@E1 zeexJI>Op9_yQo1XvCmr}`r_=OFf3pEu062({*TwBOz#YSOyjOm>*1YN^gidtgIC-G z^u@9L8jQa1*?+k8b6xOPlYfGPdp1w=S6DVO$(}P@QK$PiQPa|{UA$m`^vNWg6!2w> zy){bDSet(i`q$Z}E`9wJ%+%*U;r=K3VYi9szvj9ONWkP!+f=N#cCHJkJ9@LKxcf+7 zD7LYqrc~R;c3JdsOV2nkGa6qG#&tsKZq`oZTWQbAOBeeQTBqQ+r}!zf;Zz!^sV?74 zk_~0KpKb$ncZRP4uI}P*ZQTKTM9#0r)#D{d{+zsiw))8o)#z+hZs?=2`C>KovH=35 z?ymdguv^S*3~2*B{PDm&_} z%|C(M-Rb2k?3^zjY>SyCslPg@uPcaT_G+x@E@u@*TkA&H$ z@Hx3Y(hsIIA=KP(qabUNnrUd=F^-;DfvdZ#TU&QPpAaSF)9UJj1r%%FgLUq%Zn%Mz zGentU>6(JWcEp?a+fu7luu0Tu7-ULn7^r@HBzKD6_ll7741S-G^?9N_?vXXsKl?W! z?~4QZep_VN@NvNY&5GD8=N+0BOT?h1K#dFDE341ZX|3!1*dN2@cT*rfj>p*wMb|RF z7L)y#nWhNoM`Tnol$AWmXxo*jOqy>W%7mK!*}us#LT3!d$GI1Kz{Xq`kVAk@U&`{_RmJ6+`A?=s&z6`kp?T0@M%(L~a=}aXF{@I(PQG^iyu20wwTRx60 zOkh{iKjm{Lc5BnIfMtfQ38OugNrnvp%cR~3gs1;TCDxJ&(Yc`^c@p?~aDRCozi_ow z!thGcXWgZHP0x_P_n-Zn$Q0CV;CMRq*77uPKIfA}RIH0oh~8ZMw=`t-k34h9u9oVB zn)t|im4Y|>81Q537%GK-K1LkC(nOmsvDq}}wu+tN8o^W`ELbS|({(BmIEtAX}ZO#WF9BUr6PbNA_4JYj(JiV(Y z$&IO`*OE&|DcQFuG9sLok``a6gjV6@qoVbfl8!V5y3f8H{V^DxwhCQ`jd?;~uB+KB zuO|1-xARUl-u`(Pi+x`dCexk~nZD00XcSbC{xk;#x-jdex+w25)5p%@(L%@N?396} z$*2;Q-VblyKZvX}&hnRs$Q1Q;W_M+<@^$)^t91-U6Jpxgb4S*IFV{Fwu2FCP5Cn#6 ztdXW~?OYddPC`G1hs4{dN$XO7q)o(!6)81bVX$8Q2WP5g5m3{S64#=+J(iR^M?^zq>>R%0uaX!C~F zj~};{g}D|#xgUCV>fZDT3w#ZAel2MHW~*;%qOq)G#z6^^Jy>jGOhe&RvePi~OoMXG z5LLh)7_LdD5{0ckp=mbd(>JXH$xkfWS~F{6dTI@{uP1+9M_kSv`7QkI>#>EadT4^C(Zfq_)jW zSG1*mEd=EnTPooYa9l%pYqZ1oqcd+ghaw)RR zvfRA)^6hR5T;I3G1|oA^qAo3RvSpj#ak(@C{;RO5S$IRwU^8f+kf&-%3tXSj3O4$L z0N2ziY=>L#*jHRf5T-X&KCP6TDL!7i-{yI8;asEy;hLTi`ala&l6TV_Bb)5Z;X>41 z5o%({Ji8`^qcpT1;uQ}8u1WRm0mn5TVT)^G4s-0k`-b&Sk|h4lPw4LZ`TF$GPb0&~ zwkU(<=KN=UNvLM#IlX-+gFBLNc6NspY5mF5@e=+Vq2Y;v^bNOQDA$a!&AkW5H9WAv zHGpqUFkY@{&biNVnAMa^g4VwfsF0HFRq*6=sFl{m38F6;U1o;}9#7$W8Eb_Ci7(iH zSri1oiM2Xa8hy{7m?rk~2J{6t_cs`Qu?pRW4c`px7a++x_i`F%M!6c09j}Yh*ks`4 zqSrauPji{hh`X8Vs;*UtfV?4UVAIn9B~|5O_G@junPH5*^ZZ5vo#S*2?iSQHN95Tl zgYnJPnq9a49G=?{4dbVeL8*gNJoygPDltu~g|RMV=@Gd~2=6{a^yO9a(|k@;qAB%H zb#Ia0in1xa_*MD!vL)uJ-;||~C4IU9R9`sr%`U*`3sy1yt(_+X>h4n5O-@F$GC!!BHQjE7V5uXa42hi3|LdNavoNZi+@*kW)q$J+3L z=krWL-9sPF;>?qWtHYSZf$04M+;=9hwcV)dr0m3@bw|@qbqc2L+>7tS7S~`G+LdRd zf4+dv*|F<+NzeIezNaDaV-@wes3rBy`v+Crwi>MBMIX{W_NAhk)jl1O=Zb2cS~)H6 zHXF4^sgh!Wa!uxlgFHB{S%VF(0dlCa#{J7(4qNZ+iSrdM>XCX4l*HUut!l5=wAU-| zK)6P|*Jr6$BeojH>vhP?aCz<+Gnz3qos~;1`F0Y z<_XQ-u}HoDb084%C;Am>3!`=VvCwHs2y3vr_w?*BB!|L#GKWLciIVWw2jBleQi2~t z!_mBaRo4ZHbc0AwbQF9HG>1Ykq38#bL-E<4+}ivTm?xY_*tOEX>~tG2CN3K{O3$e% z@so0Ks^lh}7Ng~Y)E(WCh`g!zwIf|jXffMsE;2EtzGV7p(d7Bd)lI2!Z+tm`r}eGX zz|>tjl@e_6^u|$Rns&PB$vNEfmoN7}95Nm!AoLE$Jvwds*6s@7Y4!yNdH?ITsMBq0 zY|3cr2a_oepNjSTHj)v@3hUE;>Kp*@v6 zFx0_u&ET!&8ep$QTgOC5VNXNh3-yL%(bVZj(uf%Rncocs5gyt2+`NAxsO~fE_dgCF z{2g;^%QTp`wyMttDC4&HIhJF582DA*#X-4-{Rqw;4A)>4Q{H->?u6gXt9GUqA=Qsh zrNzv{RFGri6``IIt}j;#C56;oPw(r^V!U#cGLpBOq^q(l`6bPC!)2&}k-e%fYgrh_ z=m4HBNNWXGcm21v?f{-n+kSD5D!6YqR>L+4$Wd+V!a_fc7we87Mv>9<7}p&IK27b6Ljr;EiozJuZE=*hiXKTjyj?|sF}N&Vp4g3>3%G-jK{#h!)~q~(BUWvkCOdo7Ysr4A)sG~h44o6qz_)OzJ0+kQ{p^=5n0#0OlefhswjL z1(!qlz(x)Qa1HJe+_!*UGfAS$ue`HP=g8<1gFJ$5drRNWPWeh9`aJVzImTS z(&F?zdGm=^YPcbVcC$5}S65F7uqOIh?npZ|2_!ygO#$Y*R2l-o=}QZ2=?nR6PqFjs z*A4HFLtmUVauS{S3Z@>hpqSK^576B7Yc}_E=|s7g+u;#x$UxLv)7{7 zykVVnYRNptIo_@K`c!r3JwssrVLy+zp#N}Br=e2|pfBi;=E3QU7i{TE(aO-M5P9cu z31=6t+1m(ychBTaabA0i`ekeMn;eRR%0%^*8ab8-0#6CuAzIbQ^lMZhW=;d)y9cUp zyJhO@fPd+KcmPgc5O1x%06ElVVqS<$#*%!h9F-d%^{W?;$O1x>*c7p<1|Id8x@i-ARz&BgG?FOSS)tbY%);9xOgOna19e$x2JIY_j`g|s8E6VXc>5XqbKD_#<&_#~|pUfvdS}vr1T^npndMwyw z;~~m`Oy`!p3#~gr`qj_i>JIDH)*Zk#MJ*9h`UxaOqct+y-e(5^^ z_aJqbk@}UHnECkh3tyxu#{K(LE2>??aXNJ_iu4S>On3u3b%zUmFkTWjI)>ZNQuq)$i6BiyfXBs$)e)KZ0@%O1z^H7_Q;7 z-?;TTRCFKdAP;FO!N%R{*My~YzCl{`a5fio2k|cOsq1 zgiMNwRSL^0KeUJQlk~Gp4frj_%VrdaQ{=rpwv1>&>7erq?8s<<#y|RXTXOK5m z8!LVeerfeU_vv-> zxoG`A`vrK^tX_iQ8uwxh*vJEbxnT(g|HqjdMG+%tj8n5-^|(j(dA%O;(!{4eudo(} z^{to94y$7hmk_sohwd^%mZL38^mQCmy&BMFj`)CH${LTeZ|2c0<YKqA&G$F_}Ag?xIw5{(Q8}`ZMiz#8LI0Nz%Ks2;un! z6)&%$`ZCwkD*#Sk>|sk^o@+~^(r3{8dC@S|zll4@`|GK&U(f<#_=}CrGz*Bn^oICF zA^##}*DOFc6VCHaMcYu}PtB*^8o_KwG&!kcLnmXZPv8@q@kfn%|?ah0Yd$KtfOvq96_7$<6Qc@WG?#wO- zhmQ{{2-U>;KWO`o>kaKwu)jJ-1lOnFhmAf3Ft=Fd!(`6LD6RP1R7U9@Dg%OVaNb9& zk-m>M*oezg5Pw}dbvTKcxb#cgg5YC3$@cqv%l5eshXwddon+5K;ddi1fIh`fLJ=^1 zis;F~TR*pGi5K3i+NSJ)zErImiG`}#y#BH+1T|4-E@K)$38F8W^u3*wIPk86Cg|sh z9BTe>8-_2LS(VhoxCd=BN%bddphEGI-3eqUl25( zq=D0yIoQ&drrIy($F3rFToP#xX%DM*Uk<8O@5CKz?__+KMS|!{zluA>r?FTiJt@T` z^9N6xco`T&$(Lh4@(*=?(D^ko?D=xk^3D^1c6IBZvo;ope>u_Ap&4}Ah<~w+dIMYd}fAK@n*aYWaj$zBcWFsMYEF}|BlJl%{ zY0*!8d7RrPkLn>7lI$Ukmj}_8RPn=xw|Cn!<}*&ac^aos&{>vW#fZNpnv%_QpZpDP zF9OvUEuuJPaQgBQw)BO;by1|w;*t2YL() zF(OUK($rBneSOM7tYY=FI2;gt;hZdzrn*?WJD8+sO%cr^h@!zU|C;VCDM50J8HNJs z=q^-WtOjM8!Rd=0Z0HN%Ul^PSER$O*zbP@lP?s$HMaK11yoK#MjfdWLISj3>pCc5_FyhMqk{EX<^I1ym`EzJx^RN z&)TAoqx(@Y^kuu1pnYW30R1_(w+qC-jJ#5ioTw|?i#cAS&UgLZ>0wqFe;!GW$E{C( z#JUoyL=X6v_i!uV{LA>Q^)JA_Q{51f-{i!*=|+gTwI0`u?IMe1_tGl}$*s&sG}9*gV7gjq_|tZ@3eJe zYwJTivakOzH%^oj=Ha4*PDKdI;&)|+qM4h0r;;Ua&uK|XrH1_)7M-uYWpUceVeGDl zPbqcvKlMd5eMAGTyW$PEZZLJXIynnl`TjGKos${+0{#BWyl3}b+?o17UJ4H{Qqe0* zl4U>!$zL4A*myU3(!w-07ycfftDY(ssL+b89#Qh$<;)~B1e)4^_hqbAh<^hJOL(lQ)L*wbC!|eXt9eicYH$uV&ht zFg&*HDD|MI6Pt(l7j8r!3TE7@O1mAVm?qq#GH%yZUvInt@s-9L$-opwpF$vi8Iman z<6rpf`)_T&5A0`B{Xoz0gha_b75+nEZT#V%OX0tT8rb85!NZh$h!B0Dmu%~bQ%P+Y zZyRYI5OOfnc=~`%6-i>bVIFL9H)WotQ2s-VZ3nO&WJ( z^F0@?g4P{kjQuK@x>K;{tQ;R99BoN0W_Cxo5VAnV8~mcaE8s*d)|-ov&%`S)-IXW3 z$;R;5^c?aWf%qkezWsA8{kLlg%MZ6AB-^&4>zi}io}Bd}UqypEuCI}1J9{szR(Bpo zEWXJH{yr57A<}V}S@pgn<^s~iE1S}j9jwNemuL>w^^@{Xgcf!yk@-_fUTQG>=g$Lv z?tj0!>Iyah=gGhBsaV(~ml~aDbYURc{qb9u)-V>S53H_sd2ebxCcj{4WE zk08p^1LQr~Uef58vkV<9i<*#Tj879#-~0mSZz(w6TslbO-s?C1m0dL|pUNIfH?mUd zirtb(YOYKr-^J4aZ>C9km)k_%@#@Wgy^kd~eUY2{0$j%-_wlbir~VMy%|06XlD2PF zD>Ee-CcY0H2ul#_Uut>E>7N1jMNTKw7m>Q|XY%8iPe+;s{GEj_U7yWT>mqYA7B_4y zMg(mqirXT6MwS29@9n?ezkhw6xp!8>fNvJHZ1Nj=xTRTqvhMH79)8YUxF2|6Vp7S_ zEhe4{`CPvONFJHH6TB&`m<_4S4AiD5X&hkvjF{`BmUD^soEl>v@Xb;q-@*CjNxtAu zN!y`@ayj%aPDrZVClWg}rU=rrdD7=D4EGQsGv&_MEzt0G-@5&mdMp{gsk@u|0$gbz z_whY@2_s|H&3ndb$9YVvzLXsZ69>PmknGTB#K;&*z>Ws)3qi+0aKYtvWM}!7noj7s zS~4$&-x8`bn{K|o9u80W`jqQRRoB$D9p3-r{{8Foz>OhB0=XZHVFVl|T+V%inOqE2 z5oUL(>MN5Vw>ZKtZiF~Gkk9qtmBgpEDLt>|#!XGt}Rps>%lz%-og+?(bhZT>&MpMTXG@b~}A^$b;k8}Q8>o}oX~ zopPo|OVHWVVo6QNcVAqG?x(lH3HvFBKt9)DeR6@HVo|;{mUAK1XyZv@>ockY^|8cx z$o|uh8gQ%0KwlP{g%b?dSR+|7m+;o1HZ-r`&DTk9F*0ClMm9;D3ppU*$twl?m(N== zP6hq*|5$gngFZfASiVvb?-^6`Ng5^(N9mIT_xb;~x|3qHJY9~!qW%?*Y}#mGWiTt} zvz^|(v%A^(Sj+=bcML?uHs9`dzO?1k*W~_d9g1dkA$F;fuwH)keJss+jn)(B*KAU3 zf~z~mTU&R)9s`w&^D;Jd3obd=!x#AQdyif66XrD0^?t-r$Mw`g>h4*BA6;(GCk49L zP+Nu;;>fa3B#igSy=Z@ERPI~<9&hu5)}01ZK^U02qeQZ1E}?I_yL)<6iFl&9$n7An z`aN5Ejmnuq^T!Fou^9uA-dvf)j*Fy_`M*7fIG}$9uAGqXubU=HI#1w@uH3U_l;nz6 zPl(S*%HS}j5{#<&a!SbJ0(>8xY-N(7UJqJlk*JCL-dU)*{8$m){Z4C47;aULwouR#RD1pHebv{I>l&I6@BvR|eDk3T`oqzy zr`jAB%%auJd(}{`vA_0B1jjYGu*Egg{+d+tINhBh58ez$pm2UFe6;py?1wZRovAlf zAmsb7GaYXtzM0YM>m{0x%XW_)H!t|^qbS7Q9iy`!?pe8HyHKv-a&prL$2D`X!8O3S zE8-RB2gf98c@EJ;gV|r1>yGYHcX%n-I$JBKK9PkyCz!f0s?>vX+V^}@M#7@CiL1(* zHD|3$G7ahyWUEPZW@VxJQd*Y*YMyCzk||al@_ZzOJSYD`o`-{t2%Bt<=ms+x%DapV z@=6~e&rJqXEUm}{rys!c4SK!d=^NbzuE7vK>?7zCcP!7V5>uS)%fwZD=7eLEF{i7J2jKv;v=p?kFGp#Q~Gtt1vcB}r&J zN92ES)PMY(f8&Jz>$$1gtd9ry<}1X;4kG&BWUnLiM2&Ln#i}RMuB)T^ETccSBHipY zR>mnUYURR{9m{aOkT6n|enx4#LLTxtqqU|zqJFU(PYCLp$8u-t!TII@MU=>VYJp@_ z7gspVzFdSYb_OKXA5G8JTz;O$0{?F4l^uHhAN55}Q8#(Q-;-U`Ub53@cRlPkw)|7-xmU0E7F^+Oe|(9WhiJ~<)54%jGgoHv?4Y1ljQ?3uN$;}JBUghHqybfe#8!<7ddPGDzo^#^yL5gp8ng< z|Ignm-%^_;@SYGqqn5OY4CLP&_X%*lVs%HTnr7x=DMJPD1;yRGAfLmZjIxGzY{j>QVe61-z^Hf~M3YFlAAVL8apXG5Mv{_s6fAu;@> zY!XA$qBVx_KR^He^S)ZVuKx&}_v_;C{?_~Y4bDnLRvMC};SR#1qqX^GQq)~MEI*hb zpDV1HY4M?HVK&+@y3mE1uwVO(u2(Ywp~W40djssX`QPr)JRpqSGZ=4A406RQYxQNlmDOl_OH(m_+{W~`|rMaO3CC( zuKRnJuxvu;`j6>fHnuZ8=dHxhFQ(yjbIUUf}f5b!_lHH zmBS&wcds5B^fa==H%29NU+lYl*Zr5ul$7z4W8Qj*?C?tf3i^{3OGC2S88aDh3 zpf9yGN)sc=U)O}r)6K_c42d}O55(2JIwY7T#E0DE0d5>SXZ(U_0q=H)V;xtzzJKS^ zZmAD>`+9WYo{G+;C;ug&FF_hl!RZUlt<@KRYdC_c|KzVJw4U+)`J3X`bcs??9#bFZ zGKv0vrwKKMU`iMXt6Z?32TZ-GND0MqC68+Uq-zzX2Iyo zHdXSi{GC6ty>8fhs3kx=&JTXXL3 zV2}6UfAAhKkLGZV;b1M+4ZrKU?s#%{=0|mpev`~ZPDTyg@q&I2$J8+i4=euuoDOPK zz?{h(sy>6skFU@S8)=7kS*p%|{F45^Js@X?-Zo&qxgENEGr1TkWzR`aAg{vjh?+8v zACXpQFsQsVy+lk$m-l`OdCcP5jT?CoU8hUjqEuLKCf!$$=Sw0l`YyA2lrH;UQ+51z zuY}X#qct$!+zTDP8PX4!{$SywpUqwG8c^G&-XbRTho5)q71Pfr;_nGh?saW;QT3JS zv&jUB%?{YVXmYn#b=FT8iCHF9xYWLg)Uyz_^@8t}M2gq|%Vl{Vd@kFjhIf`n^QM%t zzm@g7C;|HFlZZgxaMdkgl=(S}-Y(X%Vadbz;sOjbm#ijhq<^&jpxC(oM@XisC=L-!t`} z6AsBLIr|C}u<_jV*_Z1Mn2)eL5SX~40&7_W- z-F(vc{jRFMgQX4`4%{X_FXPo}nWZmtN3lV=CVQv(H>L~u z-A6drZclv)rN#vQyOZ-(ka7!%uF0cJh7R8hdA}J^b%EBBW-Ax-g}qfnu@aV3x~;)V zk7_Uc3pfE)kk86^GdaNMj)@^t?Ad5S^Q9n!F~~i|6UIu8JKKTg0^wRL7knpYFCq|# zZzhdZBXyeMGy1~$ou2gBSc_&Ezm_;%RDDAk2cm1X z<|Uw`9{}MCwtCn^ijfZbV@XyAq7_l6?-4}Ge$#&i<&;#Z2tj=D)p^EF>Sisb(N;i> zawWjl&DRVcL@qww+~~fCo&EgP8JI7#8A>R?_>vA?e5rH}n-^1xJf3@uuq#)7ZJ&ER zll{wM^+VrliaQ$+UxY_>E94H~9oes}b;rM>TA*%*qTdm)G+X9#l#8f^x}!n((u?*3 z7+;hhJYSMz`oaeNduVo8ba``$=&^qAtMW}};#{%_Z%JZ;_|m4oM6znCR2Ct!n_~WJ zo5zF%6Gt%|fv{<9wA7))!axqp7dvTJT3~!hfDXPu_M7Xb29Yz$_`@uBX4_mAC^I8r zTv;BG8|Ytt?p{j;@kNk4HOQBptC&+)tEym4q;6!sQ=8-PFhJ+u78ThqQ9m*WUmE^y z0^tir)wy7FbMEF6ff4K(2oSZ*u}U4qjAG%qOs9{_`Er9)*ylZV7<; z0XAG-=&#wn5xJYt%WIF?bo6fV4Okf{cf%ag!^OvJjFw9McV9L^=}!W%egG|W^#h(< zDZE_%Hv5QopB2||VD7IK`ldHpYBTCfA8xjIkiKn?tY4LB%xL(V7(>bH&V}66HlFX! zE44N8uANMr)$Ffj@xB5ZIXdUcdYO&r30r4+M^9T<%mxat}!(BW+EPo;2 z^EjE7{7yWF?YUk#Cr>6KW%qB!<~9&tukTv%2PDzEXV`4fBAbH$LhA0j$!8#Sw>6IjUA`Gh zqrdG}pd_rID545x$v<97r9G4K$AgVm-=w4oK>8LX#*wFt-e5Mp5<&bkdWUT{Iiqtg z_9qtg@{E%%5AB5zh;QDId;-ij7ebeBPFt+JK#Bgo-F6rUHxm*3!X_8rJM(bO&x;k_ zAQJb|{Utms>y=z>F>ccA@q+kf&vkrYzB%~8`euXw z<|66O+tt3FId%l}^aYDrqszbb%~=y?dqQe%64j?k3(_@i{IOhj1h=9;oOyq581ubA ze%e!-xxbO(0asZbl$P1d2gzmYY=?m88ZE9@6Vwh9F=4N%F0_7dJ*U9^TnD?x=LK)n zd(n$?0r7d@Pd)JG|2RjtajD#?Uy`Tyc3x+5Wd!c$vljbRNG|@rHb>{hcvc~GiL>?B5!w(Q65@d1QIkmMEay*Xc#s z`^NZ)up!pr1CPawJzhhfz~4(J%htD!;r7_RTTP3n9RKGb42QW;J7(EYkoy zWly4~Dlf)G9g&Nlm*6prZdRTW+1|fr>gt#I#3I~<0W-q#Vy~vF`pgyXTscKM>6JQe zRC>m#iU35{RD>M^(KQtCGSK0hA#-%3({a|qtIkYu({?w^E><=|EFU+v*Q@_nLi)|Mj@dwbv!DyygY7p% z=IChN$NKd7X3U(=a0DBR9I{~Aa!`G25siKG(2E-~4w19*C|vplo}vb99ipqodMF`^@$oclJ6x@&ompiCtM$ z0bcKVjDn(B-!iD~T#Tmcv2UzQY0+Q3OR2JF-V-@%t6&{?A+UzHv^9~yo05bH78=!8BbHsZs`*MbS@Xk%IIwu2y1P0%mEhsX|D1+XFG>IL zL>pOi$Y&@SVa_k)SPe^#G`tgBcasGR9>D6Z1U*pP*UXmUP=V!Fv*Hc*;tpuBdf^EL7eV!+TyyD%A?lBK_by9Mjm%?-NF5I z`3|J+R9%puBPWFDX&crcsvouiTr(o(p|njhDqPt{c9DZ^xb8)-vO(WR#9Ph=%X1wD z@exydTw6D0wr$x8>fxNNlaoZgaxP4c1_H32c3t*N2d1a9AFQ5+%-1YS#)ah(tSX~~ zSx|*suCp$T-DsL2-5?e2OGG(<^z;`6T?IO!Tss_--IY>5pI2;7dqmf~G!)I36BN8r zqQCONdYW?AVhxC%CXKax@IDT88op4vC_9N6Ng4Lh@>I>mjIJ!(0XlVas~HRxsO}gt zc~bQKBI!h1L()gE?1o!0>ok3vhBs%JCd749L(&c)b=UF~8CczAKKQ!3Lk>qdt0Ur2 zUVKJ*Xl=vV_O(uZyR5%+sC{(X98`BIbDOW`yKTuh4a8&BO#abE>|Z zQo?j@K?6gxWxuyOED*GwuU06IzDhPo0QE&^Ulg0$r#m0;>b^v`Bv!F_Bhp`tp*ax&#iS6^hk_^Jw@I{HSkKHI6r*j=tu%x|aez0S+F^C3GE zP>z&Mv>|t5_($jmk51375ns|oZJdYpt6mCwi8A{D$s!?S$h)ScL31>F7zb;YrnoepBQ zxOOu=p!e)|PkCH!!TEt6UVJ)`{J;TTd)X+r zl|*})-}gmCqwQXzNfR^Me56%r^FH{u9;)5axg^bYMe*-emcZ^B7BWv*NkIan?zZMB zpsO#U@H1h3<7(&yccw%1<*!ncj-qG6DDE3J#6+tso67uyBr6RUzW>za7U7puzDvWPEI1pY_g7WDks$30*R6BBc z@do$69HRG_U)2VGyHoU+4w7Y<@1@RiL-Oeym!H7$X?f_%r+uDEidVFBvT)nY*YMRM z!!pLuhAp4NF{2pCpw@%<^7t#y6J=@Z?~k56=K0a=cz}qENcKwWy$!$m6RZogp_*MV zU(}IVUjX3?1w0#c<C0_Mwf@WCz+z8p}FJ=mNOl240%k;=2AntU-i|I5ehIcK%o))3hbYJ*V97}KNs zzQ{W%%K74fIlbd>RZ(`+V-Ph|qEYiR1Qdw}U09O%+D z9cMZI#RT<7St=Uv>hZ)cbC;c~-wV9oxXA0Q z%3(ac-^UD(gwwf7oJ_(fy`Zyh8lwNK&Fkp(52d$8@&d1PF_pXstZP)YC-Z>m8d~Vk zHIP1(e@+0QM~K5KN0gqBu_-bwHv|ulB9!{#iN_WTmmt18aqCygZT)r?*&ouJq7sb1 z`^rXjPU7&db=vXcun#JFsQ&-m5qT8MiUfo&sxJP}(T9TMvT=@h|MH$iFw45<#FGw6 z=CPPI>$)Mxwx;ytDKUU@S!IMn$*9BMjG{%tExUu5hEK!Ckm~u{3lJ4}l|4}gZG!3k zyCdR2i_;7&m;C` z7(d9wt1u&|Lhyv~pVGR~?1^<~4`g;D9)UKRsEy?!N-{$b>G6zEc!!tH+slRWg=-IYMB-)^^Lh1_R! z_VlUOc z{=1*ALH>${Co`BH#Cq4Mq%Z$eL{iKorciH?zPQggtlR5#<2f(*oTJ&)dM0{973)wOFam3@9&88^)>zT<4CjB?Q>R~6v6AV zbUNm)G8cMeU*+nTVpO0=2j&ZElO3Sq-6o=g&Fi)#ZGdZfb#i}(^3!S2t71M!)TQSaW3pM}ogn|BE%|m;tkPRb(;DrU zGv`yKXTCa77*Vmps3S$W;@im=R@ndDsnV%`e+0z8IKYcP_`V-8jmqy`{3k5A=?LSj z--3h!xBNwVoXs>(_N&>oL3LMJz2-4wpgUlX;XtAC!(UE3ZJh*PF+JRuie*ncuBG8E zxb6zisR)78-PXJtbmS_~CzdOr8Km)+CBXiYD^boi+}OG*v){^8sI1f*-ie~)nR zeChI0`|yreULpwhMMiMRM7@!;wzRhmvDus26o{@_b_fTiYnC3ou9;S6<3y2jV9;!@YqBcp3A*#ECJ{^*_e8O<-`Ires&s5v;0u0dZj zphe!(&@C_2D&h0hx6eu}cIoo9%y=gD=L!xhpi302Yh2j*S%B%9KhULXC_~K{1DQzG zvW5Tc(;6nfRclg=Q;t9w=i?hy$iMf^>y(^SADV++ykEj;|8e=J!D47&nZU~p({>_~ zr91T4L)SojGvCw!>9GvP6P{$RAp1;ICXdWbZZkQo#uM|iA}^;rd2a%qh$rece zg)|oP!OzP^RyFA)1(DZZ(+A7t1V8$jdJ^!e*WfBH@p(d}IEXLUG;ih&eTHN6w{y)L zQyG>R|4K4RIl!Zj#H_xgezrvc1LljZ>YV4(b z?}AOgdqZU0?FT19OwT3re=ryKcd87Yhoji?3?vxOe%r+W(s(2=;-@F=1O+)502gak#%8?tl<2@B^j}wnVqDt{ywJL9t*ke z`^_2G3SXe#z4;i~<3U4@5oRJIiv}}t7&Cg?zOlpqb3Fv?Ul9HMuz~db1YMjS>|6?-dV^0W$CiB(vEIHu{$=zQ(Ofe}Rh{BLTx`uq;VY~nRSx1^qyGW~s+X`R( zqo+}R=<7KPrrAw`a6Q@(U1J|u14P#xQ0_ih-wdg{w@gKcI^FT|Fw|UJsCq)!>CJ0C zdN+t)GNskM|AOl7_WXK5R6khgWb$(v?jEc<)8~Qxg+JBP^U9}^vci-XvEaIcm7}=@ zR(Dztw(cN$`oo~;m00Aj05#XAq|YY*__Ge7rTC=;b{5@a>D=cvqjaSmuRVS=?*9np zhgFex59#X=@Y|G{f5kCqFf_tMA_=a$`I+oEAa$2VxepzB8ZxJieVpNQEA?F>xo+`9 zX(0#evIW!OmMc^_si$OJ9i*p~YJV;(n&BKZ-1@Dj)uL?RA}M%T!V;p!!vqizwOVI( zfb}#jW>_Z>J#8N=0A2G;aVnu(?{xNR2phEeaciH~#8KD>R~8Ejvr2|1-On=xI6GLl zX{#rQJRVma4AL$uTTEo)=f9GDa`-)&y2{aq39M^kS}>mh(>0dRrE7>x!dRP`6>>YL zRS_D)^pD<<9!0jea(EM(bMi)kbPba~1L0(R^p{TFl0b`{SvUk2HS?jslJ?ygWeNVS za@!NIuF;#X-Up^@P@qHCK>E$cPYhUiHHN;C$@E%u6ISnW4-QAZkVFv_G5BqL|DJF* zQ#5s4(5)ev%6X1>+BohU?TgK&_$l_UMI3arVeK$AV7_4HA}j*oi>iw*bmg-9g9(1u z>bIEmH3slHUqW#N(x|_k!2cRp3qeQ|2Knas?tXGThMfRD!fCGn(N6(kN95zNn(_I# z;o&{*UiG4*rL8o$9@U z`}@xDyw1L|^968kjjtIPv1Znia+Ek-Poy?@mreElVdm&ZL40$eH9HXB>|50Z9l0!| z-@J)REPE;Cw_F4GB52-em&b#}2Wh5^s1`)%S*|(S)at&EIr_V_Q z)JL1VFxZ(@M#rAsH4=k!*{Z@c79hE7(!BD6?KeZ-drUT8Gg~4{x{dR4@r$N88OgF? z*hKXZi6+Khe{tV$rm+`~+O5l&%U|hIGp9sAn;RrodmFj7-_K6o%S@W;unXpk?t7P2 zV0=M@F1~#0OwjL=6(nshEvrn!^Xv`_DSX!199P@nVtLb$ctsH!s}+e0R?Py77RM&U(xl2w&dLHvr>H zKXmW~lFPE@puD-og?(XZj?CmaEdR$6^NnlrcU%qU?RM+?ccopv_bj7l>vv={z~I67 zB_%NBoi`bV1y8!Wa2t83#q#`u@a1=T6cD~(RJA_XT(gVHXFrqk4xfNp6)y%QehkpNE~v>yP?m+KJB+u zgXFUPd4@pzOS7JxH;;;1we8Os6`B4z$s5n-D7YU#Xs(IbsZ8;Df_{&Hcn13V|9#Gb zKW8ETeA%b+%7w>d68ZjJMY@{4kGQOOM=h=MjXXTcwhMI50os^8kiV~F7#j>n)%GKc zBf02IuUu%T!p&RxJCzXvA465g{#tz0L58#wd^M8F|Nr$MU-$q0T#B@xg4EsWzq}th zUgKwLl{!-yzFwX-A6^R{V0((=8SDxcg8sc&RZKk3H)#iu7BH({4O0H0^N0vdlUA2K1&>aP94)*a*>YmXXhN4UVx81@8xqlHbT`C159zUpvT4PpnU zo+P0E@6*;f@~H>R`mVR9+dr6}G%u!5!O^*^KDA@V-rSyhsVE7qyVzs02q1Mgpyv%; zzPX@K$4lrFq1|7NvlFQmloewt;xB0uqM4)%%TBW+qd}ff3 z3+QO%8F#z)&8<-ZKXHv1$eXh}YABe-*##U?&~Ee!%EJu@e>&<(d>8}k8hAwp8z8<} z&_(LO=dwbLavCkytEcIy(q!ioNgPO%d>MH1@WlbMTFb1U-@{uk<NKD|tx$IOfQy8#Z_6E9g*{JuqNA(v)6M3F9S!7-@c4lNX z)gd?@=6088qIMuYja6G8zeg|qao~n4a$cc2DUmuP_?}@~lvm4lTw879>;jz2Qso|~ z0Lx`5p(~e_IiArjLaAhvZF(-rta%cbcurDjeT#J)nr(hf2;xhvBbUF(3Ca3|cHH%a zJbE5AigD7J&bvL!SVjElU*hQ65M48+m;#J19?-#;|NCwQ>)%-k_^W=B(O%QkE=dE0 zC+9Se7s*!Qv|gIs-_Mwn#0nYD&?>;zlxrS##(Dbb#PKUf=3TE=mc*sl*LNn-V7_SF zgf0W&iv#?V2Ro+?$!p3m8)lg6yAWYNm08xSqMF=&Z^Rj$)xaK(M~>G3>04G5Y+Y;C zr!Gp$v8<}i6?&cT$~uk%`kI~SD~7dWZ1)u)`vG(z&cN~--3Oc3K$o!@r3U(0{<|SG}9kJ<~g4WtdIrKgHNV*txTFiVYP~Gh} zp;B+0_8%dbZHA7%Gx+_@-F#)}WAt!ghUUvi2K9MqNZrl5wgaiVt$AbUn%{iJ!(|Yj zf6JnYof^paEJB|BnEPK$YRny{X&z(t-P zI9gG0{1RIP(r?yG337?*%-u*O$xUwi&RlVL&Hd1g2{^pi9^M%n(6q zs(m8La5WpA@{KrT4sRjZ=Dl_FG6>37(Al|_G7oH8^W#+ zbnfqGD4Zn@tm_vtKRqW>RCn$k_-*_dK8tb|H&JGK^TZNSPyg|M`)B&fX@K_6DBvGM zhi``Tn}>O+Fiv^U&3DD=0wp?bYKX-o-rj_i{T$!8u(*Hk(Z3+%^6;3MN4aR2F^vH| z;^%c7{`}qU+Iyzzliga6m7U zyP`c+CiW{jtznt-Q1b&NC7&f_!*tWt(3RUNOD){Ur zVfXV)FzK0{8g72iW)<-j#d9m@u;*%CxXHHsw(jOV->rTvA_wM+UV)eg5dT5}e*_(T zf#kAJsB~Nle?{TdOK%ZiUlU`->3QLIBz&rUT~@Pod*HcB+Fwu zS6TZ;G(3+R5%1g9^2`PfgfBua!9e&T=u-FKb6GqN%OT6!;ClN8&6g`JY$hX);lI@u zZ0Ei;hukoO{L52B1O;qXyyv{@r|Yi#dW5K3ZA1(2mm0^mk1MAC&KLCZf$z3dyY>L{ zFX+(aUp~^)KEDiF7L;)M>q#?*b`Vw(D*PMAQzc@2z|jZ97tW-q=Wm@ih!K>};bi;P z#JK+{`)j3C^D~weObvYBxZnlzh2A-&0T^GVpo=e+kNyOk!~PxH6=)|A5OLmH9BwwW zioG55lq-O_@B4WbmfNYdgtus8#o6!2tdyhL?GeDCxs-hK^h_E-Y@$vD^CjLkJPa6L zLZFK;gjUo;^v03?wp>*HIeIFV>Zh0fI7FNIqJ+$@(ICD&)0CAWIYJ`N&)A-(Z9gs8 zPoEkQ$*UuKYHZAAR7qFh4d#mmF@85Nz8pXoU+f0;9fM8=2@B;5m$XKOBVWjL+BaCA z+en!qei#JtMNv+kzSoOobK8`QG0Ia2iDFGuG)*pc;O?_#$)hErNh&a3kYLV2f$^o| z!Sf~AbBqRQ=K1^3+}1*uTA8H~Pf8<10oT2Q))bTbeGIuG^rlBLCvv!*Z~OaB49>n5 zyWniH48C9d=nYHvb1UTz%$M0^nNwhVfq^c*gfqZc^oOZk&Lf%AkZ2T1l%KTwsCVjS zUX$_kScCYo`MsAt^#crPMA9f?xwMedZTi+ShN17RX-16X5+nQs7R0}3$_WDF%L;Vx z1v1Yx!;zBaBHOxyDdDETuPKcs`gk<)B6&kcEuQbK5Qs0!MzqfG&N6CQofp1T;ntDw zr24*x&vFsyy0Oo^|Ka($70j1tdVfIoWt;WlAN)L1C*A={xTP|D@zk2pbhA3b0l`A4 z?eWr>!=G<*m7sYhcFM2l0Y>tj{^K9JsVF8T>1;kEVq9l)2+L|SbKvqu!nXZ)caod) zvaT{tbj=2I=o&~aJK$Al7DmZ{P&647E|8clcmv~qfmh5)7{tCKJ_XV>@7QhndN^)+ z+njFFEyuULv<+h92;OqlU>LpadG-6oQ4FGMicUp<=o(d*PUy&GA$N_t_oH+?^~$_k zVENbma(^+oA5i^Xp5`124MIr1e{W7lzo3Cf2yN)Bsn_Q?;h0FsU8S+}?JX&H6ilOG zT78x#IG1%HaeM|Omu27*hpsv8X(RFh*;u}i3AG6Bw?n^KZUZK1;*KLLSV~Sj?(b)8 zu0M4%Qu!X2@HJ8^>HQz|!nzmlrphNywX3oEGTu{JxP$q^bAkH_7++)`JYVD{?gHu& zxpR8LS*>E^u~Ku86b)(6dwxE{tF|Ks@gLI+@%=X0%d6V~ z;tOfA#ZR@ZAuDC!7=y}AYSu!dpX)vSrirypt<*kEI}kKiLPYas+R9mY;jK;R01D~dhNyE< z@qBZm+yx2M*CpCElfC}`?rB&2^B@2+SCTZ}{NU%A7|#t))45sQzaRZeK`vh5c`}aX znF|}nHG6U<&;qKvL#pfRk@Q;hRHI_e&*OoLWfIMAPnls!#CK^-YhpHhNFa6hGA#~R z-CaG{x`WI!xtkvti##f1eqCQiRUBne)t1(FgybQnGEfx!@xJaXCM8;6#rO!w2tF+= zDhW!lQncUYG4j5@F(BZ2d=y79yxy?b+V+n4M_F)Ujqqpo**j%4>e5aYboI z+RSKOtM?_6@aLkX(~3C4Pad}6zuP|SR}t_B=QY&Hzf*zaHKehhAM8Ck$ehDrj;o?! z6HN38_1o@PZTS{X{v>*Q!!C8ss)RFSke>E5x(cgjBw89KHU2{1kvwgq<)Iy#aoOZ% zp)g44i8sjy*3(Wn9>GBLv=*1ZgPn7L)Sa?Kc?y~2wc@|?QSpz|W^uW#;W5EUe!5WZzb0S1u%4Tgi%c*$r;_1bB9OtE(U<>j`By5Jg~YOf3S52 z@p0FWDr{l&vICwwF|3C-+AE;b7*j?HEzrtkd*>s9>W*=S;kaPnDxG-5e7GOwJ#Of9 z&v~{2zPS3UkFo~&<8<7R`5KyrS3v5Hfr}41d>rJS11`4L_nXn2p{@7NSKL2Uei6(y z%XF$3tkqG#Re25aal(WIs_4$txgUsqMEahSCx+Ap$pk%r^rbGmWCZ4NiBKDo6JGZ| z2IAv<5&WRm!~z2F$|y)2~VPJohznb;A2}_U&Xry2b!MO(RI)^FlXT zaqLu6zRg&gy$(!%_$j@M7CK!wviE1OuBmk$Vg#aVlID9Jte%F_ptWHF(>LnG68N49rVmtkg! zgUt=r)6WLJY5>vG6!5>GBPWE+*DPmYu;c$Jf@PKDF|)g{@TDSC34CLKBy*z)yK(}` z3B~0^bpK$uBU;tHdoJu^?;zawrTx(4Fg!^;iDFmm=F|%^Uo*!#2_z>Jbg6pq{ZEc; zj(7ti^j3itf9Ab8IC6W9_G8ucVq;j$>| zb%)&v2I-pM;Nj~}yDPsdT)JAiJ}K7iM%x}QGisJAQGL_SLgw((hv?}67=ZoH3+U>9 zt`cW6O}f)n6r(%!wab4c9RQi$z`tR3kC2U7ggT?8IKRfLzt(qaAeEaWD zOy@xj4y1^!54^c_Q&jdiu%p;LkC}roSv;(L6URj zt;(xXOanF4*8!m4YxK-zE<0sJwTTP$-(F&HZ4Fdl(}l*P!Oj)_GCj>?ZXJ<5Z&2M;N?^!(m6o@LeeiGg9CQc^<}0%zVA59U-%K#@dNVBD z0I9pg`57Q}XCHeDUAo3h6*lwq^H8kA$z!#a<+@FzkIQi1wO=~O3$vf!>k>ZfCNxE! z;Q|Voy_E943w+x_aRpd}80_`&Ev4yJ0V!gzt_e-ElL4k{Y9G9=X+-^TKKhY!cY&>P zKXB)hsOqRvmhWtD*{>POR6{;mxJyM0U@9H+g*t z>2ye5<4K$eOxFZMm#*>4TP=Nu8P)=?)?c&B7;zIstS2XNrBhfTagnbG(lsxqPnkde zO!N?p#hupJXB>T!u!J+buJP^GB+`;0qdNx?tZS-tQX7Hkni%NPHIKIQ%H<1dYBQ+T z9}j&WR zD}unfW`28i4w$Z?g)Uu_aK2ac#l`HHVBea5cdBP_k?$PNf3tNY`nsw9e(!O_ut~8w zohkd7>iOUc4n9#y+B(?sd|4WcFtm;r)L-Z0A$<8Ez6*peq_I~IR@XrK0rET~3TGl= z$$v3;zEj7D)~*lr{o;UYkiAZaIlK3}VtmgJ&dm%|y}J#W%1B-|?NM{VRfp4kx;#64 zN;TkUx&+oWb|$gnKy(cQ*CXia2XwkKew;B}3VdJS9rr0}ZaWfFtG_I2S<~s#w50&V z7b7fT7*A)3pWDLP9$XQL^`;-Ze&3p8RO!E#Z5VG^Sc`=4C7c)!7+?IMi!TN{5%eS} zZ976{-_>#KuXMJ35iAi@6B={(`%~`w0VpS(qYhLUvK(S^d{bmLb&RDaUSv5Pu{$ti zPu`EFu@Hm#QW4M24U8`-(7_kT`!wO`zGAx1Z(?!COVu84tzgpk{Ape`N7=0F6w@IC z@#Qp#U+61A&@&@kTK-=~4h z4U_pjUO+#!{i5dGvN&m`Ih5%4M~>_HD&A|)8Pjpl??txqj=+nL4nLZVY-=d(x%9s^ zkw)IhSmO&P|lJ7{Ulh<(G9LJ$M^k4PRBLp~V#5W+mB8Pcr zoapnowprj-GrrQ0q<*uv=ogqTLjBok!1!VTU3}p?i2thmm^VxthF{p^8L!LTc?U~Y zlu}k)f^4G#h%ZkYOUAziy33Z3x$)qC7nQwp`si8A4T ze0N8=Y^m>u?sLzV-*HlO!8(RYC+d_N_)lMwYiH^5?NT*fGTFW#@7ov?ng;WQ=eMCV zFupiJ2VWrj0bji>noG-RhJCB0#tN_gqG=Q8zs9Ub31!vxBXk4tC1EzSxH}jlEZ9`Q z52kB~=|-kB&81tJH+iaa#xAY{O9jlA+DCYzK=_h0pYh=5napoDrHM}%N=w%yoT}?> z(@@+g7mS>0(%bcV9V0>g_jblb)qnn&7hbDAcggc32TkhVzWETQ3wg(Oa$@-&UpIi~ znKT09g@Mg8%|pjL6U4u$DvYkaIvF#oPrat=@-KN*m@;qtF3?W}Gm>3m0OVhg376c= zl~P6BzT!8@43%ISor(qiSQojtcUlO6qzqAVrwlxdBY~@4# zR90)=cWpDIpl>Pe*=4+;!D*k})R} z+vb{r`BI~=E&+ruTl2Zl1CLGXQV#pQq zmdM|=w%9z^n~c3Wvz{LROEW}eAZpH&1^E{zxk{CW0BeJm*K~o8FTJGUOZ8dYO|vJT zqEaOydP!~0Li~%jHZu_aVjr6Z9sPaCdq6bv+pwg`kCu^%DRKJL$2q^!Khh^hYjV>5eGwCMY~z5K_g`QePK z;FS^CR;1v(w$C6P$<8WL)yTQFvZt%LuVVXBeabD=MAipaiN`0OkzFds;SX3xA_N5k zylZ?3T{=?ka^Hchf=xUqNs`t!(|+@j%4<$-)vsjgUzzCGK|0cBo|LqUx6t%`bHs>8 zHA84?dm&qKvcba6It5h^!G@Fz%YS!`ZN-rUfayp_=+Kdn_vU)=N5vC$YBtzV48qM~ zk7>)OYw`v)P>aiI7{0lnEz5!VOa%`M82nH-Ds8?JYB$sq)`l$-nJnKX z=^Dc&$^W~Xuloe+6c}HMpo1@vy(`S03u`2F8^(x7@vrSCLRIHNK}XvI${1RLA)zq|QJ#~%Q_(=O<8{owbm z)>KzJvn}2z2mg9v9^>=p@a|0g3uTb)jKNG#`+a}k6yCUfuY22Xg?eAM-DQuF9QJ3- z{EbGch_3rqc7BD!gz$fNjpyW}c!BvBY3TAVp|Hjyx*x-pR2qvaPd9_ObF#qxn zy8MfhbxKNl`JP*olrtg>kV?Xw*YPrxDl+T$@6CA)pi<4hz2MVOG(zQ`W=fFu z5@xo{z^_`7eKwrjECE*z=1XjC!9Fm)j6w%rAorOL(6fJNBh#M>OUPf@;7cVbfX=`-XU>v9eQ4Q31F5Ge0G!SI6;R zo9S8zxaVKxI9qac(SrE0^oEs}iQ-%)ubY^ySPfU4&;DR)(dAB>n8TWL-*LXM3(S|e zm+Yp%_%aV2e1YU+vRb$wT^SU2*gIh@1)Du*bvGQ?-4rZ~>cyR}B0zkRT%m>`{^;8m zLQm+cNv~tHa=A=0IhN!HhX{+1gc{B20_KY;?OQD%e4&8vh7SJ%>9fMbEV89bS{6!b zt2umLFi-gU0wrN3O*EcPfvW5Ny=+$$`p^eYuA>Ym=cf$`^qW*Q0|gEU&YL#d@He}=k#p@H%-aSa%H z^tQJrZkJtff*tY9Vp@oH^yd*LJUc^;-|4C}Sit$1lc6g%uzbuPy7IA~g-;MI73B6# zX@n0xRk|&IwzT2nttYLX5123y0p(-$*wr2{)QB|SXrghh7jZq|HWtyfjeEvf=lWMz zo@E*?0GyBU#MOQWmXAGpu=yC|ZvI>?^#Yp`MtiI%0^6&PIx2#x9egJizIt1xl4z`eGdW^v={;0;ui+e3~Lt++q*4AD`F!3Bash%;k>H zo!BSkk5H8)b_}2ig4CTC?i8@PgMF}d2kHCyfAZ^oSzUtoLt5_2GL%$(pRFNlYmEeD zSYI#}4pes)?tD|&9_+7+$L@M&4N6jF{hWOI2Sd31d^u*lzxLdGgy@=PVPzn7H=wry z9l8eMn>CYkin(2I3cg-F>S!ut!?+W>)o5)he?DPB*V5l+!k5^c))m<0pYHTqH=Xh3w01ANni_05pE#qELVwjl+Fa)x6~yk}!u z*>=&WD7BR#g*W-H{=dCm1=se!3v0QExsvDUuRk7H`Yk+1uD7O*@Lh5p`TPQT`vUBn zYZ%Nbf%xX6`9BYSZn3y|UdT!HpAJ?^c|o_u@Gqs>+T$|#R+a&54zYGn-K}5J^e}EC zslogu530)4t%+?a6sOAS{EUT`?8BbDM}`8fI~{-bUqI@P0=@-0^faU&VBP6uod83D z;fB~>^JwbO?vdQBB4+C?!&7%5)e(@MR)LZBp!a8lO|F>zLs^OkXUT6BLC2;X6iT`# z&uQv&WB}IFh_@|zK=ibr%khKl2SE1yV(2& z_k+~ko#Kb)zqnth3*%L>t`dZGt*Saqv9U)mySRS)^@8dSme-l32c0a`@b$`QX5wsX z9lhYqZrT^p-$Yu2eCQh;PT;yjC{>FCR(FRFw(cN%K!hSZ?n}aB;h+AYM%7xouAILb92jI69*qmCJ6xt1pQdJs5qU-CB*h}%1_t+B%+5ne*-}}t*k2Zn`K92xoBuVt z2BhxtDD|N$C%n8(t)6pX>FgbedVJA&Ize;EX#o9gdd4a5*C&2Xd4s_{hDre!|>zyc`qp5GJXv8XFd5xKI`M*7uHP2Ak@81b=t1uz@hdn_% zmwc)hD)O@x6@lY`7WB5m#ov;|!9Q}fED&9@$=?V>*Eql@JXk#qnbYpG$e@^$|7dVG zJtpWt@~VUV+mt?=@SN~`3&F4%s1L;ft2C)@L!MX1yJJU|b;l?BL{%=q&yWMn;yuE) z`acg2u%51#MZ5u`r?=+&psNq{=D?4^J+ZNA(oOet1Z+kCk4M-pA-Do{9zBoY#Um*AYN|gdT zr(M_L{1_8C65AFz_s6b8Vs@mNEI4=Q@9+P4$bCX+rn^p%+i~$COzLsx31SFA3rTzD zi@Ml(6rqAy4CV`{6+HxmFQl>c4}Q;7vFa*va%N>!ws)(+f#jz1XFU>Ma+=!O=2BW5 zNFO4iX4W>xz4;aScb?`AKh1YV*BAW06S0+}`qfq+_#PR*-T>#*E`i9p!18GY=*Xub z_hm&tAB^Ffz2)Jcz8Kj3u$51mBP~a=N+$nvq@A)5loRrv#_l{?E*Td|+0Wkl=!uo7 z8e~;p)KPrMpfk_l)bgnioD*`EO(6rx34N=$p=%GQY@D!@ZqYUDaKyT&8f8BE&*<2u z@~5trJcS-_FA!hQwL_^VGT>g?dd82E1nxSr=k(K?RdT*z)LtwrXSdiAf#fxm?uNkl zG724hf%xX*;)R(aO}UVM71dAJb+h%ayUt6n}P z)Kj7Ts3m?=OR}F`YyK4JfE}NlKI;X6I1|hzr2pB3K?Z~`T3m7u{=O`nIecy5P;!Ps z!~Wn!59@ZAW-s;*+Ouqmy@FIrkgoZB>xqazsD|fp#x`}tQiAg?#4Q`aP1BJubz#d% zm1M6Q;+vg_wt?sx``A|K^35m-hSNW*jG~d~o(E1*WH7%GFj@*vuFrKd4&)FA`DV$? zlJ$(n2!TB2cE5M0gV@u#CKmqO-igm~0|@ns1*_yW1} z`ByMkeKF?+Eo^15vw+-kmW;=W@?=?aRfEE?uPBHwc;_)8Dt;})%O|;2wxbu%%H=+K;|5DwL;5a9yz_2+daU%lL=))Bej$! z#_f??+(0VM0r{7k{Q=norjMlhQ;i$f@f_)e=}p5g-u`(bqayR}QURtd6*A{QKo70$c@?80%KWnfDllK-6YDc#-OC7nulgLHSN(%o2yG)Q+z zcL+!$AYIZ(h|;`^%eAg|-2Z!A&v=e8p6dha(?0MVp02eskK?y(bIyJ4f;(v>k$$;p zBdVAwRYEd1B9lbDiUJt=Am2Pwu<>96F(8rhkW-{MofjS9^>b}QadW*a1VsjOF$R1r zux~CwIxqs}o4-ScZ-&(UP9Lr8Uks{Cpzr5>31uT}z##fP(lC%)69#N|+%-O{XC2wfJ+Cu8hooNz4d~-UPKXl|}A@eihc}cRr zb9C_h&Kb}*5oq^Ullqa8t{BY+UxO)pcc1Cj@itLh|Ic2cwn2uk`V$wNiS{&t4Fsd2 zb*MQKB}J>7VE;k}N1g=Czhpy~f8no~9A_6bmDYcvb&yH0QDm}1rK7G*;&M@8@!;2ydXvzdR>W3HBYOGH7#PQ?q#_RK}f{i9h?j4>ioo zetZe!U(^L!C{BFyV?7(~=~%C6muE6(!&X8|k0<{G`_Aat)Ad9Ai{i6kVE$zQy8KJV z(D3RlW~v~aePsJ5CY)5}TzNLTO;U8s+Q}svkbmJ?sV%7wvFvbf)mllT*51r67>wnP z+B-krejf^1>mrO5{;jCVGeNDGAPUlv@G5zy4@RS$u+GkY4JSEH zk8&@ID!^zboa1b*20nA=bOr0kf@oD9U^;RUx^yIg5@*Z{)p)a3a+E)Ai&jc*qlp7V z5}4bmKe0pcK{|3(Y;CpoBbG7Go=8v*eNJYpg^}xKh`OcfxZ{8xR*WD!SVvB!j)Vf! zk?zo?BlnSeG?G?iy0YhOcbJw_b)|mon;abtY0XP~YLW%%NEfE2E|_0Pb1o=KA`Ggo zob+uvFamJMC$M&}*OMykdHTRQlHsvV5HKBC1RXjOat=st1zw{gQ7r9AhqV-2$Eien z|5lZ@{S7x=yJtBcNJn1AFv<@imv8hvW3@{>uAH0?;RvqySce`vY7b32sRT$y{S0;JDAln1xi^z?+05HBx@kivQ>_!>N=25%3AgK18 zO6*aX!pY3#%UrtAJb{bLlD%NJe1y3l>Iu>dcYQ;5eH(Wx<$tUcp2g?M>}9mH*&3k# z{$+~Cw6?P1NC-b7tXisDpZ0O)=sUCo$lu5AK8h&3-i_<2+ELSqx>Wnf`#5mrp(~qi zfxg~To~q3mkG0y~nOl3z|Np-S`FH>C6=o7X4$?nn@NufOJrIEjcgwE22q``7^O7Os z;B@>$4Gg_i1<>bWO|#GKwVJSS|bXb3#Vp#2~b z7+)xp)&HE-;8M4spVQ#!jWok;vpFFd{K!<^r=^is$~e_rrZGj;ihJj>@PFDxi zQaJLg%lMW0AB()?a3a$vb?Pt6E<>cGpwG2Q%OCjK?80q|+9HEZYi6c-BG)i&?0s;m zGTG~Lc4g;@XaDBpOy2IS1L4aqS(FlDTmiYj$A@knFq%W3;da<*5L6dhz|V^%s6u zhrFAxE6y9`u})W~RQ{G{s4&jBuS*O&tOW1QH^Y*hlnq+9iHNA;@!I}|y-AkRNBFTv zFHbttgk7%DnvL%MZ$1+Y`a?5d{)Me}Y8-c>?NbT87u=Pg8z41Tn z-+%wPhwif`Nd1Lq;+ZlcwXt5q6yuLB&{*CCI*%U#8WG7lO^a-I=bKr}OgldvTDh6M z4r08yG}Fvf7{fT|l; zBYneeg(p2YT5g;*2E+VZzWR|Hd!8AHHGkS!IIa5=l{J+o&v%b)RIyDh`O}3 zc^qB1Gd@!^(YSh<0zpesy}l+mo8f%O_Xe*B;Pvjk_h0dl47wklEaEe~^xgBFu8t8= z@Vze+MI7Ne;_C-`)1_AKt>3wj{YO}JDpB|-!8`fJ*0W__Z72F^blwA_xk$OVh1aJl z1cqOgO%f^Ev?k&H{eJ#C-XMSf|5z>k8Z#j0OmPg(^bRGK#d7kTy!vIRJYQ!nXToCa z_aADIJ|P2ruDZ`p4kagd$Amr13rJ-{1LiucqP2N{e(zUtPtW96RQ;szZ~k8tZdW@H zUDKrrN`Tz4ia$Y=C;D+SKG|~t)Rme%CzP;xN zdt6I#J;#ktpdnP<&^Z=%T?P98tRbzIx-PRUFgg!^FLM-^bW}l~r%^@?h;Q04V_cqK zk|6yz|4&QfJui^@3n_vvT^WeW`yf8UgE-9XD6cw@BlDBYl|*yl6z;bU!=I+s=s@inUcw2O!h3ky7CV$gBxUD{@3{O$L*b1j?AP4{_04! zn*2qL$54Lw8=L8mvV|Xe`ap5~#Bj#^-P)f{tF~9{0?FXuWL<} zh{1e;bD?bl#+PT%!52vX*rvDOtevJ8TL+Sk-He{s&3Ouag1@LvP7xeG2_5KpAfIYB zrS6!?#&BUK9>LwmFEGY``0j|?6aUs@My~*MR35^Y#|NT7_@c_5KTKgW5*Zv#eyY08 zZbphZQ~|fl?T(<^e%g+43gIev)da71y86H36QmDSrYU?>nC_l$t_ac9zjDo5pVKwp zPphvjeudwAS#P}rc`i?$m)y|yEXq>~*Hced4JZ0FY5jE+kv?vwf@RJ?3#S&oXzhBw zJWzi6f4u+x<9z~oAM>qrSV8J99U?9DJQ%1011y1u#`HQD#-|3Y76Gt_=g;zf+}$6$ zTlNx^3G)`levIYugNg-`r9|Brk}0}GgMHYBzKaXA&l z=ME5uC05)lA4v^1XpCj3xh zGP&=s02>{`b@Z7YvM>K@e6dSZsvI>bBnW|nk!z?sTR;#hsTFZ)L^yCneaHm*egCYE zzrFa0vf|=KGB2jGW}z9y^Wvr97Z(HCA9UnXTU4cBzC@JbM*`yuDs=D#Qr~yiBD_HV z&7Fp7ZpJ1_#c(vg$=|$ss`ooL=yogw^gIkjlE*XA)5mDD(G5DH;BgmC?j-GJ@G|c= zxC^Zje9?9V^F^KgaW)XX*dQ3umGL$_Y-(M@Tx^ipp`}67jBAm&6m*2clvfIda233g zfmeus{=a;)-N>s~l?+{E_`i(F`J@a#j>YQ}L!QU~HQ)T?fuBGh{RJo4wFvLkafz8O;A*YZFeRQF2`RzhaQjtC1_< zHX+b#&(gYJcbCT;UkCX?Hzd`8Q{x#}2uJ>dEB_;3_G3d_a4RKm9z{DLHWq7rH!ORm z`oG2($hoWjYhw1!&v7_1aR+m9YHK-6Y1q{(zMX?X>`Zt0?WxwNK+|uRXz+`2;^O;S zu_*Y>@R>PpPbYCwVX_nm9Mn+2e4&utk^#aOq9{FLyD@IvZ)|fkL@x*HRSI}a<@H8B z%dQB}#1*1LzBhQitLxtBz5mfSvo{<{hip-oNT&S4M-Bgi6LUUJ4cY(yHQyX$(NnBT z_;knmxO3X&!OH=Ww?EP`elE4=VSD-rfIhFIDg7S_w%>;2q1caw56mO^Sa{`!dEOeI zhuuVnf5NrZ2kRQEQ}{R_x`vj$6*_z~Brl7fzq!KfNjekDCRDcyqw3%&Gye2t!fSPH zAx;#quFt#aH4)=+RE~=v}@J zxioz@ddg~h)P|U3KjOgCUy&|}7khA_8HpjKk?;#CAJ{iP+$cZ-=9_7u%Qrj!YEq~y zt@kgGz8P#j4}B`GtSqVG&Y@PO$*+It35&+e_s)w#U*&=Dyh3EjXO z^!{vAJjlq4zFcPM5SC8LW#emKh^N`q$KRE-F}@pSl5uDMA`z;HP%t$(B+$* z|MqGLg>?F(Dtn*@-c;0vDo#6+Uh~g-tjw=bf_yWY647hy=aiPUG06CTiG4>hwWzA7 z0}g*6O1zJwO7LIq1pDSl=WKUizWFJ1_-066)~(u~ek320-5c{4=2*lF_nJ^XQJdl> zY%y$l)LIZ|d zF21y~Ro3muTj9@6NoRHR#>;{N=-+WiIYbQ2F0b<2YGcmsw8dh^s*kUU)Rb>AbbgvX9B_( zTJ{>~@Gp?MpH;zW`LJr`@AyrEPX`8+16H1J)Rss5D>#AyIAI|FqF+Ju(juH>$3kD( z5l(fJXCum;fn)0|Uq#aIDuFp4K@#j=*q!RPfcO{GlHq$__tWTdt~dRpt6}Rp^2q7z z`*V@ete6X$;m0G%j{jBn!}Jdpllu8Oj(ehN%HYM3wpgr7VE(~Abu^(%=vovd>?xQp zu_#Rd>wW~##g~m4I-jA{qcX9FC1JmQp(1k&u#)U5l)UhbQ2!kT;)}$`K}k!P2Mtwr z$%dWup$5o5^;9H&#AeD5N9iSqS?NxL`J&y)s0@rR&d|XZ$h^geV>Zs8xhA_t)3exx z6RX9puVa6oyX*E_4^;gg0r7>@NXC-gyj?8Y-)*%P`_lHO&RKC<(e`ULaYxI=Q|F8K zV7?%peO>{=7d{8(+NpNTx$Vj3xID#u9(Q?_!Bg37T89S)alBvXA@xr18UYW`6l)=dbzuL(?Qup9%)hunmw(|@2_9UO)la!>v8GCN_>}X3 zC41WOW^Hlb2sEEP<6_FkMXDb~Ke*^0DtjvbB8yvxtOi42Q7wYrgg z*~Z#Qq$XY1PUN9Z#p^Rct^`ku?vnn4FRedAaO^E@Ep2y4fl$LmUR^s zE&{SfMl>JqVsA`^0Bs24()C>OuN%clD1#nh{KErDkgjptCYyzs|IBbj?5APU$5>U2Ib}KOap3RA zo;St(ZoG9HtZNv8>H>l38dvDjH917ame9ifn_Ud1kto-1?BbY!&(=V0l><=+HHgye#)8 zkE<81hD7;M5`_^+PM>I;SL2g!qF0`d8Sua|yME|;16&c2%2qr0`ng zzg&OMOLq_@_rOZ)$52~;>??J7Eo{fa3gSyS>%uJuKa)?xm8z~$sq|Z$>t{&BPUqr7 zayCosl6y#xz zMcxu6Ky6>#Zs>!1&??9ejbDGxbax zKXsWh@$y@4JWcSRd_*&ENIb9pQSd|)k4yl>7mtzVV{gH*)W+oBB;(%GJ5uHi4n|dH zzc-bfjtahJo?C&Ze~cBy)`P!LRRu)TyCKE z2&Av;Zk-1I{6C%p>cwQjI;j6LUCqfUXE{S{@2iI93Hp0?eiiZm^K(FuekPVw)QDq& z=dhuH#uANx-Azk=4<<=mGxg-2;F~3YKEGqjrKLu)kqdu(eX`DJHG*Jcgsoqqo7CPM z?%8ZHU(p!k9FS$k9*}x~SIH%G^)soV&8_}LN9OOU*n7>OM^&@m5QCwnRz}a?X_R4Jk0+jCyZl`mS zP(plj8zGRPr?e~G0@gLkwuQfe=o%XY{d=3Qft+tH<$d^GBsKbFai(~wd^Lw!bSH~K zU6m8DfyUu$MNr&%3kRWogL_ANPU3uA>Hs@55P$=RkSP5GiX<2pAm4 zuCI*PpjJzsE^Jp->b~PyjO(tz%dZ&itOWeN1CZ##{jFlK#P$m64 z2SkBt?*c^E5Jg$u`#x=JvcQp{w$L=4+r`pC`^=n;v|Bosa4MD65Z}uuARWN+6^#O~ z=H&cyh4gJlN;lu6!lde3<&h*us&7f_TYF-*^#9H`J48*a0qb-42pxS65MATp$BGb& zmAdsfGlqF~NHW1fRCF5kO8Ucil15$*NY}Jw{rdL7ZG1JZo}T_}Az0~6b$m!_nL(O8U`F z;DV7YHq#&djk$p#b!j5-`DWe7MPRx{@80X0nA)sd^+(BvnX)2%o1D9>(~ir2Yw38x zzuxw_8-U`jHQC{|?DD7iYXs^674PDb&yzDE&ryse`w@*_EimV>)`R0NH>K(jNZf5q z6F`@)8MvZYnXJEx2wY}A>^ptSykk6xxx^cI`D$EN2DFc5tm_yhdIOO{`w`H6{1{IjDse}$)1L5SUr`;s(=`sf-#T$ke)v5~3X7*Uw$48OgL+YfP#SotHVPv}2;< z+MD#&PgA?6aZhP};1GTodUuzvu}!fy;U9PrHW9 zN^Q;JBIGW-p;}bv;Y`X=69?s)Im`IG-aoRHB^!S^nN;TJ5#bzpa)~YHfErof;Fw7nt8V8CG&}md)XuCqw1F=%kYp)VZ~9-3<%deY}`U zJ=-CCxopS)#uw>(&lmXaI;@Z!eDSn66%$zh9l{*j8@hWCJ!Y*O!-7s|^XLa;0#$G+@DbS+koDBf#>qyU>-FMWUfeob=B4 z^WI`>t2Z*mKl1M~Z(7P) zL+w_Qg!mT=lTBcJNrn!-K>D-`#~9qKUuE-z=NgbusD~miANuc5b^A2Az7py50`Uc< zae?JSGyM8XwW56|={2r^3n|;>C{B;_805TMr?#FcFki|FwEBSXC1Kk9-sWW?eROWi zb+JORPq*Nuq^lWVMX^C0=Ybc${;WGcM zuy(1JIjYzp_&U;q^Rk-OUbR5-vZM%9(A7s5Wy|5v(UbR`94jId1-9FFuZs6nh^G zjysqFo<$&Wr{LfPUFYg4dJ8MPJn8-p|Be_$)k~6o%=E41io=e0g8nAzPS>>2NyG8i z9T+&g2wv!S_1r>dP!&EQ>KnqId(GgUhvK~g)-{f^-|K+snpNo1H4mL(SR;w#508r< zN$?d69Zpk;kj;j`I*%02uV;dM^8=OUjesxs$WbQWHT=4+L;{!dk&9QC*UV(os#Vrt)0!1 zm6yF*cUymek(k@B$HTy1GLHCydVMiULQiAfzT5TgeDjT4-MqF&lfD8B<$3lhK~L;v zv9btZbA#5G3G$G$`fXNlUN-uSVF_4XR_os9Wy?`?%qNoiR==iRvXx@Jo?}f;|HfkC z+lfBg(tXDlo1IXqCZ0iWL@HIvKYne8q@JOXhD6T|IIB!lnv~r}K0)|`u1N-rFX7O| zmr{CV7ZRtR(z`!4zxJv5=DZN!-7&wRBjm6o9>43;UJi0;D;i7*@|Zgi>OXzSDJj-* z8@Ygm%g}RS`oKcT0Uwf=z3|=##usPk;tS(6mZRn@%wNi&uU<1|;cxiW#5Sn~c5!V) z?d+*Qd=V_$bi~ejL~ZikV!rCdf`X_Qd|Ctfj|@qL)uHros;_onzARvBYyjiSJaq5{ zl9%1iC?9^eeA7(&e)@J+@o>pm=v`mSjC(;t4hyyih%e#1L+fbI{bI3TLmb}e=W_Nj z51iigZS^5WJUv=6UK&{j^M&U0YZ(x}5Jkb>+q^8~zJ_F~%`xiYW4chS9Gd~U&XEM# z_4+w(Mgvj4!6ZSDf2j&j(h0XLHQLOK(W~aT&8i!`tjgVXj38hNPvC{kjmn4g%bHt> z0r4-Y>}U6W?qp00KWB#DKv$E#lUVlDAQi@Lbk3>CnxV(iTWx zdXOpw_N@IRsU;J|(5Pxbs{ayGkaG-~-az8cItm^-bPeP_p!ZtY-icYh^-kTr zVaGd;EqcD04W-YLUmrS6J)#qjH1z&iV|p?&hkK z=dFH#`Aw_C(oOL(zD<&F8rOATekD73%y@Q&9|7+=9MUhFI7kH~FKdIqeQ*0^A@zWa z;Kw|_jRtS(Jj0o;<%gb&BGRt75r{wk^lo_R?jBvmeY~?5d~uWYhhOyaUKMV2U;Xq zCrQocfZ{G?Ee1`|5gsM#iKOK!?PJE+xA2;mfv7!loQ)mQ6kMv&;JDKmMa}~fcccis z&`}S7+;eyky>^b(`<|mMD(0J5ujU|=e9Yb{hU}S-Ox=sS^RHch_?=jM^*QSbmU$U7 zB2TGg{)+D9|Ah%A*)CqqL{{4b=VcKYQ%`{9WgVd_FZyvOGU4wxBv{nsnJ#4Q6+1_QR@!J*Gd^P{i=v~GQ?oy`1+^IOQuCecq z5CEcUXxUGoD=+&(2JK|cy;FYdi=6US8mpoNW~)@HjAMM85TkS;$Tw5D6I}dA-G7yH zgmVUG|Hsm@TqQHH&uH}^xzF*+_nEx2H&;;l7MR-zTWh10NBV!T z|EcVowdYcrT#Qxas;C6w%Y%A4A+x|CyzwViJhW4LFQWz@aGu+v<}B1UlJ!}@zj*=i z&6_?OK=`sT?GIhP8To->i)3Dg%GPkOtcf$@q9G3EY<{HApzMv7@l%j*X5LBMXnwJU zHnI)pgy^O;6Rq+R({RvxTlmOO7)GQjKoF9bJ)9>4=9{;m!#6|nva?b1YtH7=aTb>@ zsR6z(EKQdOK4$-nD9w{vZ z*&ORp9C~%(nSWBuO{Fg8fU}N1sbz{>2PB{0pS7BroUXW8Hz# zCQ^gQfY&2N*-ol1+oD4=HR5UFD3u@`X&r!!UB&`S8$Ly0X48g3i=5U~S`zwYV)(e@ zgKRVCLRqkmRLA5z0H!0+p+iSP<{Dq}_;dV_U^J~qWmF8ILan}LTlRd76*2D_9YA^l z(vc~Vey$owpIgszAi>S;T!e5FPpCWi1dL$>*STZ~ICh zbB#|&gR@*qy|nGAQHaM~{_bq!a|$4wxwhiz7n9!cg`z|QX@~i9%8OWGqkMSQUwvFH zasE*Yut!Xq&$vmfl3BrgnMP-=1ICwq=;Dj-2y>@$bENsBgmU>0D)Br9UnEaWXRfa> z=GS+8L40{J^k(bfyO$mB)9PfrVe!1eEE$c8zT%>W38hnw+>nYdf%!u5+&~H#UpSzP zFQQ&1XVrnvzfLKoKFfMoX7rt<7nxPwrLq4!t%(-I7dE8d)04AHYL_V&Q|P~lv=*7R zWEGmt^qCPxEE|c`P(2{|F-+}4V00BnEhbK5?gWyTreejAzQw8(eqek-fG)nA z!cb4pQfN#eKF3QqzP#oeep%Cz$TSq~)*?4Q2I7ky!$Cs&48TZwWac>e2KM9dam2F4fNd&?I{-=eI&@T&km5qteZCDD(Fy(2p5)@Q>q zkzxFon&c!Pz91}wZ0c0sdY8;&S!r3bXyE3^@_nuxB5Ed3`K0-oRdS5~-+A&W49ztl zeAy*iy|;af5M8r9tnmIs(m&Cb=Xdy-h4F;Oq4+f-VQ1u7)tm6U{Fs&t3favTp4Re> z;i6~_34(In z(^(A|U9SR$2TXE`+-)q7L2nL8P%tpQhD zR%T?S=Ae3Z0{fF35xM^y?_lgQe~J1TNY{9_#WhC2{Or}xI4=IyPTh$ut<$|>>)uS_ z#uZ%sJGiJ&2|ULjiDeRquBp?sy0`sIkb9^MpG>B1=Q%FVXfnF!!Bd}{g@;@y=E=|< z7lyhOf%38x6=$2eytXRY0&|z7LhiZk%5yyO(y=6#(^O`-k_H)1YyZt>;*cIn29lRW zEor>B_fSFN4xSz9QV6 z8%zi>4(Udw#OIWsY6|S8l{gps` z{kWOnNv%_{Dyr>jD=6-~<(jd4c+_u%{^lF?suCZH3n81>9yn>Ueo7334Ku9A2ge=f zlQ1M;ahH2<;||j2AZsm(h`&8AsYLMj@z=6G-?7AznMwV09v;O-;Q?}L&>1(x$KflH0MXM54l>YDZ-(^I9l<_C zp+u)gb6PfXYvKQnqX?TdrG`BCx4>`I3LaE%4t#(+VXB9ya5QoA*HIFUs7YasJZXVD#k7RB3E**wB_IZ?y#%dnGp zJWA?)t5ub%V?ny6U?hx{hWH`5RfFtY;!#tX@;=6I$=+&F~%)NNkRFV_~Vlt`Dzyz8@P_c_(~T$-762}$ro($bRMrC6cNFueuCr) z5nuWO%hw!3N4^GfK47A_z2b_v+b3&1NzHIoP7>|K#1B!s&q6+0B^?$ZzRaw}_J`Dl z_0zkfCAo=uqPevYP_`*sR;XcyVU@H=yIXZ4-;^?5Xo~G3biCwM*)(#!9?ec$2%L zcT54%HOsBcz;w;{z1KAl%l=Z7t}UYK?l8PLt-^>3OmyYsQlfmFd6pu3r)!wYj^8M& zA!!9WT^~tmJ`AReH;frkH(xUk8i=qmw0Q3Vjynp3L@FS0XB|ZkUAl(Kd2KGsKPu%m z7|GTRmfis_>2^F&v~$=>7Eq>QJ!&Rr9aGhJC_#jubWn%neMYhq6JJ5O^}|(x^=NfpT`}V%Hp#5-g8pB z|3jDW@hr`5n~F$66;^B15m--`{rqqVq;EK3I`iJ2OBr7oVfsjq97+&?YP>SihgVlP zIombB8Cek^diWWXCsZ{hV-TV#^a)5Tl6Mt*oS@3}JYA!;87? z@ryOfg>8er*Kp4U{LM?w-Cyhka+!kjgbxeL<$&c0>!B-82yZj5ALcU67d`O4|4Lai zjNE_S=apH5nFr;@6bwjDN2ZYn9zFP)Lg;wyGu;!Y(Z3X^{@qgS&cy zliS$?)6QKS%2z+@^~8r=5a;VTa?b_ej+2=0+6+4cB?^z`ace4WJ6TcJkj zUec_O__rm~o4eyXkB5$1X|{dYV6CJeKCT`&5}2MIhb}#xYoDuRH7prM2a7W$`nhix*J{IH24O9bWJ01jHtoqRC;WlOv zvI;JeLa7uR-DU^tWR)^V*K8BNFnt0uLrLHYLlgZhHRhuYeb>wV>=*|mM#_@;Wu9q> zu0d~R2c~O^@4c=GJm(-J_$@*7MMP)SWG3*EZwHZt)xOquO`QI^6~yBV!!WLhj<^b^`LY*dBhhm9&&97`~j@BziLf>DpyqP;EHYa55xIN<`NY~VF z4te}>%om=p_l3ZGvo>__1u_Tmq2%tMPS!5RkK!@m3tc(PC*3t%CjnTfv!5# z;+DCGWKqPds>Rz8<5m0*n3IjhbtP2N^O*4p2%tKYE#riv@!xZ(bif>pTiRhCSd-h z8#??8q>s)mPZTG(C;g);&h2^1Xt6SOk!J%}cALW$7Ct)*h)3nAd01_6>!>3>*R{)) zZg^4s!CI+@F(=-`;i)ppY3KA{{}PppQV+zxbZNeau0A^3i<9l|R~!LX@{Pv}e?-b` zW~KIp11BmSaH?(Z@?#8^0#rqYT;Y5fdY+44-$-0$a%{5XE@(TlySI~z{uVm|>qs?n zkwIWO@&-C|B;;Jm0m*#2Q(D)x{>&r1*vh5xZ*{F(y5aK1+x`AujzRhe`-rUVU0GJJ zyZWR9wcwKEr#UB_y$>l=tw??%zJ+>1!Wd?d#buhY0s#q z2mhyPvn+Yki%pYUjbYLhw7Wisft|cp^{&S<)jMy0y=8N(LV3+Bk4(qyEkP%-B*WMJ z&>Yg|AW6Uij4wpc#h2WvBuZI?UIQX_Z1#9X#J3Tl72e8H747E;Z~u6K_|lr1{$QP) zN1#qsy1<)$+c4#r>YY}#GTettm8a(x@#x8rdUNtG6kvSmfG)n&CUMc)M9P|6*|>zw zZ*%Lq%wprG2c){d^;6~E_0bu_JTLpx*-Ka*%p;0M5aeeU7mx1sjY%fz?bBiEV5>y| zFke8wfY-qIk^o(N@lJ~DX(vMI6mW)_ow&+A#+WTG_CTyJ>=YQy2?g;fsPpF?8iAcFC1+$s9PnIb#c9Vu`Zg8j5PvK2cB@+4a90qX*?F6!X5x z8b(GunKd%szC|}=D^Q*|*te?hE5V{45@q+U90TVm966o`1IttJLPwqg(pM6uXq-ME z>Ce)1@q~n+HBBEcDC)1GuC%Xe_b1A7P@dvPUvR+^9fA&1pGV7;#kVmQJEvU}CC`2g zJ)4{P=4a%`;5-HA#wR);c?#4LrF-93l2Pf!_S!$bcmIWir^7N^;F1_gfsA?LZ!+f0 zOllBc>`etin8{yVKdC)yL^<_jdtIH-^PjCR%U()Vv{RI+t zYEQRqqP5tmi?2g_Z@4>ke>ktJ+~?QR}j(b5+^;}j9w*ge@HzvPf|u~bGF zTVz&6g0cgj*u#dUBXHbtyh3{iEbd<1+qi@HX4sTOA%}1JgCB z(4lJ}b9P}2Jm#qT@w1ghDr_~mn0!LsWOHM=-~JKQ?d@m+>6!qON%-T?;>Lo4fWYT+ zJh;WBuHO1iM6%hT*pgK&Ir=j=lAvIEB^dIsOsCN-Sjq`QSC zI*oqicLa_h|P;_`!$%dTB{yFBNWlM&>6 zz%y2KV0qaV=*r9DIbR1Ay*H(`YQn8A2;M2_ZI$|b>J9TGkNtKS4dk0m+O873n#Sn5 zpV>yZ6!Qy_&eX*(^QE<9Xz+N(K5R9E1N-Lhs)QB5d@~(%_-4rY0K=9a!&%WY(aR_k z-s`=(H6|kS@+@Wk$D}I~mXaXf+>W7*nO7a%Qp|D_x__NfX@>V((ro&0pM9cp#6&vq z>G^;AX2ZWgd^1th>AgK40IB=IIu3@_COvb|>hSfOI$=oN+MZG2)|U#BY;kqIJ0C!s zRdiOlwrlQ$Ci3cWEo#SP?t#N`XE42i$MC+6QMO4vSWnx((+4zep!oKj%ACL6kRjq!r9mv=B; z@H4lmcuZ^f6C$EV635#KqY3GJmIWYPqcA13Bu(=I>M_G*+@K- zLd+K!uFYUwqk&uZ3Yf0>dT(_Nq)$6CIsPcWyYFW9SulCmQ~nYv8n*l-xxE|L&-8j~ zAYId24@c<#_GciLbuLA2h;g6|;&c)g{|(nR;wN2s&-LRX#;Q_Fa^>-8>28!VYYhxGfO0_Ub)JP0_v zw_nhHT!He0RhG$~2xmP7=0=%~SRsUy+zi7S^OS*K@ShN{^~(#gTtMnjB3}XC>zWE3 zc|yoM2d}YL*QPM2?c}~lSwR=)Nba{7;;+T!*jru`wlRWu+?}_pM!=&(szY%+EdQo-lYrh%giG7u%KH*kutIoAkivw9Ff#mZ?(2({KaU=~KZ4mq zC0}Lt)khUB;cHL*_+>syNzYeIb`~b|`M>#Sk$z&jz;sP1bm^Law^@9@wu>5ZbDJ8P zaN9V?N@tRyRDQ|ge@q&=t3#RaG%?#edB@lB)qY9D!o13^sZ_IE>zd4ABwM(@FeO5d z;NQF;jnwo}AbhbwXutQmX3u-@i3--!9;DY}h|kW;zwyk@tnRgX!SjBpO26yVHrk4% zu{$JUTYr^Wn0VAUhqqIu*7Eh=JcqIumiEAOP5iyrHM?8691jKe z*wT|~e+5=;cM_a zH83+z4T+yCDvckV?SJyGZ=&}2Y-uM`6flzG+7r}-{lw|tyx|A^-NnG_pP!&h*Bl2j zs1koGSB*X9$9ytk^#1c%S>Fk-$%jghY^FP1gFh7IRKETFELAwm2!n^B%)r9RG;v`o zfHsM9_~k?PKo8b`bE@ohiL`;~8j^djYeai3$OMTJ(YHR+bH9A|vc`*DVi1mUl@od3 zdA&PG*I-~!>5ju>49mde%tv)Rve|k}$C;DKkz0+zEg`R*H81k!-`rQ4$i6dRx@H5q zbWQDswN`sEAI=x0_|8w?Ud=9I23nUW2HG`B@+Q-ObPa8(IysL#CyBt~{8XENqwW$H z0hgz7Sq2fglJX64%6Ji2*F>9!2LaPHHPE4JAor!;2rU+)g1<9e&qf zP%eiS;PRm=48#|$^+niY*k>4mvL6l;7`0;12`1$ZqGs(Wrb9mj)be?{fcc^+P~Z%V zFH6wHmmAM^9JRko@Py9`_!)_bb7od43S5qVee5h5WmyFArC)$1W-FBXj}P@Db!q7a z6|7+g1;@H4J}M4>P1b+Xl(T~Q0>2!JL4XSHTZJd`UY0mhRk3(Iir#c+fm*i*kXtzI1XK_<+WvApJrnf=B|)7i%|* zT_Aj+WhaD=dH`g8hQ2&bU_&)=io?9e@2^@OL@eK=X;+i-Y`+p z`I9#(1XuAZnW8!kQRNF)-QGRbclWxENdKnVnW!u4H~p+&bP(cKfd|7_ju(m{-@x3Z zKAMsB9_(LA3p^cx_?M08x6n~awXPBKU`J*E#22$>R%e(dxS#u^;;Te8 z*=>G}ogo~@tj7t$45}myNi5`xqT z(p~cK*pXM=L>0^+zC>9}wEXs~)=yY7yZVR$EADq{stsFw`gF$q_+6U5Bq|k{FAi>D z&w=nol^yHe`WHx_gKW-mZDCnnLtOXf>oUJL*>!Hzea8+VX%k4zu!u!hPr$`44G#dJSOzOeJ4oUu%|5+XbZQoXwe zj_%c)@I8ruiBalP-u}8Qg!*#0u_>{?9`JL62QhL}f1M$5$N3xJeJR_}<(p@wK5Y{8 zVGm56(_M(**9b8Cv~+(A78moRX$!vV8-5T?dR6W8!zLU}ko72E7>>zmw8n=EPf!jI z6Gq@S2l)b|9zZOW49quwfiB-{XE4HqrGGm5{XlG2OrBlh>EN7x8}7V=Zbk{JKlnlHz&T$j2Wa| z;Bnqx>O#=|c+5YhxGwhhimVkODQX0yYhrA)u@SuEFl9cKAMpL5#ENG_=~FC^LPhBF zv!zOOaeoQcHHg-JM!9Bb_|O^I7A+Jn@z?fP((&$Ep`9#lyOevbS8Ds+wqqH72}QGoD;mfaCLd^6;H zz;L@NMbv)G=OfoWZMuY=y1w6p99N?^3;RyU0-r#>nR@Cp9BGSbtwp-sX^H07Hg5{q zgmyI^e*%9L;hIsZfdkk#b0U$90P)SJCAjy#kFGz#k$2rVr$47J`F_LT`zGyo2o@41=dAioWOz3Z5403-VA6n@g{3TCNDDDyi<V?KS!}1??ZrJdrH#^A1ma8f-#`28-*F5B7dSI`1RM%; z*{*z+(+c6kc#}{5f84!gR8>*9E=(f=0us_)(k&nzf`p`WNJ=*nQqqm2fOLa2NJ@!- zfOI!Xr_us~-{!d2z2lsF&o{;zmsFz@*f#a7V z*x(o7UVUK<&X4&rfuspCxd#mPMor0^<5(P}?N8R;Cvn}}d!+Lh#?o>Zi3sSf`~055 zfWk!t$vOOS`joNd7p@DditlCszr^RBgW(r`yVF~HuO7&25<;JEof0Zh9o*kqV3-a` zGQ-9D(WIx$KEzSiLIBBYPKR9%sm4_rborlOU20Th){mNbbR|Bf{gz^2t6i_lQ3K>P zHFW&o@|tJ2KCfAbV6b7mzuv;?XxHMaE%rVoed~jhPr7O!OVvyl1b1$if4#Zt?9mCl z2l@N-GF_uDRC1jBNJKUH}Gnigw4arE5G-_UJY%!Y>)P7{jIMDnC54T80MMtM@`|=X zp6AcY8CID^{WazB(Zt0UuTj&wQGcHht3c`jq+!uQ-;0_WW6A2&4751|qt8oR2z=)3 z-=M?0z`Z?Te+cw5WfZG}sRz8B+PwAsOp!y9yCLR;@l;Yr4_M%{y3cdFzUBMJ`=`+% zVq`;f%~M9X31a^32lJ+zE+U;Ak1x)p>ammYv!iOazH8LDkjw+R#_XpMI9*c+8@dLl z2ShmGnkU2n z+4pzadJkNxOan1k>&{uhd$lSc_Fw%gwyhKtb978PA-Gb@IE1u*x-l;>0l}_TJ@B9E~!EJ z5{f&sgPAKZxMScnf-PP1DVPh3{Ic>>QQ1|fH+$PfMc;vF#RxUdeTjbhAc(G6DXms2 zG}XF5>0&={QcyRW)hMg0zFH6=PIkata0oAqh3XoBy&ojog!*W# z;We{~Tcl2B*Aka$L|bxE`wk=_y2g!Z!HX&8T_*#p$+eDRSUc(JvA#_IW8ou?l;lkr z1XUD3*Cf&22d8U}ZoRH)ToOhkp6k~PD|9b1ddYZ@IQ>#H+$!b@cNAg&=KdMaaO_~y zviSyyP&((ueP(!rfxrE9DI5tn*I9T|>{TkQhr5L4=$^-pFgmz>rwh01L53 zPD3qJ*C4-Y*afF+mSIEJ0Q|y_TlQQ|rb(Q)m3U+$_>x{aA@>d{|JLG9l5dtb&k3RL ze5Fhj#g?~bQ>bC4qOg~gUN-8>ODCux#b(gZ_n~zb&^5$AWWe!@Cv5NwP!CA!>K4T8 z(~Toxv@{QxLJD1_#mVNvj)pUN7km&8;TQ6@>BWn9Zn-garau6D|c z;iHT$eCH8BJ-~GT4j6vHEQPzZ^#I`cXYSg+Sr?KiF>Npr!{gjiT!!YAl{g0)W22Vr z(+D8-0EFU?smLGls#uxa^XpdfGu6)O-}N=_tLJx6diA)P4FN|f++Qr z`&*SX6Z5Czn|t*qZYl1CQ;mx1{(|zt+H$Bb;{y7kiZt1N5a-|JV3)hVf$Eyl($DGO zbj`)B*ENYIrDoF(YZecgID61g`3PIOX2<{`v6xCi>-*2Ehm^gi*r!t1$>!=meG)0Fr*T4Y*7OqL4(GkEnWI2Gz@sM} zoUYk|EnU-y*t0=xcls<mvS5>8>5ec4)B}|J z1;OwOW+~dO)iuBzL?v5Cz4fF0jAH&6?e%%vj*nduI7j1EDw?y*VmuICb8?s)njSl# zg)ao>t((+1M8mZABm2(p!L5w`49a%_?`)vD#y>Ru9T;8HsWAr|eGb4J#MfDb4U;1> z@As3BNd3C^Kd#ccAhpuXNNa=^)rLU$g@Xd_Jd)_hH5coX!{9QV>+9osiB_`ujJ}uL z&o{(cQ}UqvqH(e40ES&M z=$gmmF5YeJ*cnC%uQv!CPbd7STROBkr~dHGcbq5(NDgZOc?~1IGB{mxb!&AEfV+ki zW8bb%l5w^f3oX@o+HZ?{6uxK56mAB2AUNOjZ%#c3K=M(mJ+LkvcoSH~_hKqe;Z2sO+&y24I|PTj$Xk!Qufcfb=7zsGJ`G79>pUlS5VvyZU8Y)(4GVbp z{wV}^ldt4NcOq1=P#w*wrkI9o25N+=bXL;59zXqagoYR;stUzj;d9(>aJcKd^|-r~ z3RvwUX18gvdN5oLNlKN6o~4_m??jof zcBVf5Sq(GIhfmTafzA;TN#h5HyXaesJ7DhXqw!#Y8Nc%~^E=Ydxn|(S1aa)s_~e-{ zIU?Z7#UZ%+HHxsa^tqRmguj!?-*G2jm1QT5iYX>&x?Wk=A{Z{G?d!ieB4uBNJHX&> zZK?yd?%@SLZDKu=Bw~d}pR4_o)rkz3q#Pd3A(uIqMA(4|>FZ(}w-M#3Msi)+{;AZ5 z;qxsZGmI}W%UXZ0kYjPTdiShT6nYQu`(XpPzAl?v-`AyMGp1_Ir&S=@MJFdLP0#%` zzRkR#Q6BrnQa$cX-94!aU5)n{FF^&K@2odj4!>qx!%IjOrQF}XBX2#N!AauyZ;l9J zGBpl3A2$gbJ`Sk6YtO451@^i~&S~+T4zNbY_z5BXb$yrh**8Rq=H@vHM9xYoLMXP1 z?&G{{O?B10%=~Xr1*3+BKVZy~7^5sMD z>u#RYqph)a(!86RLnVfcZ8y-w<#$LNM2=F287t#=#BIX5k@NK5e6(=9-Ewd~ZXY&$ z956=&^X##CpW8DuZSGxd#)rAO*c0{Dn*++cf5pAg!y&px#~O#@Lj^uU7i00yP58H@ z_HQaxa+o+97w^9~o0``2^ML9aRe{%eU~~-yQV?wAglUh4x5^E(p7x#xDPYZ7HLrNS zGPC8|C>ti#WxSaqqNeC%O=~B3WuW9;*4I@LfH{QEkM~}DW?%zvU(BiEk_*Z&DDwu~ z;P_<*Huwdoe`l$9yW>JFblo%Oxp;HW`yy4Pd4u|C%x+Mk z;)54%DP#8m(CnTjjS@&)D~A{+bo*q?k$K_S5KTU$3Fgq`j>J`33@R8T;%Cp_?rCp*m8!Qrr;?7bmumBA17&!G{OV_-s*=8A^)h8gvIKdhXxjR%i z!_DiRyF7U0*{GBZ(KX>@YAZWRhvyr#qV&fS;XIv76OQ`QUdurf-C~NGZM>#XUBj)2 z?+H%V(8HFl8G3;|jBrrYu=kab>rlDReJtDVM?mxm>IR1gw=YE3#N7$`Dvz3o(wI1v>33kptd999{MI>I;TmjkR)zWlpP?5)qF! z_J*PSQW{{p3Wi_SrsiOy?w4>gU(SX*x>cxaNHw;Se!Y=Q@tA!sk^6UpjfK`{O8DP~ zHnfnspSViLV#M;B1AM=tcb^Hr8eX90m+Vqwj_olXUiK~B$5e;b{YD7)m%!BhGANy3 z<9Uxj{e{%eBbJuLb?aImgGPQxceg;~>X2A>xs?@}S=bBWUmno(VamQuOzXm-xufO~ zT>SQu`BejXX7_T@=14`1uU8(_zpxBCsDtq@mf;n!QGWsM2hc7weNDj-N=Ei`pIN6* zYtyW?#9n1mxkjU>jmCxOA1N6V%UVx{sk06|1V7w>ci%rUJlwA@={*ysoMS4`+$9I< zFX@H7VCpY*8W*?regJSU#qSYe$irfbLsATaH?etm?e9jnF-LxUBCy=uFmHf--dSo- z*DRyB!il5vTpt^^C>cGCAuIP-tA6@xXYHHi6Tb?;zcgPz1j8?wrHr?}e^cMlch;+e zpFg=F_tWQrMSI&Iu|f{G(g#wKvk}4&+>Hn*BYM>_wz_fudKCok1GoC5W{TbDpi1xc zP8OG2?*lvlclb<5U~t!|@f)`4etTj4&8#|l+Xd37e|=(FX{u`%iwa^c-ZhSz?`bD zBQ4_cMjhEHWpQCfriX)j_aeH(2sz-m-l+>+LHx^7MNeJx-#33P(E?Kk5AYTg$TW*R z>CCPBSolKL2y*HQ0so?X0jjTr-;TR_q8)c`Yy4Am{+6S*x0J7oRnM-2~e zbi<7gg|6Yy^+vz`$Jalc$A2rG7>@Emh5WgbI;}DtkBUWwC%mf#-6vn9m;-ZxPXhS< z&^8om*NQhITD@fFLDR7qs0vri!q189d&WK-446(gNMqtr5QisnMgQ-g2mIXsb5(8C z!v*G4X+|RD=6w(|$1*js*QXz;H|ixZ!{7UCmZ+w4Ndb9Xq>T;YoTh$5e)h!}!k@qF z`Yn8*>@lY|yQ_Hj*YptIi33#EjHloqg3&cQlwqF>!YYA4-gxVI5;cF+AB z*k>mj*_0TsA*&o>H3?tzrTVMPnzuc{n&ZN7YjTh3lMve$b@G3w0t}ME#t5 z>wlt}6WL+sjHOuHn{=8rorAhw0mU84f_Nl2-^^J(G5TPvEv@vSJHj8Km&mw7RqA_! zPDJAU1qelqj}&CO3uU%A=-Ev!{|g>&`g3pg1z5`=`}hILrsKok8=dRP-6YZ}8v5n$ zU!=LN{y&y$yDU-5enr5(P@+6QI*u@_`{jVKg!ISNy5eLP^ONJ%9eb;$DFrd1rM+5Y zf%oN(&*}f+8`%H<`}#`faWha42+x+Iof;IFKJgQ{AVbpp9rJ!}JJ+BiR@(c8rwHVA z<)kMaFL<81Y@8c9bqbGhdPSzpe{(YVqgY@4!F6h8xErW9%Xf)`@y)87Wxdo^Ltz0C zR41ydoTe1G-zpK7d0mlo+E3c=p8)S0x@thz=fKw?pI?=y@(?T9m)hY`N;lZP(4i@OKu8IPb(AKbaJY` zckh4GfjiqG`Q7E^9qImNjVpQ+3f$;o+#*Ula*d%XgnlHo|AL2`^X}$-04oY)ADJja z3lN{&e7-Z(?}v;Mp)Hdnml}`nc`L$41o9jdJOuUy>4!t9Vyfh$@dRtPmOZuY2%Ly~ zc^3e;vCo04Sy~@V1)NPvM2>(C(_rG4xORuI(;9g2{Yk3L0Dse(Qf1+laAfJ@p zSItLro!BeXDCskJkk_U6W-}X+#@^uxil!g;W8T_ve5Mn^IA)oo`q&c}!VW!JsIG~S zNxBD4*C0A@8_4Bz=B4apL}$xw3DaY0PFE`Yp>jbJi#{=Eba6rw@2`DQ^gu)4-@XC( zL%G4-&AtGu4P+k|!qty0iEcjM5-9G|O2*f6J$>E6Pm{to7H0OXZ59@ReJK~A?!J%8 z-|izTK=#9dk&J3!W=Q8M%jdgl41UQsqhgatj!SQ97Mh~}`{x(-D z@zPmiEjYR}{uwVN|hoPPkqR_sl3N)tl)6%_n$vcB>8LEmvmCvy6RN>>rdPHaTi~%IRGIc$U)+_eG>?Tuo(<%{$U47U(K* zd?i0erGw1NP};n`92K&YEMbFGhN|#i&J*DK|I<1jY&in-Gr>L1f8+o9eL6qfiz1Qd zMq+5oMl9Vp7*|de+A%k;OTY&{MS%#4n)9(}Uc=e^;<8P{Lb2V8{gb3A!r~?7t z{M2CyjBj=?ofip(oR8sLdb&7#)f>~VRz7x5|Fy;aFXsvH{r_o&8%K-=a@ni9 zaQEy2Ej|U^&kELC;dK%$(%t)Vykfo@92R@?x(=NPpM*=&?7k#_UP-t9<;a+3&PC_* zSNCWAmVSJw9)bkWTs8}1<|(*bmN7vcay~wQoRjB}^Kj@B!WM@En*MB-@*V@df>I{r z+@v?b)QnDYS^&;B=nD82U=97R{-teW?x)R@eQKTW?C(4DuI^%dF&zT-<$uh-_-;_c zi_AMKy`UPXqHTW7M2CQk@7xBLx{pUxLJ7g~^nP{Ln}P6+gK*9z9F^Z!x>t!dIMiE3 zp=KUcw%z{ENTL2^N*7KBoPP9VSFs>5Pe5dCm3 zS$Pva^6;`G(6-S0rcI?3glwI zs{UW|vAw83rw3J1*LNZuP?|nyP8yUyR{f9s0`mX-KhMX2epyC40*1F|_QdRn8RN>~ zf3M4SJ}(+_oNn_3oZS?|Qp9JXpgU9`_g#=ax)=+1-_W%by8gGhENA1d zCxM%ErBW$Bi7-PN2qMo%>45$JAIoL?-C25zjyU4#6%2f)Fneo-V%>Ef>hwNR_*K)! z579NhT1eEZW?#&!J(}coBzYZ~-5AUEKwgjAruF+Bo}dRS$$)QmL4FEG*Qjzf-TGXX zS;Ea=g6+=8q8-*^Z60AfDz~HoGo1ZdZ^a`GMF{Q?6cl71NOP?W3R2I9VtHjAZkWx@ z*6H}eeJZbfkv5Ss1;rg@Ch-Co+*yW;!&WXE!!bilcA8FKD;k?G#GwAVc7JqSj8kS0oBEWbwPkZ*Hlkt(TxnOwc=0L^8iA~LSQ<+9kY zmCJUUt)G#)3Ods)tfLh=+NRU6(%^qeW8PlfkD9od7vz1t^QuqnmDWTNeX-Myd8$c~ zpO86QV3{G7<>%u(nx7wldNU4%Ah=xiHEiXw^2n0w!F{}3rGdzU*blpP=p0#+6bVKj z7)2AmJBH|*pqZzV7`@e@gm*19I;*7ju{V!V*?Mv}tkUK0HhWJ-pg8@TV}L{(dIyZI zQM6-*E#J%{mw`~d#@q5l&_aMD-%1RfwSpWj5KnDxvMA{09+2ao?hw3MOR&7F6UM$EFo0YZ{_S+|IIP*{vj6$hF=&s>tV|`?~-fzYT%>2_xWsYa5q-r zTaB}@)7C4AoztW_&LoI$-mdkePgt=QO8ZhSWYvfV|J1Y}bM(T%u}@2Ax$=ZDoVN1c zJyWg}tpRYpc?hmT@x~kNE((mS&M;2tIPEU9KXN=#(NkG?@7*AJi^ff*W0O$p z@mYz^0Brf@sk`O%TYpN`*fg_b z16mhHkKJeYRj2XmHYPPDZ*tkcnWQhpt)em>xN%3Ggv_C`v#*`<>Bd3FxV63+=qsr#!H&9~S5e}xs;@SR9ebm`II!r>USmb#g0FEi?=&@O zx%(h7Tj+DBf^OvE>WbkmE@9^b#O8P3`32N>QSykO{DQ!?e+-Ua*kFTSfWDGe&iebK zZ#v7jO-TN-_Y9n9ndEUij$D_%VyPgYhwuyPxXW1{?iCxo1jaGzV(a;AL_e31`ES1- zsmnF3uHZpgD8JbJlFS6dFRGlCx7NP^zIn;~U}oK1__??O;bYgopGs;7g9qCwACqxv zn-6+I{7Y1&J$F_BHyW?|Fx5{&0`d3ou4~6U-+yillgs8U5-A`h{hO0R3&$1##=nq; z%imhx4Ah$^Px+dE&<`=epRsb1_9qH5o=My~{^uqiEbL2$QK#wU?#=_4ZfqT&q` z4%U&1NXH7P4U62E3+C=f<3y<;fV=4kTrjv};EaYXUDHJA;atLkD3ZR0hGr|eq-!N+ z+ZugOINaZe`RO}|uIWBlxzisSur+!|e2o`*u9%3B{6rE#2H}-$-|EG3?*uBKYuITF z!0DP}*w8h=bNBpmN#Rg~u#^9OI-_9c^+s50V1I$#vbAvX=RKbfMAvxNc$)sb&f>GF zwwCd3-XnY)NMM-#M}Ld*1JQN3lk!3eplh1t?ZD`ow^MKl>Z@m;#BIxpXsysx7EZ+W zTm9=(V7<^#5SipYf8KFz2mGM!$P z;s`NFMFp3xQ`UOrsE{ zh3pY<6_f&i_YGYsq3eI!=U_AB<5R`dNlEn6h>Bm@Aax{0j||xV|FJ#?v0NW}5tU}i zBXhe-GbFNOBIkl{O~fsiM%kl#&XC`mak7LeXZUlu@yd?l#u=uRZ(>i>9JH1hlaz7- z_1uri_My1TVGmUUgS)jU6xh(yKt14^X_k?_%%OY;*~_S@%c+4yB5ZuelVB6;V1gA1 zf`5}qxah|ji~|~P74O=4aCE%+pn_+Z+EHAzhoUs?n6H!y=;@=_Z7_N|gR%^^>H$xf z#r4`{4=$@qhHUnn1zc8F#deeu&RFH2uRoE8d>-h2SxnNiB`(B%e7u*CfhIvD?r=Vf z)@w%S8erqi|F%y%v}m#?3qu0wDzrEGp~+G!@8)qHADR)whH z2i_RV3{6rU*M+grgxtTmlcAj^qhRU*bsFpr+zQXiUhak|NPeyqyL2%{!|kqBSr)cX z7~ydN-Zyleg0BB<|7JHX3*LTx!&D77xBSa#I@{N3_uPQ}{~zn$9BPnN9B&a0j8;(?I`bKg$?V>6esQ@zpQMQ+MAYDl=d0FCX|6q9+IamWANI zU&U)0lOPS>EphOPnbpNam~ zRUkijYO`CyAHNd05Z5hxZY4COt2VGj)_!yDM6bSG5zOrpezfYJt}EIi`%Zosv}^^<2~qB`^MlI?$zdxe z+?=aNu{v138{zydRp&B+>V5j}*T%cXp=%K;nm5mT{40vhdsaVSKG{S_yqu2}Rh5OZ zw@Yv!WBE%4SDz?u3LeO7^kr$l=o(c{zgwRZitas7!u_SW5mDT(Bxlr+Q)1KP(nGnf zBBIB_cQem{w3Gt*X>m@&K0S>mOD=(UZ%21Vg+TH8Y2-(Yt16bwGt1F~ooxmP?!2s2OjlM?gP0IjD^fld zW%%=)*#vl1b)L%m7S*U%W|Bj3XRD1U1`c-_w-$H6y~i`W#}?(%D%!>3(wm1x;cIx4 zboj-?m0Nd}^dCAxaF>wd>W7_Y8LDC}{A~@(XHz3-dq>WRPkvtl_2f;LH;E?T<6PC$ zz~Ig@+zht*YvlBP1tk$aK=roZ@2aO*9a!+?6%es}b{2m2hveoyoYOkthg~C9EYoRC z>k@(4D0rPG5*pdHBW1<($gdpf*?&RxbQErX1QSe?410gZzSuww^3H z7wGdFJJl{^$VU!SG$Z6fd=!rgd7xk`_7qvacgWEW72468(<<(ATBhj0~`0=f&?`;O7A2#m2|ZHn(V^$D$4KWT|r zGY?p|>v>esaVYP$<^8l)I`e+bCxsq>>2CD_$}jYM559oo7h~Asmu)T~l>0895dI)w z_EKX9chjgYE}r*n2P8iC3dM);%jckDDHLWF66Fl!a0GJRrElIGO7V|~JT;SvU1z*} zqfmjoM)+eRIDX-TEq?LY66;o)!3i&JbXey0oxKm4UfgsX^}TtD1M_YagkNMw^e0vA zejBoyVrgeO$KB&Jj&(H>|1lSFMU71srkI0W)q*|~aX?_)XxR|3Qi5`D!(rdk%{ z-U7bap@SYAzubilegXO%;_aLplD`IPZr@9+Oe%V(p}@aeGU(e*+rF)W&$ z2*NM-zMg~(2Cfi_C##-kq%m{Yt9a`R?&8~_6K8)=mo)l{3-}j;DjjhAA_yD&0`xi5 zj4|gQvSG`O-7z61?*3>PN_1X{SEz`|U5YJ|bK_rF;uw0yPLa3SX82b_#imERM?>Zu zIKI`T*;R9~GTlD`{7dBAI2eAhLQ=W)eGX{TzX@r~w6faBo@~<}?J7KKJ(V}ZvGZ4a zW{BhpsRtDJyAbjYdAu%<;@gDJ*K3jF%j_Df?zDj6_~-=)FT1L|*mJs=)!K=^k7QB{ASr)xOd45L@^+LfT9 z`XVk~bJI;7*X!^%nVZDtH)^{YYDCOQzb-3!{ml|4cbN5Iyhb9 z3>&%z$Yrw%45)sb@M{finJkcS$*-=%ta5YTcHOnmlYrp3&M=yB@2d7M*V^{my9`emWd)b^B81ZpX0EIIrw?q~ z8u|5SC-hWfNb?})VuD*}%d7i^KNdu=9xZn~#2Ct{=gZ|X_5UqIzV|NKAT|@|->mBM z0;g*jZ@sSZMdij7U8UzHik=@XeOZ<>5Y-rzlKv?;i+pAw*iwbKjKcXd{Ec5uhI6yQvFK*SNrjt^x9z-8kR+T)~gVD26loAFlY3 zm37`E&hV^6G;3*AAV72t`lR6|&*&$&?u$scN0Ku0vkT>tA42`gDzlL8GvgS3{|MDJ zc6=z(U~~-w=Q?cVHFa}GI(_Cp+3 zwxeyn3Nv+y7e4duzBWNMs3sC~usn@dX1Y;wIpCHvX-T9B$i3XcAi8Gm`3LlO zR(xvV)w<|>2pOLzes1Cli}91~O)^B}iF%$yKy{6t*%~((T|*X_1IA;rAf? za+e{~o&WebN~CDM8FEH}4Ur)VcHNP2u=*e6@0oD6WodwKCcEPehF?@US8lCu2JYLI zIlpEw@8eC^t$58&fpO6N@O#&N)=07vnpF(`|n^U4BWY0{3AIXv5xv9QvPR4&H z?rW>T(Eai{jv*!#XAJPosCq$Qd^2e{@vZHn19XjeI})jY;(HXD8f(1Q-{}pVhLW7J z4omSVG@sMo^lxr=P9Fa?j2C$ocb`F3r|TqxL*1B(woSsPXudx9ePK*86n7TuHTz(2 z$H4g=HgpXz-}#fG&+0g81_%97wn7=JKgXbW#SHWH0-1xw{g@Ppu6cEUEK0pkk@#9n zqEUzUBiKI_#2F_aW4&mjr#$>eOvZ2x0bb)RjmSr zVEFmhQu&%cGOvmm(_SxabNo_<_~zcdPYc`q(w~`(RcPbVrO?QEJe|9;18CG{EtxKg z4yaV1zPapZ-4cv%emmuS>+jprDL-3Ok!Kee(AXwsW&ME9n{vG7ZSG<6z#dM&5u$6X z5NQr_;+HMgge~pVJcP0rCA<%pA7+2m%uwNw%E^J;_xX3D?zMU+jAJ%AX2vb6Q$@O+|5xJ9EiEa91DOPYHiuE#aON7#glLXD(v(6T853 z-iY-;Gpj;3^iFZ#zxg%gOn*kf;BIZo5jOG~;J)ppkDxcHQo7`?c~dxjIeN>!9$5o# zigw0SLmNxTK9)ysAS$s&RSxB;=aj;~Q5h|q`yrM>erI|1s-j@Gv!Hoi_}~1R34cyb zFnLV|9;A^Ucbz;hTXz2Q-5t*2rVzJNuoRTPoOrDD;G143!;ADxUC=MMOaO zrF?!<_o*d=;l>mD!J%!Jt!a`*8j1bK&jb+la(~hEg=<6kMY+b*0UWynpGzY?%%Ano4LO}RQC_(0(|Y$^4ZnYuC#}mp9W}Vh}Tlf zh(m4)4M(BTyF{zj(q1zX10hiT1>@O&aj}e{o}zViU=${&2}%mWvr!Se~dA zy_qZi)giElGwU6XO?uG_a-qGS>+o-VACZg*hC4Es$f@d4^#lB(QG5>!zwA(s+}e5o zFjqVy3d^eYw~A`oCVcNbJ9qMq%H&PA-OX~0mb<^FA$bjENW!c8{(<-BNidpt;JBxr zvZ=awX|Y}HeDlQr71|0%P;Og^Am^lgY%Mcw1xPRaN6`Eg$^6z;th8n^U&1MCTY0JV*q!U zYzW|RH+JiBH#V0CtyQQ13BTovrUcy{DX=O6LK6Z^ z+@|GvGLOnQw=6H%cuZU73O5XdAUUDBer=HcBV|zrCgWNE9~fH_D%M1A(++W8f5A8M zQ|XYDX8AX7*oLc#8%$29%6V|>^XbQro(udWQLFihd7wydJNruBc7_0-D#IG{PDz;D zO<&jMqt|12Vxile?kRotZ6VWkkXFoTIobl_f;Osm2~W|XxTBm+eF_eDJhv8iKt5e; zBz~lK_4lMlMEhk&`w?EBZAk6YyQt07*vuEW5ZtA6cf3i9o?x}bV9_MG_PR_gz1mAZ z`GC2_uL@ss5p^U1#hq=z3?~@eF>nsSMm`P9P3rla_aP;GWDD6kAo*|3)0LC3Xn(|v z`qDkE=-LQKK7DyNH|)!iMd{YN&a+qVy$|Q@rM~R^IY^(jvDWrFX@84Y1)XzkJ!k?Z zpT;b$zqNIDU~bai7bPD*i@qBlF=&)HEc(cFqG%{@L}5A@(n^T%5RxBwkFQFE4PRk# zl8NPQ%`l8oAiKJ#o2PbUDC*!-j=2#v1Np(P1VJ$QL7j%#t)H9J-p1f{cn|B{E=JY_ zu?<4Od=8z$`mriAaYyP#Is|vEe5vJe!!eq*tEF>6GlU04*1r-R37$wb-6hAZr}5ZF zf#U9wFwZs^+_{%l!A4yKn42UiWWpBkh@|xqcamx3soyq^1T#x#`!=H?<`S6#BtMv? zc>cDki*%H%=uwDUZWS*{l5_T&It!mSTc{q1?Z8YTG(RZtegpEkWHPXk9{_sVbS8;k zYN!%@M4vZ>1O0Avgsc6)X&LoETypZs&D; zJ~J5x`~0kZ3)R!3ae|cK^z=S#>FK2wq+aL6jvrA%Xt*7RZ|r1Nc4ecb*U2Br?lMS2 zbj_yh<}ati)Ow4RjdanUO3%f3CC5DO&#=6ArgAlmWpc0q^fY1YG&o(ued~2iwq3#_ z`gFO0*0~Rfr`X(y2mX9+=t3cj&!)95Z|+G*Qp)Qt2E*gZ^~$B>whvEP+i*CwqD2u5 zvsNMhYELGo0rd1o16y#qrW-bN4N!Lv!OC;2$EvsGDJ|)zX80TwTx@(amolTQx)#sj z2jQ2lJ1e#HVoIKvW5G7@m0qk?EEm2-+PQKkADf(4FUK8Bp!~vlD%T2*Up~PWzhIVl z#LL#=UgjJp)U@zhX}4!m1q@-Hx~-AzyitPi%d30C0zE1J7{=1rbr1oypPr(ST{`RR18n;z= z?k6S)5Po^gs8s6}=DUj(n*Q*Kb#GMLX2xO(oR{}U)ou^uBoaD*D8CH*S_gsQmmSKL zTb~nlBF4dE?%7LkV)C=t|3(hN)D$sce3AIcID>W^QeRR(?JjwCz4lgd0gW$xpL8tg z%U_Ehe)Sy!2sll_pU2_CAz zjyAuDie&PKxDedA%GF+rZex54w=pxR+!nQ#%^SDjdX`Aa;X7RTkTmdJJrsA7^i`JN zaJP2ramR$I=#*=`oK!P8g^+fY>8fu?8e)h;lD~qs`E(6}yFCtf$Yi$=Wac@H6@D6Tn?CS8~ml^ z{5UdcDx`D}+}UPZ26~T0opeT>E;9Tw(eigBjyv2~&D(w`Xy^9F>scEVcjM+rao}*b zaqDp>i>~MMrYmkZ3E|N2MMuD1yJh)1n(#s}G8grSIDl$$)dKj@S*?Ok8^m|o*?dJK68i+?t&62~k6++Alr1BW~DTaUXfM!Oes`16Qe8;Vn^KksRNGTpXO-14Tt z3m5Ulf#9zA&HGLBKj)!Sln+7;wcju2+?ja#XyIGV@qj=Tx*Eshd;oVg1QOtIcX{h^ z_t{}&EnHEo6+=t}w?=QNuDp%L@}Fy6L@w_aizIG+AZ5{< zdC3}q6c$A2Ya`zsaHZKhXD{-4pX zcY;#|X%HQ6o;NV1i*3RfMKktOLY&V-?@O^|T>i6-ouXhhouoE|o{ZoEDDH+&H}}Bc zE`#z4HgYSVukwyE236DrE6EdHJ01ErVPe-Q{qAPuyv54d@mDwVZ&?Z09_K!4QyVO@ z{dQ05nzSAvrGkvWVMW)9SSh?LO-T!yTQQFntb@s|EW@c`tFQ9g#dDdv&z>VJr-Vhi z<2eg>xKw@HC<^!Em0yo$gVaTGJx>CgPVebzGRsdB^icmm5VH7Ey__YLu<}OH3h(k2 zCbTXR)=(w`rY_Q{;RqXjmB4+((8RkI6AuloJ*W-hYm>#Ki}=vWyXpL;7DfeBZ|2|r z8KJ58^!)4b9kIbgIH?Oqw)P)nY=jxlECScA{806#=7GNbI;$x#eU%hQc(8RJkzQ;` zFoEKnaL*+zDA$1Z?bFfzu&=MZ>^Tqp-MbXL|<^Nr{&3X9n~j0Hrz~s!5N$>$HcZ^gWo{iooNpnTs}Px zTlsW&lZ5fdpjvc~j)k0?Zt19dhKUXOxb_@9oYJw7d>Y;2FlEV|<$3Y9>d3XZGR@QG zo$l2^UrvnzHPb&VFZuI<`sdXK4!C@}9Jcal4lxu8#q(9It={O!PBPwCaQU`x!xKDX z`%xIqHE;6iu;QP6%8T=9il2gAzRwP5e`7)2Itj(#A6cHzh?kGz0`BJw)_8-EyxHzgweHvrak5b51VHw|{BZY468Dps7ErJixkrK~$l>>*@a^G>iTpQ@1Tn#rh z#Ab0D#15TcT65Hy_$>?g7bN|5FgkLF^5oX~7vTBQS4#=KRH(^<4qjVMjfV!IBzetz zdAiqzg&OToZ=P3`)kQ4L>l*T;kAGf*>70H|a@^Hdl|SZg-;&{=#MD69dnmskbe5ol z;}=EP;1{5u(DPGWV4THec$$9Nig5+~D=u4Gq3X=`78P!$Lo^7#c(=KA2zL=k_wPzP z<^9r2S^fyqylNz4x8=@7mGShSkRd3)*a;Vbx_7L~IdW_J2?75yC9!GeZsGcgK>b`F zMU-ImtR{knneYYC6DBKidx(Fbf_vE7%@rd<_G(|qVqmG=NHv()XjP-G)#EUEEDz2m z9O_>fWR~{8`Im9n@-I*B#3&yghMde_y(H8gcosFRHoS%(&&c?)8zBG};$J%KJm{It z{N)CnSps>K+ojcF236bJuT+00KUcN5px7DoWS(Q6;1sRei=tGdl=m0 z9ej;{R;PjJdz|V|b5%E7z-HxqRPp1YxxDZC!vnAd$=sc)JvsfT^3L7IMb|F)pq_MH#n*cu?C_KC+-lr zd<*TiXrm9Xb&r(P_gQ9;H~cu7oi5pN@V6!JS}_g{wVFFiO%klkM?#xi~Jiy?Nfs+w7d^6Cm&Z$EnAN;3Ava_p_-TTDBhcpmxY1`7w{Kc7nmbFB-5*xHnEHdV|HRO&S=bnjVadt!f!tuC8*CI01AaT{iZ;4nygvg$<-*8b5?OyEs7CUS^oFfjE=wFDl>0_L>`#@RWvD^&nwKwqOB!;w8-DX9 z^|xTE<6B0wo^7>EU*>GNrQO@8yfBC6HF=p8Pr&6hMX-_A0Cm5X6Mxw`v4ZaBb%wEv zMBD?`RAXCpxTn9LWxq4Nx&N8OwbWM|YpHsFQ68bc@Vrng@pGY8cT|N9)$p?4M;}jk zXkIg8{?ZOiUXwwI2wQc(J&IpXiDk_i?1o7>v^8Ud!@5rurwAz@J+ONDph|&Z;?ND9Am=CuMM%P$|Ps5gPcHDQTV0A5> zE4Mlyq0m?2cX7hmpEyq^nHoj1zN!25T09S`CaJAHr-8GZ!{3rI%{x~obb5{VMBg~F z$5B((9PrI;wcg--a|CShOAQUazo$j?f=(P?9TP$HyBZGEocvk_v!XOftr^NMs_`_f;P~aot>+h}dqcg7YfZWL zt5b@L+o#A95xu@g{rY6jEI=@#58;;{D_&IgcPP$naH^yB`mF^WXWULGp>)$R5XsAWs$kL~N892Vt!6E-Q??gl0LE@%g9ZS;j zGcECto#?Ie(D;-rF2BTlI`nOAwE&KAo5%f=%p(Z@=5>w4Jb4C=UkG80U+x>Wvm367 z?^!QUu%dl@aoA4FW;-yMXoNuiT^7PG*3{m43Nvh1_}CWh%umsOG5)QsuUEE$=ZEmi z-}f{+n#@~>_%>^Uf4e$HLL-{dXpA~$0(qs+5nqJ12fO^6*A1o)6oqvoHr1`2tmJARhA^hSZg3ojCBIcgi`j*4wsvZ6a#vmVC zz_^l;_`BEU*3EKK^w4>b_mS>D?vBFyDYdv6>w={bQl~!x*@scT_p*$574nJCED*jN6W_ZN0mmPMW5(vE z=v37yBdGa6;osaOR6%%%|yyTY%iEq zv6GZblFF=J4e|$j?n3;h7ODvR$Ik1U?(RMdt!&ep3Zi9wKQQ>CP~E;dK>S5m{UKvGF{u_Ygy;_l>_C8d()BT~ItQD{B9hcLU?8cef7+RmG$zb{o4SD92Y!f7^fq zHNHIhg<|L%Otd4D#r&m*lwf~BShnB<*7wVYE`KSpeSzB~ zMw$5Z2380jgGR=o2T=4NZ3EAHG}TP~DKIZ|snBKm!`i!|6@Y+RIyfcXp8 zz4w=b5@OCoXJlCZ;_pcL_`{g+dL=K2>mI0Bj9JjRg8U^b%%x8mlRPcqP*&c_E2!6o z_BrCLnzL5KhF!b?>?|z`*k9-sdH~HwvkV!6E`RYnbUHyBDVuqJh7;r8!!+Do%_#h6 zF!~Kb4w(iy$X~v6jJCa6T=Jv9l|L4U<466CBT2KHr7F6MW?+r*F1o%J>@Pu888pEB zB>}qp1(j^t>_YU^vfRG#>PNyWqz5#S9XKB9-Z`V}8_ghpu^oGa88oDW*6lP>EAlj6?Pp>JNA99Zn^6S`o3c@wX=1H@nGIXIxJzrS2COXX2xN>f=lWnPPz z+yjHTE|zy7`o-4I?qeP(Uw9CqU2xv7bI8vMzeb0mIwCgr&QHgRiQ+ieV`~JSG1%}R5LHb$#&qHEo*O-0Nd@46Z874C2FQh1sK>pH*$1TCq zzJL7p(cUxZkufy)lLPjQ$v}n`w%B^U&4})5h`+!J#{%=09O&{FX(Mj85ndN*vG}aF zecnD__6k(sPiI%Zr8|x*`h)!Cyh(=idA0AvuQ@oR*a@k)hGp_nk7>9a8a(|fZ1mbc z)?k0ppMCrun7{mh4u65n6>pT;#ML*VKqwL2oa$Rm6g(+MoYepA?>57u@bm!WFD2!Z z#Ar8xztD^fW)an`9^o5}R?}*2&tN{glT-NBg%@?WCYwhspaAyJ|Elw7Vr0B^e|AhB1#fn`3rS zG8M%>Wqjs)Ng}xaGWK(<3rPQEn}Ye?@5ji}Qr$nN{klrY;7K;1{=-Z&$<5$n>INGo zQM|$390QJM(p#N^>1Stac^;{jJ+95B2ROdiZew~lYPGD*re+#o-NC}QwgJ(dqHP>> zan0L_;oRG6YSOl!MpzF?_;Kac9i&Ewgf2ZFRpj2ycc!-tw-qZ4B-f#&QPxmi$6|c8 z%0#e(%d}{qzkXn?IK~U%n*MVyU|fR&U0n0NDz;2HT(oO2?)SA!4i0Xx$=b`l$`bLl z&J2_S5ZAQ(2(Or)DQzA!X*r5SKe5z9n+w%d(Mx`^rHR^m}@wh(-wenjpM!N z8WTc^cQ1ClSh`JadtE{#-4P@UG6VPw#CEda-q3@%CaI?CiwQ111Lu1JD~ni$Ce*Us zUzbkgswvbPBb}PNEt8PG-x49fbtT2n#WkFW{hxmwe{d3Htg*K>5u}Fe+%{IOVPDK4 ze6Vx3Zt+YYQNbt!|64!_NjAz$_>l)B(b*GRec^u!X7+K4mDjkzT%%$y5DAQHI-!ee zBt{V4QB1Q~e$9B_WX~$|vc;x;c=FTk*%OQv;%Sh-#11q>bBNQ29h#N~#8)P{_NI13 zbIR}d*1)4KZ4=k)7=ir-u?OA)h`;dJ#@~Cck>%%qr1IMPPpM?xievaECkB_-{0PXO zV)3RwNk0T}O-X50h$v&ml`^x=Jig>Li5wl199g+bcS%Z2{@-Q|2ceVQ+UizB(e6o=^k^ zL!xZoeDkuH8Jvu}A#As{9n8+^dg zE2xnv-hVgW**5KiU*p(n=M|r77!nOHS|QmyE>=^orc;SdJ|*0bXX#+wjgJQanv=6l zL3?j?2if0DqcK!HuvifJvmA_S&lWq?C;L8l=0#%%5s?)1zxBT|3MP zyx#{N<05iS?wAi|ahp7PtyTKg9;`cLZo>c|x>K}GhAy7YG17_~(u61eZCc1?OoO5P z9g3-BqIqb>{IHK4viu-UuksF*{8F6-=|U^^531ST=UtsNa}H$-E7uo z!T1c}V8o*fPA@X1`sf0R2F-BXDn&3)KNYDW2FBB7_nxQKy&s@GTB%l3JgUdzoAgyP zpVY7@^YnRtmdVJ71LEnGz726{ZoSvbe^+vv^WCS<5`z#fA3u3{<&i08S<7Sb0K(Ia zWZb}V!W!t}Y2?Km$4`oc+p#`twk~Y?8&s5-H_aA8GBykTh;<;I7UUsCaejd3*{L6Y z3P;|Bfy#*e`!$Z^Q|@xfM(Mwt3&W6g4(ko{z<7EdI(Qo5FL`9L*9HS*&4Eq(12xGt zuxWgF-8*7_4t4v=-hH6&MIDpO?r1&5!O5>)xibhA-uyR+u+8MM-nsm2c(Ai#8-5V3 zp>=8o<}cpR-se7ohdR`}D4k zwXhVtKN#nOZ8BF{t+VP_M>|+Bwz(FV>ln3$sgM6TKfOfKG8Wi=j2P(JkD(V_ev?!o zz_HWs@R0yizpPYzp;2dF#o`+1J2LCyArHMB;+AX9HHD)&iOm}$qUUwh4b;nBVZM_eZX<=Ud;DBA^ zF{Jk3b==I+pu2hp(jBct!RqOW#QU|!hy}|UvrWPd>+j+>j~6{oG9Oe~hY?jlbQiEe z4or6&_g;63B)6xX#{yseMk3_XaxI3lBd4@SevxiT+C4Ah0_m>b@xya&bDj2e0zt`_zR>JsknHHe2WrqOe0-09bbv zL5*cVbY~f|1zqcgKbFXx3mLl?3(+)g?arARUp~QBct@gbh#D11xvPhi$_CDBLKx52faNu{(3RI%%6n{Wc2X`*zkWOP z!LRFyU;;d{KOP~&ljZu(B2ZqlL&uzdy{7(+KJ8{efgFXG4HNj0!> z1IcRw@>zi8HHFZX*RcJe)IVZD`LHzk&PwX3F*^5eGBz$>o6>}g#`JbjUZbwH)YO~* zo8VIZ7U4W1C@G883CI5LU&h?lIQdcxl+7Y=UW4>UQ59HTa|j)I4TPs%3f{cw6;+kU zed{ZD8u&(PDe(EPA0HU62k=oH1VR3i@8Nw?CtHYrBQ(XUW}*HlQA8yNJ5cl#bm(In7(6Up=B=6TCJNC&jMqf#yzm0k+Q_I3=d zl+1Ptfc+(`(SIM9zqCP@zc5@g9(JAbsK#m=pmgjpSnA*LH=9e z{-b5PWGN(nc!JWAGPPog7&)OB8%h#)SCs>VZ#!UrQDco;1LiMz(B&`wk9AV}rc^UA zuxJ!f9WzS4?zRQe&(fziYqy@w4p7gt1MHQgKZ@ z!#7n6={Fw)IRNvQW9aY~$i9^6j4r=4HAG5HbzL{s?3Y6kug$eYUuWg{>WshU1o;aq zQ9ZpQ^*$?B#-G*j@oIH8!cVY_SCd~P{n!meT?T53AoYuwcQO!vq35uKj(z}S?qpI| zpo|9-d;AUNjaPC;Tp#&M$Gua>Z-Q)b*lu_GQeciS^bPC1);W971*_#%&gzs|RCvy5 z4q@BlBcIvQPo9JO0lao{dO-RC7=@(wzTb?^!=o_RCS-hjad9d;j3&bUizvyiSzcRcYYXmS9d#O$yZm`%9<=*%2^*8G;Uf zfy|v8ZA{j_eVS{nWUaunCT>4-pwQyy+4@6odD`|zFvwqS^z1`sp7mpSPRAjq8ST)> ze?J&ot)WIsu$goI$IB zBif`|E7|OU!)rhMKz_Era-cpGDfMl;LI?Y+C1cem2e5>*CpZ!XkY( z%v5TMQzck;+V<%`faxyn-s)}-tUG}-j#Iqr2c#3hiJT}L%nYiE`iPOY~nT4`cuZoHGBO`MaiBeHtvN6Zh?%0GJ%4jDg(=@m-`Vx^y4?P?~knMAr99^W_ zAf*iGdt|Eir>U4tRXApIA{+ADqpG-Anl#o(B6jMIfCKRrzxWYw-Ar9~Yz3rlCJ9Nr z_x*snI;MWzx6uahCSvlGzm^ii5;Y_II7huN4J4!4LEob(A?E`MBy}YIE2wg(c9IS&0N_*!OCNn$x8T47RY zWAL!kLtn+;f^@g#JV^5}lj?LkkF73#Yb(s?s-u`aa21;sVbW!jaFA>qqB~blfalZr zLswq2iqXKAaok=XiuBMV`?xSKS&Muxr=CcB(nfKd1;hbtZY7M2y}Wr3_6g0qSW?@? z+gfM$Wr^$U1qN=^e+)%6wLR&f+*R6f8!R{xsygymNCZ|@Vev0rn!jkm=%t6$$89N2W zHO=>yYal#**7Gv$ifl&4>oGj3>~m~B7m1jF%hqQ|S%-=+`Mz#Z%6Rtr=P6+8k`4{2O zlplQD(ji~>sPb|4@{h7}b62nWXzabgd&f0w_S>Z_@aqg#FDrk4Cg?e~CqVr^ieE0T zY??n-RP>!p9{Zp9n>MfWn1FGO`@QEH#$K+n_6jGKgbmKB;*V>4A2jNq z?)p#&DYxif65!rGfP11X_SG9(PT zxW?2Sadq%zEVJI&(mCy1Me~3ZXH_JE0-Ef@0|7h`*NhfQo}R&d>|B+1G>UQ z>XX#tX5Wt>f06NzB!Z0&aI3y}ky$lyIgij$$WcM^ zk=xBE78_D_JvNwMLo#>79bhEF@6O#zTO&1o-MkPbt5w>EN%Ni1p3cX{s*U2{oluBR zV6Gpg8klR$q#dS!a1A|&J#_RxA^V|RXm%%Jx$4cTd)dTN!%PE8OiY)t%26ipMW`Ko zK>dV1neU%$Ly*gId2yufYP*M&Ax zF&{sjCeZXo0Qrk(E(bP6dzPFGQpgdHSnnUA1vtTh@`GcER zFWVHB_jaBGWRA#L`C>}4al&xbXH}*Pp@}eG{%ks_x6;9_YKZyspt`v^g%MvVbKaVr z!^LN|jp^W7$Ou~ZOt;e^CH&LMn_8kmYCDHPVw#Wki}ML!m2)+FD2`C#@oaYW7?UI#;!DCP3+QzzTIet?Vb z&+2bM1ugV}oz$~BuiDeXy)-2>g;%IT&ucn#)TlR=^6AG z9^4#YK(%>afY|uhq-MFY;l!q1_p|$-VXxRqMIA8LJQXM62F5j<(7`p3egMwi!4?-X zOvHykibs3`spU$g#%MHzBIhDkl<%UOIRAXJB(XBNQUKGsFS=-nX?;u7dAsT)>OYM;8H?`&KwM+4eT>N^9f4_LwjQ-u z$%4F!**U0Z-eNm#Co!rN_q{P4%rzWKO5cES4IgxH4Wu8Cy#0~-l3HlamaW~*W1n5R zS-OEnGt4HIe@#g5j%&hJ)plmCtSJAIA}AA+TDOlQ=l+Dvm?ZK+%|WtCa)c8Eb4@{p z6Cx0)(b7`$jwAcJ8jq37XWeEZ$H=sC(fgU!Bx7(ODXZXW6@#adGsn`CF<^bqt*zn%UEMM9?1Ab(NY9U^-2PP1y~lSa&^iYd|zF5P^XRRJ|wJWDb( z#(<}dkh*!UR0oK^ScYUlN58p26ZE+)9*3LdY9$lUv`yYdsJ`G6oPyUc_)23BH-D6y z*g*&ScPeCSiRWZVo75A9pGZ;QY&0U@>HOTzPCSXWS|9lQEUbnJ(r+f^ehsAGT&;C# z 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�SUD-8_wi#<+{-dvpB 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Ð(x=QZ~nI> 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�ov!wlqa^nIaBA#bk+J4`LunKu()~h` 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

    kp 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