From 1256b6bfcd1fe89a7b1ddef458d8ad8697be22ae Mon Sep 17 00:00:00 2001 From: fi3 Date: Wed, 13 Nov 2024 18:30:23 +0100 Subject: [PATCH] Update channel factory, coinbase input script handling. The coinbase input script additional data should be sent as part of the extranonce_prefix and not as part of the coinbase_prefix. So that a JDC can see what the pool want as coinbase input script additional data without the need to observ the coinbase prefix in job constructed by the pool. --- .../src/channel_logic/channel_factory.rs | 119 +++++++++++++----- .../roles-logic-sv2/src/channel_logic/mod.rs | 6 +- .../src/channel_logic/proxy_group_channel.rs | 3 + .../v2/roles-logic-sv2/src/job_creator.rs | 32 +++-- .../v2/roles-logic-sv2/src/job_dispatcher.rs | 7 +- protocols/v2/roles-logic-sv2/src/utils.rs | 32 ++++- protocols/v2/subprotocols/mining/src/lib.rs | 26 +++- roles/mining-proxy/src/lib/upstream_mining.rs | 1 - roles/test-utils/sv1-mining-device/src/job.rs | 1 + roles/translator/src/lib/proxy/bridge.rs | 1 - 10 files changed, 159 insertions(+), 69 deletions(-) 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 d51438687..709800d9b 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 @@ -223,15 +223,20 @@ impl ChannelFactory { downstream_hash_rate: f32, is_header_only: bool, id: u32, + additional_coinbase_script_data: Option<&[u8]>, ) -> Result, Error> { match is_header_only { - true => { - self.new_standard_channel_for_hom_downstream(request_id, downstream_hash_rate, id) - } + true => self.new_standard_channel_for_hom_downstream( + request_id, + downstream_hash_rate, + id, + additional_coinbase_script_data, + ), false => self.new_standard_channel_for_non_hom_downstream( request_id, downstream_hash_rate, id, + additional_coinbase_script_data, ), } } @@ -246,6 +251,7 @@ impl ChannelFactory { request_id: u32, hash_rate: f32, min_extranonce_size: u16, + additional_coinbase_script_data: Option<&[u8]>, ) -> Result>, Error> { let extended_channels_group = 0; let max_extranonce_size = self.extranonces.get_range2_len() as u16; @@ -276,7 +282,10 @@ impl ChannelFactory { .next_extended(max_extranonce_size as usize) .unwrap(); let extranonce_prefix = extranonce - .into_prefix(self.extranonces.get_prefix_len()) + .into_prefix( + self.extranonces.get_prefix_len(), + additional_coinbase_script_data.unwrap_or(&[]), + ) .unwrap(); let success = OpenExtendedMiningChannelSuccess { request_id, @@ -342,6 +351,7 @@ impl ChannelFactory { request_id: u32, downstream_hash_rate: f32, id: u32, + additional_coinbase_script_data: Option<&[u8]>, ) -> Result, Error> { let hom_group_id = 0; let mut result = vec![]; @@ -382,7 +392,11 @@ impl ChannelFactory { group_channel_id: hom_group_id, }, )); - self.prepare_standard_jobs_and_p_hash(&mut result, channel_id)?; + self.prepare_standard_jobs_and_p_hash( + &mut result, + channel_id, + additional_coinbase_script_data, + )?; self.channel_to_group_id.insert(channel_id, hom_group_id); Ok(result) } @@ -394,6 +408,7 @@ impl ChannelFactory { request_id: u32, downstream_hash_rate: f32, group_id: u32, + additional_coinbase_script_data: Option<&[u8]>, ) -> Result, Error> { let mut result = vec![]; let channel_id = self @@ -427,6 +442,14 @@ impl ChannelFactory { self.standard_channels_for_non_hom_downstreams .insert(complete_id, standard_channel); + let extranonce = match additional_coinbase_script_data { + Some(data) => { + let mut data = data.to_vec(); + data.extend_from_slice(extranonce.as_ref()); + extranonce + } + None => extranonce, + }; // First message to be sent is OpenStandardMiningChannelSuccess result.push(Mining::OpenStandardMiningChannelSuccess( OpenStandardMiningChannelSuccess { @@ -448,6 +471,7 @@ impl ChannelFactory { &mut self, result: &mut Vec, channel_id: u32, + additional_coinbase_script_data: Option<&[u8]>, ) -> Result<(), Error> { // Safe cause the function is private and we always add the channel before calling this // funtion @@ -464,9 +488,10 @@ impl ChannelFactory { .map(|j| { extended_to_standard_job( &j.0, - &standard_channel.extranonce.clone().to_vec()[..], + standard_channel.extranonce.as_ref(), standard_channel.channel_id, Some(job_id), + additional_coinbase_script_data, ) }) .collect(); @@ -476,9 +501,10 @@ impl ChannelFactory { Some((j, _)) => Some( extended_to_standard_job( j, - &standard_channel.extranonce.clone().to_vec(), + standard_channel.extranonce.as_ref(), standard_channel.channel_id, Some(self.job_ids.next()), + additional_coinbase_script_data, ) .ok_or(Error::ImpossibleToCalculateMerkleRoot)?, ), @@ -673,11 +699,16 @@ impl ChannelFactory { fn on_new_extended_mining_job( &mut self, m: NewExtendedMiningJob<'static>, + additional_coinbase_script_data: Option<&[u8]>, ) -> Result, BuildNoHashHasher>, Error> { match (m.is_future(), &self.last_prev_hash) { (true, _) => { let mut result = HashMap::with_hasher(BuildNoHashHasher::default()); - self.prepare_jobs_for_downstream_on_new_extended(&mut result, &m)?; + self.prepare_jobs_for_downstream_on_new_extended( + &mut result, + &m, + additional_coinbase_script_data, + )?; 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); @@ -690,7 +721,11 @@ impl ChannelFactory { } (false, Some(_)) => { let mut result = HashMap::with_hasher(BuildNoHashHasher::default()); - self.prepare_jobs_for_downstream_on_new_extended(&mut result, &m)?; + self.prepare_jobs_for_downstream_on_new_extended( + &mut result, + &m, + additional_coinbase_script_data, + )?; // 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() { @@ -718,14 +753,16 @@ impl ChannelFactory { &mut self, result: &mut HashMap>, m: &NewExtendedMiningJob<'static>, + additional_coinbase_script_data: Option<&[u8]>, ) -> 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()[..], + channel.extranonce.as_ref(), *id, Some(job_id), + additional_coinbase_script_data, ) .unwrap(); standard_job.channel_id = *id; @@ -764,6 +801,7 @@ impl ChannelFactory { coinbase_tx_suffix: &[u8], prev_blockhash: hash_types::BlockHash, bits: u32, + additional_coinbase_script_data: Option<&[u8]>, ) -> Result { debug!("Checking target for share {:?}", m); let upstream_target = match &self.kind { @@ -801,6 +839,7 @@ impl ChannelFactory { coinbase_tx_suffix, &extranonce[..], &merkle_path[..], + additional_coinbase_script_data.unwrap_or(&[]), ) .ok_or(Error::InvalidCoinbase)? .try_into() @@ -1024,8 +1063,13 @@ impl PoolChannelFactory { is_header_only: bool, id: u32, ) -> Result, Error> { - self.inner - .add_standard_channel(request_id, downstream_hash_rate, is_header_only, id) + self.inner.add_standard_channel( + request_id, + downstream_hash_rate, + is_header_only, + id, + Some(&self.additional_coinbase_script_data), + ) } /// Calls [`ChannelFactory::new_extended_channel`] pub fn new_extended_channel( @@ -1034,8 +1078,12 @@ impl PoolChannelFactory { hash_rate: f32, min_extranonce_size: u16, ) -> Result>, Error> { - self.inner - .new_extended_channel(request_id, hash_rate, min_extranonce_size) + self.inner.new_extended_channel( + request_id, + hash_rate, + min_extranonce_size, + Some(&self.additional_coinbase_script_data), + ) } /// Called when we want to replicate a channel already opened by another actor. /// is used only in the jd client from the template provider module to mock a pool. @@ -1080,9 +1128,10 @@ impl PoolChannelFactory { m, true, self.pool_coinbase_outputs.clone(), - &self.additional_coinbase_script_data, + self.additional_coinbase_script_data.len() as u8, )?; - self.inner.on_new_extended_mining_job(new_job) + self.inner + .on_new_extended_mining_job(new_job, Some(&self.additional_coinbase_script_data)) } /// Called when a `SubmitSharesStandard` message is received from the downstream. We check the shares /// against the channel's respective target and return `OnNewShare` to let us know if and where the shares should @@ -1126,6 +1175,7 @@ impl PoolChannelFactory { referenced_job.coinbase_tx_suffix.as_ref(), prev_blockhash, bits, + Some(&self.additional_coinbase_script_data), ) } None => { @@ -1157,11 +1207,10 @@ impl PoolChannelFactory { if self.negotiated_jobs.contains_key(&m.channel_id) { let referenced_job = self.negotiated_jobs.get(&m.channel_id).unwrap(); let merkle_path = referenced_job.merkle_path.to_vec(); - let additional_coinbase_script_data = self.additional_coinbase_script_data.clone(); let extended_job = job_creator::extended_job_from_custom_job( referenced_job, - additional_coinbase_script_data.as_ref(), - 32, + self.additional_coinbase_script_data.len() as u8, + self.inner.extranonces.get_len() as u8, ) .unwrap(); let prev_blockhash = crate::utils::u256_to_block_hash(referenced_job.prev_hash.clone()); @@ -1176,6 +1225,7 @@ impl PoolChannelFactory { extended_job.coinbase_tx_suffix.as_ref(), prev_blockhash, bits, + Some(&self.additional_coinbase_script_data), ) } else { let referenced_job = self @@ -1210,6 +1260,7 @@ impl PoolChannelFactory { referenced_job.coinbase_tx_suffix.as_ref(), prev_blockhash, bits, + Some(&self.additional_coinbase_script_data), ) } } @@ -1294,7 +1345,6 @@ pub struct ProxyExtendedChannelFactory { inner: ChannelFactory, job_creator: Option, pool_coinbase_outputs: Option>, - additional_coinbase_script_data: String, // Id assigned to the extended channel by upstream extended_channel_id: u32, } @@ -1308,7 +1358,6 @@ impl ProxyExtendedChannelFactory { share_per_min: f32, kind: ExtendedChannelKind, pool_coinbase_outputs: Option>, - additional_coinbase_script_data: String, extended_channel_id: u32, ) -> Self { match &kind { @@ -1348,7 +1397,6 @@ impl ProxyExtendedChannelFactory { inner, job_creator, pool_coinbase_outputs, - additional_coinbase_script_data, extended_channel_id, } } @@ -1361,7 +1409,7 @@ impl ProxyExtendedChannelFactory { id: u32, ) -> Result, Error> { self.inner - .add_standard_channel(request_id, downstream_hash_rate, id_header_only, id) + .add_standard_channel(request_id, downstream_hash_rate, id_header_only, id, None) } /// Calls [`ChannelFactory::new_extended_channel`] pub fn new_extended_channel( @@ -1371,7 +1419,7 @@ impl ProxyExtendedChannelFactory { min_extranonce_size: u16, ) -> Result, Error> { self.inner - .new_extended_channel(request_id, hash_rate, min_extranonce_size) + .new_extended_channel(request_id, hash_rate, min_extranonce_size, None) } /// Called only when a new prev hash is received by a Template Provider when job declaration is used. /// It matches the message with a `job_id`, creates a new custom job, and calls [`ChannelFactory::on_new_prev_hash`] @@ -1437,12 +1485,7 @@ impl ProxyExtendedChannelFactory { self.job_creator.as_mut(), self.pool_coinbase_outputs.as_mut(), ) { - let new_job = job_creator.on_new_template( - m, - true, - pool_coinbase_outputs.clone(), - self.additional_coinbase_script_data.as_ref(), - )?; + let new_job = job_creator.on_new_template(m, true, pool_coinbase_outputs.clone(), 0)?; let id = new_job.job_id; if !new_job.is_future() && self.inner.last_prev_hash.is_some() { let prev_hash = self.last_prev_hash().unwrap(); @@ -1465,7 +1508,7 @@ impl ProxyExtendedChannelFactory { future_job: m.future_template, }; return Ok(( - self.inner.on_new_extended_mining_job(new_job)?, + self.inner.on_new_extended_mining_job(new_job, None)?, Some(custom_mining_job), id, )); @@ -1474,7 +1517,11 @@ impl ProxyExtendedChannelFactory { .future_templates .insert(new_job.job_id, m.clone()); } - Ok((self.inner.on_new_extended_mining_job(new_job)?, None, id)) + Ok(( + self.inner.on_new_extended_mining_job(new_job, None)?, + None, + id, + )) } else { panic!("Either channel factory has no job creator or pool_coinbase_outputs are not yet set") } @@ -1543,6 +1590,7 @@ impl ProxyExtendedChannelFactory { referenced_job.coinbase_tx_suffix.as_ref(), prev_blockhash, bits, + None, ) } else { let bitcoin_target = [0; 32]; @@ -1569,6 +1617,7 @@ impl ProxyExtendedChannelFactory { referenced_job.coinbase_tx_suffix.as_ref(), prev_blockhash, bits, + None, ) } } @@ -1624,6 +1673,7 @@ impl ProxyExtendedChannelFactory { referenced_job.coinbase_tx_suffix.as_ref(), prev_blockhash, bits, + None, ) } else { let bitcoin_target = [0; 32]; @@ -1650,6 +1700,7 @@ impl ProxyExtendedChannelFactory { referenced_job.coinbase_tx_suffix.as_ref(), prev_blockhash, bits, + None, ) } } @@ -1682,7 +1733,7 @@ impl ProxyExtendedChannelFactory { &mut self, m: NewExtendedMiningJob<'static>, ) -> Result, BuildNoHashHasher>, Error> { - self.inner.on_new_extended_mining_job(m) + self.inner.on_new_extended_mining_job(m, None) } pub fn set_target(&mut self, new_target: &mut Target) { self.inner.kind.set_target(new_target); @@ -1879,7 +1930,7 @@ mod test { share_per_min, channel_kind, vec![out], - additional_coinbase_script_data, + additional_coinbase_script_data.into_bytes(), ); // Build a NewTemplate 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 7b5f0feed..d66a64d69 100644 --- a/protocols/v2/roles-logic-sv2/src/channel_logic/mod.rs +++ b/protocols/v2/roles-logic-sv2/src/channel_logic/mod.rs @@ -7,15 +7,17 @@ use std::convert::TryInto; /// convert extended to standard job by calculating the merkle root pub fn extended_to_standard_job<'a>( extended: &NewExtendedMiningJob, - coinbase_script: &[u8], + extranonce: &[u8], channel_id: u32, job_id: Option, + additional_coinbase_script_data: Option<&[u8]>, ) -> Option> { let merkle_root = crate::utils::merkle_root_from_path( extended.coinbase_tx_prefix.inner_as_ref(), extended.coinbase_tx_suffix.inner_as_ref(), - coinbase_script, + extranonce, &extended.merkle_path.inner_as_ref(), + additional_coinbase_script_data.unwrap_or(&[]), ); Some(NewMiningJob { 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 265cf6d03..21273f6d9 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 @@ -112,6 +112,7 @@ impl GroupChannel { &channel.extranonce.clone().to_vec(), channel.channel_id, None, + None, ) .ok_or(Error::ImpossibleToCalculateMerkleRoot)?; res.push(Mining::NewMiningJob(standard_job)); @@ -123,6 +124,7 @@ impl GroupChannel { &channel.extranonce.clone().to_vec(), channel.channel_id, None, + None, ) .ok_or(Error::ImpossibleToCalculateMerkleRoot)?; @@ -189,6 +191,7 @@ impl GroupChannel { &downstream.extranonce.clone().to_vec(), downstream.channel_id, None, + None, ) .ok_or(Error::ImpossibleToCalculateMerkleRoot) } diff --git a/protocols/v2/roles-logic-sv2/src/job_creator.rs b/protocols/v2/roles-logic-sv2/src/job_creator.rs index a73cdc8db..03ffe3cb1 100644 --- a/protocols/v2/roles-logic-sv2/src/job_creator.rs +++ b/protocols/v2/roles-logic-sv2/src/job_creator.rs @@ -70,7 +70,7 @@ impl JobsCreators { template: &mut NewTemplate, version_rolling_allowed: bool, mut pool_coinbase_outputs: Vec, - additional_coinbase_script_data: &[u8], + additional_coinbase_script_data_len: u8, ) -> Result, Error> { let server_tx_outputs = template.coinbase_tx_outputs.to_vec(); let mut outputs = tx_outputs_to_costum_scripts(&server_tx_outputs); @@ -87,7 +87,7 @@ impl JobsCreators { new_extended_job( template, &mut pool_coinbase_outputs, - additional_coinbase_script_data, + additional_coinbase_script_data_len, next_job_id, version_rolling_allowed, self.extranonce_len, @@ -137,7 +137,7 @@ impl JobsCreators { pub fn extended_job_from_custom_job( referenced_job: &mining_sv2::SetCustomMiningJob, - additional_coinbase_script_data: &[u8], + additional_coinbase_script_data_len: u8, extranonce_len: u8, ) -> Result, Error> { let mut outputs = @@ -158,7 +158,7 @@ pub fn extended_job_from_custom_job( new_extended_job( &mut template, &mut outputs, - additional_coinbase_script_data, + additional_coinbase_script_data_len, 0, true, extranonce_len, @@ -177,7 +177,7 @@ pub fn extended_job_from_custom_job( fn new_extended_job( new_template: &mut NewTemplate, coinbase_outputs: &mut [TxOut], - additional_coinbase_script_data: &[u8], + additional_coinbase_script_data_len: u8, job_id: u32, version_rolling_allowed: bool, extranonce_len: u8, @@ -193,7 +193,7 @@ fn new_extended_job( .map_err(|_| Error::TxVersionTooBig)?; let bip34_bytes = get_bip_34_bytes(new_template, tx_version)?; - let script_prefix_len = bip34_bytes.len() + additional_coinbase_script_data.len(); + let script_prefix_len = bip34_bytes.len(); let coinbase = coinbase( bip34_bytes, @@ -201,7 +201,7 @@ fn new_extended_job( new_template.coinbase_tx_locktime, new_template.coinbase_tx_input_sequence, coinbase_outputs, - additional_coinbase_script_data, + additional_coinbase_script_data_len, extranonce_len, ); @@ -327,7 +327,7 @@ fn coinbase( lock_time: u32, sequence: u32, coinbase_outputs: &[TxOut], - additional_coinbase_script_data: &[u8], + additional_coinbase_script_data_len: u8, extranonce_len: u8, ) -> Transaction { // If script_prefix_len is not 0 we are not in a test enviornment and the coinbase have the 0 @@ -336,7 +336,7 @@ fn coinbase( 0 => Witness::from_vec(vec![]), _ => Witness::from_vec(vec![vec![0; 32]]), }; - bip34_bytes.extend_from_slice(additional_coinbase_script_data); + bip34_bytes.extend_from_slice(&vec![0_u8; additional_coinbase_script_data_len as usize]); bip34_bytes.extend_from_slice(&vec![0; extranonce_len as usize]); let tx_in = TxIn { previous_output: OutPoint::null(), @@ -421,9 +421,9 @@ impl StrippedCoinbaseTx { } /// the coinbase tx prefix is the LE bytes concatenation of the tx version and all - /// of the tx inputs minus the 32 bytes after the bip34 bytes in the script + /// of the tx inputs minus the extranonce bytes after the bip34 bytes in the script /// and the last input's sequence (used as the first entry in the coinbase tx suffix). - /// The last 32 bytes after the bip34 bytes in the script will be used to allow extranonce + /// The last bytes after the bip34 bytes in the script will be used to allow extranonce /// space for the miner. We remove the bip141 marker and flag since it is only used for /// computing the `wtxid` and the legacy `txid` is what is used for computing the merkle root // clippy allow because we dont want to consume self @@ -557,7 +557,7 @@ pub mod tests { let mut jobs_creators = JobsCreators::new(32); let job = jobs_creators - .on_new_template(template.borrow_mut(), false, vec![out], "".to_string()) + .on_new_template(template.borrow_mut(), false, vec![out]) .unwrap(); assert_eq!( @@ -581,8 +581,7 @@ pub mod tests { assert_eq!(jobs_creators.lasts_new_template.len(), 0); - let _ = - jobs_creators.on_new_template(template.borrow_mut(), false, vec![out], "".to_string()); + let _ = jobs_creators.on_new_template(template.borrow_mut(), false, vec![out]); assert_eq!(jobs_creators.lasts_new_template.len(), 1); assert_eq!(jobs_creators.lasts_new_template[0], template); @@ -616,8 +615,7 @@ pub mod tests { let mut jobs_creators = JobsCreators::new(32); //Create a template - let _ = - jobs_creators.on_new_template(template.borrow_mut(), false, vec![out], "".to_string()); + let _ = jobs_creators.on_new_template(template.borrow_mut(), false, vec![out]); let test_id = template.template_id; // Create a SetNewPrevHash with matching template_id @@ -705,7 +703,7 @@ pub mod tests { let extranonce = &[0_u8; 32]; let path: &[binary_sv2::U256] = &[]; let stripped_merkle_root = - merkle_root_from_path(&prefix[..], &suffix[..], extranonce, path).unwrap(); + merkle_root_from_path(&prefix[..], &suffix[..], extranonce, path, &[]).unwrap(); let og_merkle_root = coinbase.txid().to_vec(); assert!( stripped_merkle_root == og_merkle_root, diff --git a/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs b/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs index 5cca4230a..e073d803c 100644 --- a/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs +++ b/protocols/v2/roles-logic-sv2/src/job_dispatcher.rs @@ -18,8 +18,7 @@ use std::{collections::HashMap, convert::TryInto, sync::Arc}; use stratum_common::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. +/// Used to convert an extended mining job to a standard mining job pub fn extended_to_standard_job_for_group_channel<'a>( extended: &NewExtendedMiningJob, extranonce: &[u8], @@ -31,6 +30,7 @@ pub fn extended_to_standard_job_for_group_channel<'a>( extended.coinbase_tx_suffix.inner_as_ref(), extranonce, &extended.merkle_path.inner_as_ref(), + &[], ); Some(NewMiningJob { @@ -322,7 +322,7 @@ mod tests { template.template_id = template.template_id % u64::MAX; template.future_template = true; let extended_mining_job = jobs_creators - .on_new_template(&mut template, false, vec![out], pool_signature) + .on_new_template(&mut template, false, vec![out], pool_signature.len() as u8) .expect("Failed to create new job"); // create GroupChannelJobDispatcher @@ -381,6 +381,7 @@ mod tests { extended_mining_job.coinbase_tx_suffix.inner_as_ref(), extranonce.to_vec().as_slice(), &extended_mining_job.merkle_path.inner_as_ref(), + &[], ) .unwrap(); // Assertions diff --git a/protocols/v2/roles-logic-sv2/src/utils.rs b/protocols/v2/roles-logic-sv2/src/utils.rs index ba2a9aa1c..857023196 100644 --- a/protocols/v2/roles-logic-sv2/src/utils.rs +++ b/protocols/v2/roles-logic-sv2/src/utils.rs @@ -142,10 +142,16 @@ pub fn merkle_root_from_path>( coinbase_tx_suffix: &[u8], extranonce: &[u8], path: &[T], + additional_coinbase_script_data: &[u8], ) -> Option> { - let mut coinbase = - Vec::with_capacity(coinbase_tx_prefix.len() + coinbase_tx_suffix.len() + extranonce.len()); + let mut coinbase = Vec::with_capacity( + coinbase_tx_prefix.len() + + coinbase_tx_suffix.len() + + extranonce.len() + + additional_coinbase_script_data.len(), + ); coinbase.extend_from_slice(coinbase_tx_prefix); + coinbase.extend_from_slice(additional_coinbase_script_data); coinbase.extend_from_slice(extranonce); coinbase.extend_from_slice(coinbase_tx_suffix); let coinbase = match Transaction::deserialize(&coinbase[..]) { @@ -549,6 +555,7 @@ fn test_merkle_root_from_path() { &coinbase_bytes[30..], &coinbase_bytes[20..30], &path, + &[], ) .unwrap(); assert_eq!(expected_root, root); @@ -565,13 +572,20 @@ fn test_merkle_root_from_path() { &coinbase_bytes[30..], &coinbase_bytes[20..30], &path, + &[], ) .unwrap(); assert_eq!(coinbase_id, root); //Target None return path on serialization assert_eq!( - merkle_root_from_path(&coinbase_bytes, &coinbase_bytes, &coinbase_bytes, &path), + merkle_root_from_path( + &coinbase_bytes, + &coinbase_bytes, + &coinbase_bytes, + &path, + &[] + ), None ); } @@ -676,6 +690,7 @@ pub fn get_target( coinbase_tx_suffix, extranonce, &(merkle_path[..]), + &[], ) .unwrap() .try_into() @@ -778,9 +793,14 @@ impl<'a> From> for bitcoin::Block { let id = id.as_ref().to_vec(); path.push(id); } - let merkle_root = - merkle_root_from_path(&coinbase_pre[..], &coinbase_suf[..], &extranonce[..], &path) - .expect("Invalid coinbase"); + let merkle_root = merkle_root_from_path( + &coinbase_pre[..], + &coinbase_suf[..], + &extranonce[..], + &path, + &[], + ) + .expect("Invalid coinbase"); let merkle_root = Hash::from_inner(merkle_root.try_into().unwrap()); let prev_blockhash = u256_to_block_hash(message.prev_hash.into_static()); diff --git a/protocols/v2/subprotocols/mining/src/lib.rs b/protocols/v2/subprotocols/mining/src/lib.rs index e82809a79..78dea6983 100644 --- a/protocols/v2/subprotocols/mining/src/lib.rs +++ b/protocols/v2/subprotocols/mining/src/lib.rs @@ -105,6 +105,7 @@ //! //! This protocol explicitly expects that upstream server software is able to manage the size of //! the hashing space correctly for its clients and can provide new jobs quickly enough. +use alloc::vec::Vec; use binary_sv2::{B032, U256}; use core::{ cmp::{Ord, PartialOrd}, @@ -277,6 +278,12 @@ impl core::convert::TryFrom> for Extranonce { } } +impl AsRef<[u8]> for Extranonce { + fn as_ref(&self) -> &[u8] { + self.extranonce.as_ref() + } +} + impl Extranonce { pub fn new(len: usize) -> Option { if len > MAX_EXTRANONCE_LEN { @@ -311,12 +318,21 @@ impl Extranonce { /// Return only the prefix part of the extranonce /// If the required size is greater than the extranonce len it return None - pub fn into_prefix(&self, prefix_len: usize) -> Option> { + pub fn into_prefix( + &self, + prefix_len: usize, + additional_coinbase_script_data: &[u8], + ) -> Option> { if prefix_len > self.extranonce.len() { None } else { - let mut prefix = self.extranonce.clone(); - prefix.resize(prefix_len, 0); + let mut prefix = Vec::with_capacity(prefix_len + additional_coinbase_script_data.len()); + for i in 0..prefix_len { + prefix.push(self.extranonce[i]); + } + for b in additional_coinbase_script_data { + prefix.push(*b); + } // unwrap is sage as prefix_len can not be greater than 32 cause is not possible to // contruct Extranonce with the inner vecto greater than 32. Some(prefix.try_into().unwrap()) @@ -1101,7 +1117,7 @@ pub mod tests { fn test_extranonce_to_prefix() { let inner = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; let extranone = Extranonce { extranonce: inner }; - let prefix = extranone.into_prefix(4).unwrap(); + let prefix = extranone.into_prefix(4, &[]).unwrap(); assert!(vec![1, 2, 3, 4] == prefix.to_vec()) } @@ -1109,7 +1125,7 @@ pub mod tests { fn test_extranonce_to_prefix_not_greater_than_inner() { let inner = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; let extranone = Extranonce { extranonce: inner }; - let prefix = extranone.into_prefix(20); + let prefix = extranone.into_prefix(20, &[]); assert!(prefix.is_none()) } diff --git a/roles/mining-proxy/src/lib/upstream_mining.rs b/roles/mining-proxy/src/lib/upstream_mining.rs index 5ad012d10..8949b9955 100644 --- a/roles/mining-proxy/src/lib/upstream_mining.rs +++ b/roles/mining-proxy/src/lib/upstream_mining.rs @@ -87,7 +87,6 @@ impl ChannelKind { downstream_share_per_minute, kind, Some(vec![]), - String::from(""), up_id, ); *self = Self::Extended(Some(factory)); diff --git a/roles/test-utils/sv1-mining-device/src/job.rs b/roles/test-utils/sv1-mining-device/src/job.rs index 1d6b3d2bc..73ef57c8d 100644 --- a/roles/test-utils/sv1-mining-device/src/job.rs +++ b/roles/test-utils/sv1-mining-device/src/job.rs @@ -49,6 +49,7 @@ impl Job { &coinbase_tx_suffix, &extranonce, &path, + &[], ) .unwrap(); let merkle_root: [u8; 32] = merkle_root.try_into().unwrap(); diff --git a/roles/translator/src/lib/proxy/bridge.rs b/roles/translator/src/lib/proxy/bridge.rs index 93f4efdeb..dcc585e5f 100644 --- a/roles/translator/src/lib/proxy/bridge.rs +++ b/roles/translator/src/lib/proxy/bridge.rs @@ -101,7 +101,6 @@ impl Bridge { share_per_min, ExtendedChannelKind::Proxy { upstream_target }, None, - String::from(""), up_id, ), future_jobs: vec![],