From 8aed444f51a49e03966d2f6ab92b325daff0c9ef Mon Sep 17 00:00:00 2001 From: lorban Date: Wed, 11 Oct 2023 17:46:32 +0200 Subject: [PATCH] improvements for the jd server refactored the function that calculates the short_hash_id of a tyransaction given the txid and a nonce. Refacored the method that takes a mempool and a short tx id and verifies that there is that tx in the mempool. Now gives Some(tx, txid) in positive case, none otherwise. With all this machinery now we can identify the transactions sent in the declared mining job. The identified transaction are stored in a field of JobDeclaratorDownstream. It is also added a field that indicates how many transactions are unidentified. In the case this field is non zero, the JDS must send a ProvideMissingTransactions to the JDC. --- protocols/v2/roles-logic-sv2/src/utils.rs | 88 ++++--------------- .../src/lib/job_declarator/message_handler.rs | 33 ++++--- roles/jd-server/src/lib/job_declarator/mod.rs | 13 ++- roles/jd-server/src/lib/mempool/mod.rs | 66 +++++++------- 4 files changed, 77 insertions(+), 123 deletions(-) diff --git a/protocols/v2/roles-logic-sv2/src/utils.rs b/protocols/v2/roles-logic-sv2/src/utils.rs index dd61609c00..8e0da1e1b3 100644 --- a/protocols/v2/roles-logic-sv2/src/utils.rs +++ b/protocols/v2/roles-logic-sv2/src/utils.rs @@ -659,29 +659,7 @@ pub fn get_target( hash.reverse(); hash } - -//pub fn hash_lists_tuple( -// tx_data: Seq064K<'static, B016M<'static>>, -// tx_short_hash_nonce: u64, -//) -> (Seq064K<'static, ShortTxId<'static>>, U256<'static>) { -// // hash the short hash nonce -// let nonce_hash = sha256::Hash::hash(&tx_short_hash_nonce.to_le_bytes()); -// // take first two integers from the hash -// let k0 = u64::from_le_bytes(nonce_hash[0..8].try_into().unwrap()); -// let k1 = u64::from_le_bytes(nonce_hash[8..16].try_into().unwrap()); -// let mut vec_tx_hashes = vec![]; -// // get every transaction, hash it, remove first two bytes and push the ShortTxId in a vector -// for tx in tx_data.to_vec() { -// let hasher = SipHasher24::new_with_keys(k0, k1); -// let tx_id = Transaction::txid(&(Transaction::deserialize(&tx).unwrap())); -// let tx_hashed = hasher.hash(&tx_id); -// vec_tx_hashes.push(tx_hashed); -// } -// let tx_short_hash_list = short_hash_list_builder(vec_tx_hashes.clone()); -// let tx_hash_list_hash = tx_hash_list_hash_builder(vec_tx_hashes); -// (tx_short_hash_list, tx_hash_list_hash) -//} -//RIFATTA +// RIFATTA seconda volta pub fn hash_lists_tuple( tx_data: Seq064K<'static, B016M<'static>>, tx_short_hash_nonce: u64, @@ -692,45 +670,30 @@ pub fn hash_lists_tuple( let txid = Transaction::txid(&(Transaction::deserialize(&tx).unwrap())); txid_list.push(txid); } - let tx_short_hash_list: Seq064K<'static, ShortTxId> = - Seq064K::from(get_short_hash(txid_list.clone(), tx_short_hash_nonce)); - let tx_hash_list_hash = tx_hash_list_hash_builder_(txid_list); + let mut tx_short_hash_list_: Vec = Vec::new(); + for txid in txid_list.clone() { + tx_short_hash_list_.push(get_short_hash(txid, tx_short_hash_nonce)); + } + let tx_short_hash_list: Seq064K<'static, ShortTxId> = Seq064K::from(tx_short_hash_list_); + let tx_hash_list_hash = tx_hash_list_hash_builder(txid_list); (tx_short_hash_list, tx_hash_list_hash) } -//RIFATTA -fn get_short_hash( - txid_list: Vec, - tx_short_hash_nonce: u64, -) -> Vec> { +//RIFATTA seconda volta +pub fn get_short_hash(txid: bitcoin::Txid, tx_short_hash_nonce: u64) -> ShortTxId<'static> { // hash the short hash nonce let nonce_hash = sha256::Hash::hash(&tx_short_hash_nonce.to_le_bytes()); // take first two integers from the hash let k0 = u64::from_le_bytes(nonce_hash[0..8].try_into().unwrap()); let k1 = u64::from_le_bytes(nonce_hash[8..16].try_into().unwrap()); - let mut tx_short_hash_list: Vec = vec![]; // get every transaction, hash it, remove first two bytes and push the ShortTxId in a vector - for txid in txid_list { - let hasher = SipHasher24::new_with_keys(k0, k1); - let tx_id = Transaction::txid(&(Transaction::deserialize(&tx).unwrap())); - let tx_hashed = hasher.hash(&tx_id); - vec_tx_hashes.push(tx_hashed); - } - let tx_short_hash_list = short_hash_list_builder(vec_tx_hashes.clone()); - let tx_hash_list_hash = tx_hash_list_hash_builder(vec_tx_hashes); - (tx_short_hash_list, tx_hash_list_hash) -} - -fn short_hash_list_builder(vec_tx_hashes: Vec) -> Seq064K<'static, ShortTxId<'static>> { - let mut tx_short_hash_list = vec![]; - for tx_hashed in vec_tx_hashes { - let tx_hashed_bytes: Vec = tx_hashed.to_le_bytes()[2..].to_vec(); - let tx_hashed_bytes: ShortTxId = tx_hashed_bytes.try_into().unwrap(); - tx_short_hash_list.push(tx_hashed_bytes); - } - tx_short_hash_list + let hasher = SipHasher24::new_with_keys(k0, k1); + let tx_hashed = hasher.hash(&txid); + let tx_hashed_bytes: Vec = tx_hashed.to_le_bytes().to_vec().drain(0..2).collect(); + let short_tx_id: ShortTxId = tx_hashed_bytes.try_into().unwrap(); + short_tx_id } // rifatta -fn tx_hash_list_hash_builder_(txid_list: Vec) -> U256<'static> { +fn tx_hash_list_hash_builder(txid_list: Vec) -> U256<'static> { // TODO: understand if this field is redunant and to be deleted since // the full coinbase is known let mut vec_u8 = vec![]; @@ -741,26 +704,7 @@ fn tx_hash_list_hash_builder_(txid_list: Vec) -> U256<'static> { let hash = sha256::Hash::hash(&vec_u8).as_inner().to_owned(); hash.to_vec().try_into().unwrap() } -//fn tx_hash_list_hash_builder(vec_tx_hashes: Vec) -> U256<'static> { -// // TODO: understand if this field is redunant and to be deleted since -// // the full coinbase is known -// let mut vec_u8 = vec![]; -// for txid in vec_tx_hashes { -// vec_u8.extend_from_slice(&txid.to_le_bytes()); -// } -// let hash = sha256::Hash::hash(&vec_u8).as_inner().to_owned(); -// hash.to_vec().try_into().unwrap() -//} - -//fn short_hash_list_builder(vec_tx_hashes: Vec) -> Seq064K<'static, ShortTxId<'static>> { -// let mut tx_short_hash_list = vec![]; -// for tx_hashed in vec_tx_hashes { -// let tx_hashed_bytes: Vec = tx_hashed.to_le_bytes().to_vec().drain(0..2).collect(); -// let tx_hashed_bytes: ShortTxId = tx_hashed_bytes.try_into().unwrap(); -// tx_short_hash_list.push(tx_hashed_bytes); -// } -// Seq064K::from(tx_short_hash_list) -//} + #[cfg(test)] mod tests { #[cfg(feature = "serde")] 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 1f5889b6e7..61486fc4f2 100644 --- a/roles/jd-server/src/lib/job_declarator/message_handler.rs +++ b/roles/jd-server/src/lib/job_declarator/message_handler.rs @@ -14,7 +14,7 @@ use roles_logic_sv2::{ pub type SendTo = SendTo_, ()>; use roles_logic_sv2::errors::Error; -use crate::lib::{job_declarator::signed_token, mempool}; +use crate::lib::job_declarator::signed_token; use super::JobDeclaratorDownstream; @@ -70,20 +70,27 @@ impl ParseClientJobDeclarationMessages for JobDeclaratorDownstream { let nonce = message.tx_short_hash_nonce; let mempool = self.mempool.safe_lock(|x| x.clone()).unwrap(); // TODO perhaps the coinbase does not get included - let mut transactions_in_block: Vec = Vec::new(); - for tx_short_id in short_hash_list.iter() { - for transaction_with_hash in mempool.mempool.clone() { - if mempool::verify_short_id(&transaction_with_hash, tx_short_id.clone(), nonce) - { - transactions_in_block.push(transaction_with_hash.clone()); - } else { - // TODO ask downstream with the message ProvideMissingTransactions - // and add these transactions to the job the client is working onto - todo!() - } + let mut unidentified_txs: Vec = Vec::new(); + let mut identified_txs: Vec<( + stratum_common::bitcoin::Txid, + stratum_common::bitcoin::Transaction, + )> = Vec::new(); + //TODO use references insted cloning!!!! + for tx_short_id in short_hash_list { + match mempool.verify_short_id(tx_short_id.clone(), nonce) { + Some(tx_with_id) => identified_txs.push(tx_with_id.clone()), + None => unidentified_txs.push(tx_short_id), } } - self.declared_job = Some(transactions_in_block); + + if !unidentified_txs.is_empty() { + // TODO ask downstream with the message ProvideMissingTransactions + // and add these transactions to the job the client is working onto + todo!() + } + + self.identified_txs = Some(identified_txs); + self.number_of_unidentified_txs = unidentified_txs.len() as u32; let message_success = DeclareMiningJobSuccess { request_id: message.request_id, new_mining_job_token: signed_token( diff --git a/roles/jd-server/src/lib/job_declarator/mod.rs b/roles/jd-server/src/lib/job_declarator/mod.rs index 19201644b1..a48d9138d5 100644 --- a/roles/jd-server/src/lib/job_declarator/mod.rs +++ b/roles/jd-server/src/lib/job_declarator/mod.rs @@ -22,8 +22,6 @@ use tracing::info; use stratum_common::bitcoin::consensus::Encodable; -use super::mempool::TransacrtionWithHash; - #[derive(Debug)] pub struct JobDeclaratorDownstream { sender: Sender, @@ -37,7 +35,13 @@ pub struct JobDeclaratorDownstream { public_key: EncodedEd25519PublicKey, private_key: EncodedEd25519SecretKey, mempool: Arc>, - declared_job: Option>, + identified_txs: Option< + Vec<( + stratum_common::bitcoin::Txid, + stratum_common::bitcoin::Transaction, + )>, + >, + number_of_unidentified_txs: u32, } impl JobDeclaratorDownstream { @@ -64,7 +68,8 @@ impl JobDeclaratorDownstream { public_key: config.authority_public_key.clone(), private_key: config.authority_secret_key.clone(), mempool, - declared_job: None, + identified_txs: None, + number_of_unidentified_txs: 0, } } diff --git a/roles/jd-server/src/lib/mempool/mod.rs b/roles/jd-server/src/lib/mempool/mod.rs index 9730943b78..843267d65f 100644 --- a/roles/jd-server/src/lib/mempool/mod.rs +++ b/roles/jd-server/src/lib/mempool/mod.rs @@ -3,19 +3,11 @@ pub mod rpc_client; use binary_sv2::ShortTxId; use bitcoin::blockdata::transaction::Transaction; use hashbrown::HashMap; -use stratum_common::bitcoin; -// DO NOT REMOVE THESE COMMENTS -//use bitcoin::hashes::HashEngine as HashEngineTrait; -//use bitcoin::hashes::sha256::HashEngine as HashEngineStruct; -//use bitcoin::hashes::sha256::Hash as HashStruct; -//use bitcoin::hashes::Hash as HashTrait; -//use bitcoin::hashes::sha256::Midstate; +use roles_logic_sv2::utils::Mutex; use rpc_client::{Auth, GetMempoolEntryResult, RpcApi, RpcClient}; use serde::{Deserialize, Serialize}; -//use siphasher::sip::SipHasher24; -//use std::{hash::Hasher, collections::hash_map::DefaultHasher}; -use roles_logic_sv2::utils::Mutex; use std::sync::Arc; +use stratum_common::bitcoin::{self, consensus::Decodable}; #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Hash([u8; 32]); @@ -23,6 +15,19 @@ pub struct Hash([u8; 32]); #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Txid(Hash); +impl Txid { + fn get_inner(self) -> [u8; 32] { + self.0 .0 + } +} + +fn to_btc_txid(value: Txid) -> bitcoin::Txid { + let inner = value.get_inner(); + let inner_: &[u8] = &inner; + let mut inner_mut = &inner_[0..]; + bitcoin::Txid::consensus_decode(&mut inner_mut).unwrap() +} + #[derive(Clone, Deserialize)] pub struct Amount(usize); @@ -141,32 +146,25 @@ impl JDsMempool { Err(a) => Err(a), } } + + pub fn verify_short_id( + &self, + tx_short_id: ShortTxId<'_>, + nonce: u64, + ) -> Option<(bitcoin::Txid, bitcoin::Transaction)> { + let mempool: Vec = self.clone().mempool; + for tx_with_hash in mempool { + let btc_txid = to_btc_txid(tx_with_hash.id); + if roles_logic_sv2::utils::get_short_hash(btc_txid, nonce) == tx_short_id { + return Some((btc_txid, tx_with_hash.tx)); + } else { + continue; + } + } + None + } } pub enum JdsMempoolError { EmptyMempool, } - -pub fn verify_short_id(tx: &TransacrtionWithHash, tx_short_id: ShortTxId<'_>, nonce: u64) -> bool { - //// hash the short hash nonce - ////let mut hasher = DefaultHasher::new(); - ////let nonce_hash = HashEngineStruct::from(&tx_short_hash_nonce.to_le_bytes()); - //let nonce_hash: HashStruct = HashTrait::hash(&tx_short_hash_nonce.to_le_bytes()); - //// take first two integers from the hash - //let k0 = u64::from_le_bytes(nonce_hash[0..8]); - //let k1 = u64::from_le_bytes(nonce_hash[8..16]); - //let hasher = SipHasher24::new_with_keys(k0, k1); - //for transaction_with_hash in self.mempool.iter() { - // let tx_hashed = hasher.hash(&transaction_with_hash.id); - // let tx_hashed_bytes: Vec = transaction_with_hash.id.0.0.to_le_bytes().to_vec().drain(0..2).collect(); - // let short_txid_mempool: ShortTxId = tx_hashed_bytes.try_into().unwrap(); - // if short_txid_mempool == tx_short_id { - // return Some(&transaction_with_hash.tx); - // } else { - // continue; - // } - //} - //// ShortTxId doesn't match, need to ask JD client for this transaction - //None - true -}