Skip to content

Commit

Permalink
Update channel factory, coinbase input script handling.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
fi3 committed Nov 13, 2024
1 parent de3f156 commit 1256b6b
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 69 deletions.
119 changes: 85 additions & 34 deletions protocols/v2/roles-logic-sv2/src/channel_logic/channel_factory.rs

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions protocols/v2/roles-logic-sv2/src/channel_logic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>,
additional_coinbase_script_data: Option<&[u8]>,
) -> Option<NewMiningJob<'a>> {
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -123,6 +124,7 @@ impl GroupChannel {
&channel.extranonce.clone().to_vec(),
channel.channel_id,
None,
None,
)
.ok_or(Error::ImpossibleToCalculateMerkleRoot)?;

Expand Down Expand Up @@ -189,6 +191,7 @@ impl GroupChannel {
&downstream.extranonce.clone().to_vec(),
downstream.channel_id,
None,
None,
)
.ok_or(Error::ImpossibleToCalculateMerkleRoot)
}
Expand Down
32 changes: 15 additions & 17 deletions protocols/v2/roles-logic-sv2/src/job_creator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl JobsCreators {
template: &mut NewTemplate,
version_rolling_allowed: bool,
mut pool_coinbase_outputs: Vec<TxOut>,
additional_coinbase_script_data: &[u8],
additional_coinbase_script_data_len: u8,
) -> Result<NewExtendedMiningJob<'static>, Error> {
let server_tx_outputs = template.coinbase_tx_outputs.to_vec();
let mut outputs = tx_outputs_to_costum_scripts(&server_tx_outputs);
Expand All @@ -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,
Expand Down Expand Up @@ -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<NewExtendedMiningJob<'static>, Error> {
let mut outputs =
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -193,15 +193,15 @@ 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,
tx_version,
new_template.coinbase_tx_locktime,
new_template.coinbase_tx_input_sequence,
coinbase_outputs,
additional_coinbase_script_data,
additional_coinbase_script_data_len,
extranonce_len,
);

Expand Down Expand Up @@ -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
Expand All @@ -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(),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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!(
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 4 additions & 3 deletions protocols/v2/roles-logic-sv2/src/job_dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
32 changes: 26 additions & 6 deletions protocols/v2/roles-logic-sv2/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,16 @@ pub fn merkle_root_from_path<T: AsRef<[u8]>>(
coinbase_tx_suffix: &[u8],
extranonce: &[u8],
path: &[T],
additional_coinbase_script_data: &[u8],
) -> Option<Vec<u8>> {
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[..]) {
Expand Down Expand Up @@ -549,6 +555,7 @@ fn test_merkle_root_from_path() {
&coinbase_bytes[30..],
&coinbase_bytes[20..30],
&path,
&[],
)
.unwrap();
assert_eq!(expected_root, root);
Expand All @@ -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
);
}
Expand Down Expand Up @@ -676,6 +690,7 @@ pub fn get_target(
coinbase_tx_suffix,
extranonce,
&(merkle_path[..]),
&[],
)
.unwrap()
.try_into()
Expand Down Expand Up @@ -778,9 +793,14 @@ impl<'a> From<BlockCreator<'a>> 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());
Expand Down
26 changes: 21 additions & 5 deletions protocols/v2/subprotocols/mining/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -277,6 +278,12 @@ impl core::convert::TryFrom<alloc::vec::Vec<u8>> for Extranonce {
}
}

impl AsRef<[u8]> for Extranonce {
fn as_ref(&self) -> &[u8] {
self.extranonce.as_ref()
}
}

impl Extranonce {
pub fn new(len: usize) -> Option<Self> {
if len > MAX_EXTRANONCE_LEN {
Expand Down Expand Up @@ -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<B032<'static>> {
pub fn into_prefix(
&self,
prefix_len: usize,
additional_coinbase_script_data: &[u8],
) -> Option<B032<'static>> {
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())
Expand Down Expand Up @@ -1101,15 +1117,15 @@ 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())
}

#[test]
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())
}

Expand Down
1 change: 0 additions & 1 deletion roles/mining-proxy/src/lib/upstream_mining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ impl ChannelKind {
downstream_share_per_minute,
kind,
Some(vec![]),
String::from(""),
up_id,
);
*self = Self::Extended(Some(factory));
Expand Down
1 change: 1 addition & 0 deletions roles/test-utils/sv1-mining-device/src/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ impl Job {
&coinbase_tx_suffix,
&extranonce,
&path,
&[],
)
.unwrap();
let merkle_root: [u8; 32] = merkle_root.try_into().unwrap();
Expand Down
1 change: 0 additions & 1 deletion roles/translator/src/lib/proxy/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ impl Bridge {
share_per_min,
ExtendedChannelKind::Proxy { upstream_target },
None,
String::from(""),
up_id,
),
future_jobs: vec![],
Expand Down

0 comments on commit 1256b6b

Please sign in to comment.