diff --git a/.codecov.yaml b/.codecov.yaml deleted file mode 100644 index 7226deecd..000000000 --- a/.codecov.yaml +++ /dev/null @@ -1,21 +0,0 @@ -coverage: - status: - project: off - patch: off - -after_success: - - bash <(curl -s https://codecov.io/bash) - # Add retry logic - - | - if [ "$?" != "0" ]; then - retries=0 - until [ "$retries" -ge 5 ] - do - sleep 10 - bash <(curl -s https://codecov.io/bash) - if [ "$?" = "0" ]; then - break - fi - retries=$((retries+1)) - done - fi diff --git a/Cargo.lock b/Cargo.lock index add33154d..26c2ed4df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1215,10 +1215,8 @@ name = "jsonrpc" version = "0.16.0" dependencies = [ "base64", - "minreq", "serde", "serde_json", - "socks", ] [[package]] @@ -1349,17 +1347,6 @@ dependencies = [ "adler", ] -[[package]] -name = "minreq" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "731ff3277257ac76a410e8e2e2465afb7a5e6a1d13bb68d306d97bf96605546c" -dependencies = [ - "log", - "serde", - "serde_json", -] - [[package]] name = "mio" version = "0.8.8" @@ -2176,17 +2163,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "socks" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" -dependencies = [ - "byteorder", - "libc", - "winapi", -] - [[package]] name = "stratum-common" version = "0.1.0" diff --git a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/seq_inner.rs b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/seq_inner.rs index de4ab4a83..aad75a382 100644 --- a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/seq_inner.rs +++ b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/seq_inner.rs @@ -287,6 +287,7 @@ impl<'a, T> std::convert::TryFrom> for Vec { } } +#[cfg(feature = "prop_test")] impl<'a, T> std::convert::TryFrom> for Vec { type Error = &'static str; fn try_from(v: Seq064K<'a, T>) -> Result { diff --git a/protocols/v2/roles-logic-sv2/src/utils.rs b/protocols/v2/roles-logic-sv2/src/utils.rs index 827b26fb1..1f60582bf 100644 --- a/protocols/v2/roles-logic-sv2/src/utils.rs +++ b/protocols/v2/roles-logic-sv2/src/utils.rs @@ -192,6 +192,12 @@ impl TryFrom for Script { fn try_from(value: CoinbaseOutput) -> Result { match value.output_script_type.as_str() { + "TEST" => { + let pub_key_hash = PublicKey::from_str(value.output_script_value.as_str()) + .map_err(|_| Error::InvalidOutputScript)? + .pubkey_hash(); + Ok(Script::new_p2pkh(&pub_key_hash)) + } "P2PK" => { let compressed_pub_key = bip32_extended_to_compressed(value.output_script_value.as_str())?; diff --git a/roles/jd-client/jdc-config-example.toml b/roles/jd-client/jdc-config-example.toml deleted file mode 100644 index 21cffbfc1..000000000 --- a/roles/jd-client/jdc-config-example.toml +++ /dev/null @@ -1,66 +0,0 @@ -# SRI JDC config -downstream_address = "0.0.0.0" -downstream_port = 34265 - -# Version support -max_supported_version = 2 -min_supported_version = 2 - -# Minimum extranonce2 size for downstream -# Max value: 16 (leaves 0 bytes for search space splitting of downstreams) -# Max value for CGminer: 8 -# Min value: 2 -min_extranonce2_size = 8 - -# Withhold -withhold = false - -# Auth keys for open encrypted connection downstream -authority_public_key = "2di19GHYQnAZJmEpoUeP7C3Eg9TCcksHr23rZCC83dvUiZgiDL" -authority_secret_key = "2Z1FZug7mZNyM63ggkm37r4oKQ29khLjAvEx43rGkFN47RcJ2t" -cert_validity_sec = 3600 - -# How many time the JDC try to reinitialize itself after a failure -retry = 10 - -# 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" -# Hosted testnet TP -tp_address = "89.116.25.191:8442" -# Hosted regtest TP -# tp_address = "75.119.150.111:8442" - -# 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 BIP32 extended public key is needed -coinbase_outputs = [ - #{ output_script_type = "P2PK", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, - #{ output_script_type = "P2PKH", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, - #{ output_script_type = "P2SH", output_script_value = "..." }, - #{ output_script_type = "P2WSH", output_script_value = "..." }, - { output_script_type = "P2WPKH", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, - #{ output_script_type = "P2TR", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, -] - -[timeout] -unit = "secs" -value = 1 - -# List of upstreams (JDS) used as backup endpoints -# In case of shares refused by the JDS, the fallback system will propose the same job to the next upstream in this list -[[upstreams]] -authority_pubkey = "2di19GHYQnAZJmEpoUeP7C3Eg9TCcksHr23rZCC83dvUiZgiDL" -pool_address = "127.0.0.1:34254" -jd_address = "127.0.0.1:34264" -# Pool signature (string to be included in coinbase tx) -pool_signature = "Stratum v2 SRI Pool" - -# [[upstreams]] -# authority_pubkey = "2di19GHYQnAZJmEpoUeP7C3Eg9TCcksHr23rZCC83dvUiZgiDL" -# pool_address = "127.0.0.1:34254" -# jd_address = "127.0.0.1:34264" -# Pool signature (string to be included in coinbase tx) -# pool_signature = "Stratum v2 SRI Pool" diff --git a/roles/jd-client/jdc-config.toml b/roles/jd-client/jdc-config.toml deleted file mode 100644 index 21cffbfc1..000000000 --- a/roles/jd-client/jdc-config.toml +++ /dev/null @@ -1,66 +0,0 @@ -# SRI JDC config -downstream_address = "0.0.0.0" -downstream_port = 34265 - -# Version support -max_supported_version = 2 -min_supported_version = 2 - -# Minimum extranonce2 size for downstream -# Max value: 16 (leaves 0 bytes for search space splitting of downstreams) -# Max value for CGminer: 8 -# Min value: 2 -min_extranonce2_size = 8 - -# Withhold -withhold = false - -# Auth keys for open encrypted connection downstream -authority_public_key = "2di19GHYQnAZJmEpoUeP7C3Eg9TCcksHr23rZCC83dvUiZgiDL" -authority_secret_key = "2Z1FZug7mZNyM63ggkm37r4oKQ29khLjAvEx43rGkFN47RcJ2t" -cert_validity_sec = 3600 - -# How many time the JDC try to reinitialize itself after a failure -retry = 10 - -# 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" -# Hosted testnet TP -tp_address = "89.116.25.191:8442" -# Hosted regtest TP -# tp_address = "75.119.150.111:8442" - -# 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 BIP32 extended public key is needed -coinbase_outputs = [ - #{ output_script_type = "P2PK", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, - #{ output_script_type = "P2PKH", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, - #{ output_script_type = "P2SH", output_script_value = "..." }, - #{ output_script_type = "P2WSH", output_script_value = "..." }, - { output_script_type = "P2WPKH", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, - #{ output_script_type = "P2TR", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, -] - -[timeout] -unit = "secs" -value = 1 - -# List of upstreams (JDS) used as backup endpoints -# In case of shares refused by the JDS, the fallback system will propose the same job to the next upstream in this list -[[upstreams]] -authority_pubkey = "2di19GHYQnAZJmEpoUeP7C3Eg9TCcksHr23rZCC83dvUiZgiDL" -pool_address = "127.0.0.1:34254" -jd_address = "127.0.0.1:34264" -# Pool signature (string to be included in coinbase tx) -pool_signature = "Stratum v2 SRI Pool" - -# [[upstreams]] -# authority_pubkey = "2di19GHYQnAZJmEpoUeP7C3Eg9TCcksHr23rZCC83dvUiZgiDL" -# pool_address = "127.0.0.1:34254" -# jd_address = "127.0.0.1:34264" -# Pool signature (string to be included in coinbase tx) -# pool_signature = "Stratum v2 SRI Pool" diff --git a/roles/jd-server/jds-config-example.toml b/roles/jd-server/jds-config-example.toml index 16de658fa..377d50b83 100644 --- a/roles/jd-server/jds-config-example.toml +++ b/roles/jd-server/jds-config-example.toml @@ -2,8 +2,6 @@ authority_public_key = "2di19GHYQnAZJmEpoUeP7C3Eg9TCcksHr23rZCC83dvUiZgiDL" authority_secret_key = "2Z1FZug7mZNyM63ggkm37r4oKQ29khLjAvEx43rGkFN47RcJ2t" 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 ! @@ -17,13 +15,10 @@ coinbase_outputs = [ #{ output_script_type = "P2TR", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, ] -# 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" -# Hosted testnet TP -tp_address = "89.116.25.191:8442" -# Hosted regtest TP -# tp_address = "75.119.150.111:8442" - # SRI Pool JD config listen_jd_address = "127.0.0.1:34264" + +core_rpc_url = "http://127.0.0.1" +core_rpc_port = 18332 +core_rpc_user = "username" +core_rpc_pass = "password" diff --git a/roles/jd-server/jds-config.toml b/roles/jd-server/jds-config.toml index 16de658fa..d844f17b4 100644 --- a/roles/jd-server/jds-config.toml +++ b/roles/jd-server/jds-config.toml @@ -2,28 +2,19 @@ authority_public_key = "2di19GHYQnAZJmEpoUeP7C3Eg9TCcksHr23rZCC83dvUiZgiDL" authority_secret_key = "2Z1FZug7mZNyM63ggkm37r4oKQ29khLjAvEx43rGkFN47RcJ2t" 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 BIP32 extended public key is needed coinbase_outputs = [ - #{ output_script_type = "P2PK", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, - #{ output_script_type = "P2PKH", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, - #{ output_script_type = "P2SH", output_script_value = "..." }, - #{ output_script_type = "P2WSH", output_script_value = "..." }, { output_script_type = "P2WPKH", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, - #{ output_script_type = "P2TR", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, ] -# 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" -# Hosted testnet TP -tp_address = "89.116.25.191:8442" -# Hosted regtest TP -# tp_address = "75.119.150.111:8442" # SRI Pool JD config listen_jd_address = "127.0.0.1:34264" + +core_rpc_url = "http://127.0.0.1" +core_rpc_port = 18332 +core_rpc_user = "username" +core_rpc_pass = "password" diff --git a/roles/jd-server/src/error.rs b/roles/jd-server/src/error.rs index c86774c10..08c34186b 100644 --- a/roles/jd-server/src/error.rs +++ b/roles/jd-server/src/error.rs @@ -42,8 +42,6 @@ impl std::fmt::Display for JdsError { } } -pub type JdsResult = Result; - impl From for JdsError { fn from(e: std::io::Error) -> JdsError { JdsError::Io(e) 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 2c8b6d330..4cef9dd58 100644 --- a/roles/jd-server/src/lib/job_declarator/message_handler.rs +++ b/roles/jd-server/src/lib/job_declarator/message_handler.rs @@ -44,14 +44,10 @@ impl ParseClientJobDeclarationMessages for JobDeclaratorDownstream { message: AllocateMiningJobToken, ) -> Result { let token = self.tokens.next(); - // Token is saved in JobDeclaratorDownstream as u32 self.token_to_job_map.insert(token, None); let message_success = AllocateMiningJobTokenSuccess { request_id: message.request_id, - // From u32 token is transformed into B0255 in - // AllocateMiningJobTokenSuccess message mining_job_token: token.to_le_bytes().to_vec().try_into().unwrap(), - // Mock value of coinbase_max_additional_size. Must be changed coinbase_output_max_additional_size: 100, async_mining_allowed: true, coinbase_output: self.coinbase_output.clone().try_into().unwrap(), @@ -66,15 +62,6 @@ impl ParseClientJobDeclarationMessages for JobDeclaratorDownstream { fn handle_declare_mining_job(&mut self, message: DeclareMiningJob) -> Result { if self.verify_job(&message) { - // gT - //let mut short_hash_list: Vec = Vec::new(); - //let tx_short_hashlist_vec = message.tx_short_hash_list.to_vec(); - //for inner in message.tx_short_hash_list.inner_as_ref( { - // let mut inner_vec: Vec = inner.to_vec(); - // let slice: &mut [u8] = inner_vec.as_mut_slice(); - // let short_hash_id: ShortTxId= slice.try_into().unwrap(); - // short_hash_list.push(short_hash_id); - //}; let short_hash_list: Vec = message .tx_short_hash_list .inner_as_ref() @@ -83,7 +70,7 @@ impl ParseClientJobDeclarationMessages for JobDeclaratorDownstream { .collect(); 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 unidentified_txs: Vec = Vec::new(); let mut identified_txs: Vec<( stratum_common::bitcoin::Txid, @@ -97,11 +84,8 @@ impl ParseClientJobDeclarationMessages for JobDeclaratorDownstream { } } - 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!() - } + // TODO + if !unidentified_txs.is_empty() {} self.identified_txs = Some(identified_txs); self.number_of_unidentified_txs = unidentified_txs.len() as u32; diff --git a/roles/jd-server/src/lib/mempool/mod.rs b/roles/jd-server/src/lib/mempool/mod.rs index 843267d65..de3dd7695 100644 --- a/roles/jd-server/src/lib/mempool/mod.rs +++ b/roles/jd-server/src/lib/mempool/mod.rs @@ -7,29 +7,16 @@ use roles_logic_sv2::utils::Mutex; use rpc_client::{Auth, GetMempoolEntryResult, RpcApi, RpcClient}; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use stratum_common::bitcoin::{self, consensus::Decodable}; +use stratum_common::{ + bitcoin, + bitcoin::{consensus::encode::deserialize, hash_types::Txid, hashes::hex::FromHex}, +}; #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 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); +pub struct Amount(f64); #[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct BlockHash(Hash); @@ -40,50 +27,6 @@ pub struct TransacrtionWithHash { tx: Transaction, } -// TODO if we want to order transactions in memepool by profitability (i.e. fees/weight) we must -// use this function -//fn get_profitability(tx_fee: (Transaction, Amount)) -> usize { -// let tx: Transaction = tx_fee.0; -// let size: usize = Transaction::size(&tx); -// let fee = tx_fee.1 .0; -// fee / size -//} - -// TODO make the function below work with get_profitability -//fn order_mempool_by_fee_over_size(mut vector: Vec<(Transaction, Amount)>) -> Vec { -// vector.sort_by(|a, b| b.get_profitability().cmp(&a.get_profitability())); -// vector -//} - -// TODO! (the following is relative to the crate bitcoincore_rpc) -// in the message GetRawTransactionVerbose we get a an hashmap. I realized later that in the struc GetMempoolEntryResult there -// isn't the transaction itself, but rather the data of relative to ancestors, descendnts, -// replacability, weight and so on. I don't know if there is a rpc request to get all the -// whole transactions in the mempool, no just the IDs. I had a brief look at it, and seems -// that it was't present such a message. In this case, we must ask to the node the -// full transaction data for every transaction id and include the message GetRawTransaction -// (that is already present below as commented). Furthermore, we don't need all the data of -// GetRawMempoolVerbose, the message GetRawMempool is enough. So, summarizing, we must -// 1. check if an rpc request that retrieves the mempool, with all the transactions data -// and not just the Txid + genealogy of txid -// 2. if the message in 1. is not present, we must -// 2.1 change the message from GetRawMempoolVerbose to GetRawMempool -// 2.2 (DONE) uncomment GetRawTransaction message below (maked with a TODO) and make this -// compile -// 3. work on the TODO above, about the function verify_shor_id (this task is already -// assigned to 4ss0. -// 4. (DONE) rebase (assigned to 4ss0) -// 5. the method order_mempool_by_profitability is misleading, because actially it order by -// size. Correct and use the function above order_mempool_by_fee_over_size -// -// SORRY: the code should has beed divided in different files and modules. Sorry for this. -// -// - -// NOTE the transaction in the mempool are -// NOTE oredered as fee/weight in descending order -// NOTE #[derive(Clone, Debug)] pub struct JDsMempool { pub mempool: Vec, @@ -92,10 +35,6 @@ pub struct JDsMempool { } impl JDsMempool { - //TODO write this function that takes a short hash transaction id (the sip hash with 6 bytes - //length) and a mempool in input and returns as output Some(Transaction) if the transaction is - //present in the mempool and None otherwise. - fn get_client(&self) -> RpcClient { let url = self.url.as_str(); RpcClient::new(url, self.auth.clone()).unwrap() @@ -110,22 +49,18 @@ impl JDsMempool { } } - //fn order_mempool_by_profitability(mut self) -> JDsMempool { - // self.mempool - // .sort_by(|a, b| b.tx.weight().cmp(&a.tx.weight())); - // self - //} - pub async fn update_mempool(self_: Arc>) -> Result<(), JdsMempoolError> { let mut mempool_ordered: Vec = Vec::new(); let client = self_.safe_lock(|x| x.get_client()).unwrap(); let new_mempool: Result, JdsMempoolError> = tokio::task::spawn(async move { - let mempool: HashMap = + let mempool: HashMap = client.get_raw_mempool_verbose().unwrap(); for id in mempool.keys() { let tx: Transaction = client.get_raw_transaction(id, None).unwrap(); - mempool_ordered.push(TransacrtionWithHash { id: id.clone(), tx }); + let id = Vec::from_hex(id).expect("Invalid hex string"); + let id: Txid = deserialize(&id).expect("Failed to deserialize txid"); + mempool_ordered.push(TransacrtionWithHash { id, tx }); } if mempool_ordered.is_empty() { Err(JdsMempoolError::EmptyMempool) @@ -154,7 +89,7 @@ impl JDsMempool { ) -> 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); + let 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 { @@ -165,6 +100,7 @@ impl JDsMempool { } } +#[derive(Debug)] pub enum JdsMempoolError { EmptyMempool, } diff --git a/roles/jd-server/src/lib/mempool/rpc_client.rs b/roles/jd-server/src/lib/mempool/rpc_client.rs index a8d6280af..5d1955cbd 100644 --- a/roles/jd-server/src/lib/mempool/rpc_client.rs +++ b/roles/jd-server/src/lib/mempool/rpc_client.rs @@ -1,4 +1,4 @@ -use crate::lib::mempool::{hex_iterator::HexIterator, Amount, BlockHash, Txid}; +use crate::lib::mempool::{hex_iterator::HexIterator, Amount, BlockHash}; use bitcoin::{blockdata::transaction::Transaction, consensus::Decodable}; use hashbrown::hash_map::HashMap; use jsonrpc::{error::Error as JsonRpcError, Client as JosnRpcClient}; @@ -60,13 +60,13 @@ pub trait RpcApi: Sized { /// Get details for the transactions in a memory pool fn get_raw_mempool_verbose( &self, - ) -> Result, BitcoincoreRpcError> { + ) -> Result, BitcoincoreRpcError> { self.call("getrawmempool", &[serde_json::to_value(true).unwrap()]) } fn get_raw_transaction( &self, - txid: &Txid, + txid: &String, block_hash: Option<&BlockHash>, ) -> Result { let mut args = [ @@ -115,18 +115,14 @@ impl RpcApi for RpcClient { let raw_args: Vec<_> = args .iter() .map(|a| { - let json_string = serde_json::to_string(a).map_err(BitcoincoreRpcError::Json)?; - serde_json::value::RawValue::from_string(json_string) - .map_err(BitcoincoreRpcError::Json) // we can't use to_raw_value here due to compat with Rust 1.29 + let json_string = serde_json::to_string(a)?; + serde_json::value::RawValue::from_string(json_string) // we can't use to_raw_value here due to compat with Rust 1.29 }) + .map(|a| a.map_err(BitcoincoreRpcError::Json)) .collect::>>()?; let req = self.client.build_request(cmd, &raw_args); - //if log_enabled!(Debug) { - // debug!(target: "bitcoincore_rpc", "JSON-RPC request: {} {}", cmd, serde_json::Value::from(args)); - //} let resp = self.client.send_request(req).map_err(JsonRpcError::from); - //log_response(cmd, &resp); Ok(resp?.result()?) } } @@ -246,16 +242,16 @@ pub struct GetMempoolEntryResult { pub ancestor_size: u64, /// Hash of serialized transaction, including witness data /// before was pub wtxid: bitcoin::Txid, - pub wtxid: Vec, + pub wtxid: String, //Fee information pub fees: GetMempoolEntryResultFees, /// Unconfirmed transactions used as inputs for this transaction /// before was pub depends: Vec, - pub depends: Vec>, + pub depends: Vec, /// Unconfirmed transactions spending outputs from this transaction /// before was pub spent_by: Vec, #[serde(rename = "spentby")] - pub spent_by: Vec>, + pub spent_by: Vec, /// Whether this transaction could be replaced due to BIP125 (replace-by-fee) #[serde(rename = "bip125-replaceable")] pub bip125_replaceable: bool, diff --git a/roles/jd-server/src/lib/mod.rs b/roles/jd-server/src/lib/mod.rs index 9c920179e..b688e612a 100644 --- a/roles/jd-server/src/lib/mod.rs +++ b/roles/jd-server/src/lib/mod.rs @@ -1,3 +1,2 @@ pub mod job_declarator; pub mod mempool; -pub mod template_receiver; diff --git a/roles/jd-server/src/lib/template_receiver/message_handler.rs b/roles/jd-server/src/lib/template_receiver/message_handler.rs deleted file mode 100644 index f17da0996..000000000 --- a/roles/jd-server/src/lib/template_receiver/message_handler.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::lib::template_receiver::TemplateRx; -use roles_logic_sv2::{ - errors::Error, - handlers::template_distribution::{ParseServerTemplateDistributionMessages, SendTo}, - template_distribution_sv2::*, -}; - -impl ParseServerTemplateDistributionMessages for TemplateRx { - fn handle_new_template(&mut self, _: NewTemplate) -> Result { - Ok(SendTo::None(None)) - } - - fn handle_set_new_prev_hash(&mut self, _: SetNewPrevHash) -> Result { - Ok(SendTo::None(None)) - } - - fn handle_request_tx_data_success( - &mut self, - _m: RequestTransactionDataSuccess, - ) -> Result { - Ok(SendTo::None(None)) - } - - fn handle_request_tx_data_error( - &mut self, - _m: RequestTransactionDataError, - ) -> Result { - Ok(SendTo::None(None)) - } -} diff --git a/roles/jd-server/src/lib/template_receiver/mod.rs b/roles/jd-server/src/lib/template_receiver/mod.rs deleted file mode 100644 index 55da574f9..000000000 --- a/roles/jd-server/src/lib/template_receiver/mod.rs +++ /dev/null @@ -1,114 +0,0 @@ -use crate::{ - error::{JdsError, JdsResult}, - status, EitherFrame, StdFrame, -}; -use async_channel::{Receiver, Sender}; -use codec_sv2::Frame; -use error_handling::handle_result; -use network_helpers::plain_connection_tokio::PlainConnection; -use roles_logic_sv2::{ - handlers::template_distribution::ParseServerTemplateDistributionMessages, - parsers::{PoolMessages as JdsMessages, TemplateDistribution}, - template_distribution_sv2::CoinbaseOutputDataSize, - utils::Mutex, -}; -use std::{convert::TryInto, net::SocketAddr, sync::Arc}; -use tokio::{net::TcpStream, task}; -use tracing::info; - -mod message_handler; -mod setup_connection; -use setup_connection::SetupConnectionHandler; - -pub struct TemplateRx { - receiver: Receiver, - sender: Sender, - status_tx: status::Sender, -} - -impl TemplateRx { - pub async fn connect( - address: SocketAddr, - status_tx: status::Sender, - coinbase_out_len: u32, - ) -> JdsResult<()> { - let stream = TcpStream::connect(address).await?; - info!("Connected to template distribution server at {}", address); - - let (mut receiver, mut sender): (Receiver, Sender) = - PlainConnection::new(stream).await; - - SetupConnectionHandler::setup(&mut receiver, &mut sender, address).await?; - - let self_ = Arc::new(Mutex::new(Self { - receiver, - sender, - status_tx, - })); - let cloned = self_.clone(); - - let c_additional_size = CoinbaseOutputDataSize { - coinbase_output_max_additional_size: coinbase_out_len, - }; - let frame = JdsMessages::TemplateDistribution( - TemplateDistribution::CoinbaseOutputDataSize(c_additional_size), - ) - .try_into()?; - - Self::send(self_.clone(), frame).await?; - - task::spawn(async { Self::start(cloned).await }); - - Ok(()) - } - - pub async fn start(self_: Arc>) { - let (receiver, status_tx) = self_ - .safe_lock(|s| (s.receiver.clone(), s.status_tx.clone())) - .unwrap(); - loop { - let message_from_tp = handle_result!(status_tx, receiver.recv().await); - let mut message_from_tp: StdFrame = handle_result!( - status_tx, - message_from_tp - .try_into() - .map_err(|e| JdsError::Codec(codec_sv2::Error::FramingSv2Error(e))) - ); - let message_type_res = message_from_tp - .get_header() - .ok_or_else(|| JdsError::Custom(String::from("No header set"))); - let message_type = handle_result!(status_tx, message_type_res).msg_type(); - let payload = message_from_tp.payload(); - let msg = handle_result!( - status_tx, - ParseServerTemplateDistributionMessages::handle_message_template_distribution( - self_.clone(), - message_type, - payload, - ) - ); - match msg { - roles_logic_sv2::handlers::SendTo_::RelayNewMessageToRemote(_, m) => match m { - TemplateDistribution::CoinbaseOutputDataSize(_) => todo!(), - TemplateDistribution::NewTemplate(_) => (), - TemplateDistribution::RequestTransactionData(_) => todo!(), - TemplateDistribution::RequestTransactionDataError(_) => todo!(), - TemplateDistribution::RequestTransactionDataSuccess(_) => todo!(), - TemplateDistribution::SetNewPrevHash(_) => (), - TemplateDistribution::SubmitSolution(_) => todo!(), - }, - roles_logic_sv2::handlers::SendTo_::None(None) => (), - _ => todo!(), - } - } - } - - pub async fn send(self_: Arc>, sv2_frame: StdFrame) -> JdsResult<()> { - let either_frame = sv2_frame.into(); - let sender = self_ - .safe_lock(|self_| self_.sender.clone()) - .map_err(|e| JdsError::PoisonLock(e.to_string()))?; - sender.send(either_frame).await?; - Ok(()) - } -} diff --git a/roles/jd-server/src/lib/template_receiver/setup_connection.rs b/roles/jd-server/src/lib/template_receiver/setup_connection.rs deleted file mode 100644 index 14becb49f..000000000 --- a/roles/jd-server/src/lib/template_receiver/setup_connection.rs +++ /dev/null @@ -1,108 +0,0 @@ -use crate::{ - error::{JdsError, JdsResult}, - EitherFrame, StdFrame, -}; -use async_channel::{Receiver, Sender}; -use codec_sv2::Frame; -use roles_logic_sv2::{ - common_messages_sv2::{Protocol, SetupConnection}, - errors::Error, - handlers::common::{ParseUpstreamCommonMessages, SendTo}, - parsers::PoolMessages as JdsMessages, - routing_logic::{CommonRoutingLogic, NoRouting}, - utils::Mutex, -}; -use std::{convert::TryInto, net::SocketAddr, sync::Arc}; -use tracing::{error, info, trace}; - -pub struct SetupConnectionHandler {} - -impl SetupConnectionHandler { - #[allow(clippy::result_large_err)] - fn get_setup_connection_message(address: SocketAddr) -> JdsResult> { - let endpoint_host = address.ip().to_string().into_bytes().try_into()?; - let vendor = String::new().try_into()?; - let hardware_version = String::new().try_into()?; - let firmware = String::new().try_into()?; - let device_id = String::new().try_into()?; - Ok(SetupConnection { - protocol: Protocol::TemplateDistributionProtocol, - min_version: 2, - max_version: 2, - flags: 0b0000_0000_0000_0000_0000_0000_0000_0000, - endpoint_host, - endpoint_port: address.port(), - vendor, - hardware_version, - firmware, - device_id, - }) - } - - pub async fn setup( - receiver: &mut Receiver, - sender: &mut Sender, - address: SocketAddr, - ) -> JdsResult<()> { - let setup_connection = Self::get_setup_connection_message(address)?; - - let sv2_frame: StdFrame = JdsMessages::Common(setup_connection.into()).try_into()?; - let sv2_frame = sv2_frame.into(); - trace!("Sending setup connection message to template distribution server"); - sender.send(sv2_frame).await?; - trace!("Sent setup connection message, waiting for response"); - - let mut incoming: StdFrame = receiver - .recv() - .await? - .try_into() - .map_err(|e| JdsError::Codec(codec_sv2::Error::FramingSv2Error(e)))?; - let message_type = incoming - .get_header() - .ok_or_else(|| JdsError::Custom(String::from("No header set")))? - .msg_type(); - let payload = incoming.payload(); - - trace!( - "Received {} response to setup connection message", - message_type - ); - - ParseUpstreamCommonMessages::handle_message_common( - Arc::new(Mutex::new(SetupConnectionHandler {})), - message_type, - payload, - CommonRoutingLogic::None, - )?; - Ok(()) - } -} - -impl ParseUpstreamCommonMessages for SetupConnectionHandler { - fn handle_setup_connection_success( - &mut self, - _: roles_logic_sv2::common_messages_sv2::SetupConnectionSuccess, - ) -> Result { - info!("Setup template provider connection success!"); - Ok(SendTo::None(None)) - } - - fn handle_setup_connection_error( - &mut self, - _: roles_logic_sv2::common_messages_sv2::SetupConnectionError, - ) -> Result { - error!("Setup template provider connection failed!"); - //return error result - todo!() - } - - fn handle_channel_endpoint_changed( - &mut self, - _: roles_logic_sv2::common_messages_sv2::ChannelEndpointChanged, - ) -> Result { - error!("Channel endpoint changed!"); - Err(Error::UnexpectedMessage( - const_sv2::MESSAGE_TYPE_CHANNEL_ENDPOINT_CHANGED, - )) - } -} diff --git a/roles/jd-server/src/main.rs b/roles/jd-server/src/main.rs index 03c4c375d..227f05ee9 100644 --- a/roles/jd-server/src/main.rs +++ b/roles/jd-server/src/main.rs @@ -22,8 +22,6 @@ mod error; mod lib; mod status; -use lib::template_receiver::TemplateRx; - pub type Message = JdsMessages<'static>; pub type StdFrame = StandardSv2Frame; pub type EitherFrame = StandardEitherFrame; @@ -70,15 +68,15 @@ pub struct CoinbaseOutput { #[derive(Debug, Deserialize, Clone)] pub struct Configuration { - pub listen_address: String, - pub tp_address: String, pub listen_jd_address: String, pub authority_public_key: EncodedEd25519PublicKey, pub authority_secret_key: EncodedEd25519SecretKey, pub cert_validity_sec: u64, pub coinbase_outputs: Vec, - #[cfg(feature = "test_only_allow_unencrypted")] - pub test_only_listen_address_plain: String, + pub core_rpc_url: String, + pub core_rpc_port: u16, + pub core_rpc_user: String, + pub core_rpc_pass: String, } mod args { @@ -143,24 +141,6 @@ mod args { #[tokio::main] async fn main() { tracing_subscriber::fmt::init(); - - // NOTE here insert the address of your desired node - // TODO this should be configurable - let url = "http://127.0.0.1:18443".to_string(); - let username = "username".to_string(); - let password = "password".to_string(); - let mempool = Arc::new(Mutex::new(mempool::JDsMempool::new( - url, username, password, - ))); - let mempool_cloned_ = mempool.clone(); - task::spawn(async move { - loop { - let _ = mempool::JDsMempool::update_mempool(mempool_cloned_.clone()).await; - // TODO this should be configurable by the user - tokio::time::sleep(Duration::from_millis(1000)).await; - } - }); - let args = match args::Args::from_args() { Ok(cfg) => cfg, Err(help) => { @@ -184,28 +164,25 @@ async fn main() { } }; + let url = config.core_rpc_url.clone() + ":" + &config.core_rpc_port.clone().to_string(); + let username = config.core_rpc_user.clone(); + let password = config.core_rpc_pass.clone(); + let mempool = Arc::new(Mutex::new(mempool::JDsMempool::new( + url.clone(), username, password, + ))); + let mempool_cloned_ = mempool.clone(); + if url.contains("http") { + task::spawn(async move { + loop { + let _ = mempool::JDsMempool::update_mempool(mempool_cloned_.clone()).await; + // TODO this should be configurable by the user + tokio::time::sleep(Duration::from_millis(10000)).await; + } + }); + }; + let (status_tx, status_rx) = unbounded(); info!("Jds INITIALIZING with config: {:?}", &args.config_path); - let coinbase_output_result = get_coinbase_output(&config); - let coinbase_output_len = match coinbase_output_result { - Ok(coinbase_output) => coinbase_output.len() as u32, - Err(err) => { - error!("Failed to get coinbase output: {:?}", err); - return; - } - }; - //TODO why the JDS is connecting to the template provider? perhaps this is a residuale code - //from a previuous version - let template_rx_res = TemplateRx::connect( - config.tp_address.parse().unwrap(), - status::Sender::Upstream(status_tx.clone()), - coinbase_output_len, - ) - .await; - if let Err(e) = template_rx_res { - error!("Could not connect to Template Provider: {}", e); - return; - } let cloned = config.clone(); let sender = status::Sender::Downstream(status_tx.clone()); diff --git a/roles/pool/src/main.rs b/roles/pool/src/main.rs index 63ec59d47..62ad5b7a0 100644 --- a/roles/pool/src/main.rs +++ b/roles/pool/src/main.rs @@ -44,7 +44,7 @@ impl TryFrom<&CoinbaseOutput> for CoinbaseOutput_ { fn try_from(pool_output: &CoinbaseOutput) -> Result { match pool_output.output_script_type.as_str() { - "P2PK" | "P2PKH" | "P2WPKH" | "P2SH" | "P2WSH" | "P2TR" => Ok(CoinbaseOutput_ { + "TEST" | "P2PK" | "P2PKH" | "P2WPKH" | "P2SH" | "P2WSH" | "P2TR" => Ok(CoinbaseOutput_ { output_script_type: pool_output.clone().output_script_type, output_script_value: pool_output.clone().output_script_value, }), diff --git a/test/config/interop-jd-change-upstream/jds-config.toml b/test/config/interop-jd-change-upstream/jds-config.toml index 5899a34dc..c556733b9 100644 --- a/test/config/interop-jd-change-upstream/jds-config.toml +++ b/test/config/interop-jd-change-upstream/jds-config.toml @@ -2,14 +2,15 @@ authority_public_key = "2di19GHYQnAZJmEpoUeP7C3Eg9TCcksHr23rZCC83dvUiZgiDL" authority_secret_key = "2Z1FZug7mZNyM63ggkm37r4oKQ29khLjAvEx43rGkFN47RcJ2t" cert_validity_sec = 3600 -test_only_listen_adress_plain = "0.0.0.0:34250" -listen_address = "0.0.0.0:34254" + # list of compressed or uncompressed pubkeys for coinbase payout (only supports 1 item in the array at this point) coinbase_outputs = [ { output_script_type = "P2WPKH", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, ] -tp_address = "75.119.150.111:8442" - -# SRI Pool JD config listen_jd_address = "127.0.0.1:34264" + +core_rpc_url = "" +core_rpc_port = 18332 +core_rpc_user = "" +core_rpc_pass = "" diff --git a/test/config/interop-jd-translator/jds-config.toml b/test/config/interop-jd-translator/jds-config.toml index ab8ed2ac5..c556733b9 100644 --- a/test/config/interop-jd-translator/jds-config.toml +++ b/test/config/interop-jd-translator/jds-config.toml @@ -2,26 +2,15 @@ authority_public_key = "2di19GHYQnAZJmEpoUeP7C3Eg9TCcksHr23rZCC83dvUiZgiDL" authority_secret_key = "2Z1FZug7mZNyM63ggkm37r4oKQ29khLjAvEx43rGkFN47RcJ2t" 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 ! + +# list of compressed or uncompressed pubkeys for coinbase payout (only supports 1 item in the array at this point) coinbase_outputs = [ - #{ output_script_type = "P2PK", output_script_value = "02e13cef1348924c49dd1f708bf38eb79ae4648c16f0301085f37547d1d25e33e0" }, - { output_script_type = "P2WPKH", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, - #{ output_script_type = "P2SH", output_script_value = "..." }, - #{ output_script_type = "P2WSH", output_script_value = "..." }, - #{ output_script_type = "P2WPKH", output_script_value = "02e13cef1348924c49dd1f708bf38eb79ae4648c16f0301085f37547d1d25e33e0" }, - #{ output_script_type = "P2TR", output_script_value = "02e13cef1348924c49dd1f708bf38eb79ae4648c16f0301085f37547d1d25e33e0" }, + { output_script_type = "P2WPKH", output_script_value = "vpub5XzEwP9YWe4cKKZAmbiBUxC7eL5HaZhbquBYzP3vDSDJJegb7CSCRphAPmwpGHzAyH1as9MRnXFWDcZozXA1K3sQqyKdTagooPfCVDhiwnr" }, ] -# Template Provider config -# local TP (this is pointing to localhost so you must run a TP locally for this configuration to work) -tp_address = "75.119.150.111:8442" -# hosted testnet TP -# tp_address = "89.116.25.191:8442" -# hosted regtest TP -# tp_address = "75.119.150.111:8442" - -# SRI Pool JD config listen_jd_address = "127.0.0.1:34264" + +core_rpc_url = "" +core_rpc_port = 18332 +core_rpc_user = "" +core_rpc_pass = "" diff --git a/test/message-generator/test/pool-sri-test-extended_1.json b/test/message-generator/test/pool-sri-test-extended_1.json index c5632c423..4765ba3cd 100644 --- a/test/message-generator/test/pool-sri-test-extended_1.json +++ b/test/message-generator/test/pool-sri-test-extended_1.json @@ -66,11 +66,11 @@ [ [ "coinbase_tx_prefix", - {"B064K": [2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 56, 3, 76, 163, 38, 0, 83, 116, 114, 97, 116, 117, 109, 32, 118, 50, 32, 83, 82, 73, 32, 80, 111, 111, 108]} + {"B064K": [2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 56, 3, 59, 164, 38, 0, 83, 116, 114, 97, 116, 117, 109, 32, 118, 50, 32, 83, 82, 73, 32, 80, 111, 111, 108]} ], [ "coinbase_tx_suffix", - {"B064K": [255, 255, 255, 255, 1, 64, 190, 64, 37, 0, 0, 0, 0, 22, 0, 20, 52, 109, 166, 15, 152, 118, 139, 176, 201, 181, 188, 3, 22, 5, 97, 249, 228, 77, 64, 86, 0, 0, 0, 0]} + {"B064K": [255, 255, 255, 255, 2, 182, 225, 18, 0, 0, 0, 0, 0, 22, 0, 20, 52, 109, 166, 15, 152, 118, 139, 176, 201, 181, 188, 3, 22, 5, 97, 249, 228, 77, 64, 86, 0, 0, 0, 0, 0, 0, 0, 0, 38, 106, 36, 170, 33, 169, 237, 125, 220, 147, 236, 132, 138, 152, 227, 43, 143, 12, 118, 230, 239, 0, 184, 14, 106, 31, 213, 187, 33, 174, 254, 220, 64, 45, 181, 111, 37, 3, 22, 0, 0, 0, 0]} ] ] ] diff --git a/vendored/rust-jsonrpc/.rustfmt.toml b/vendored/rust-jsonrpc/.rustfmt.toml deleted file mode 100644 index da5f6e917..000000000 --- a/vendored/rust-jsonrpc/.rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -use_small_heuristics = "Off" diff --git a/vendored/rust-jsonrpc/Cargo.toml b/vendored/rust-jsonrpc/Cargo.toml index 72dddab98..344bc7e0d 100644 --- a/vendored/rust-jsonrpc/Cargo.toml +++ b/vendored/rust-jsonrpc/Cargo.toml @@ -16,24 +16,12 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -default = [ "simple_http", "simple_tcp" ] +default = [ "simple_http" ] # A bare-minimum HTTP transport. simple_http = [ "base64" ] -# A transport that uses `minreq` as the HTTP client. -minreq_http = [ "base64", "minreq" ] -# Basic transport over a raw TcpListener -simple_tcp = [] -# Basic transport over a raw UnixStream -simple_uds = [] -# Enable Socks5 Proxy in transport -proxy = ["socks"] [dependencies] serde = { version = "*", features = ["derive", "alloc"], default-features = false } serde_json = { version = "1.0", default-features = false, features = ["alloc","raw_value"] } - base64 = { version = "0.13.0", optional = true } -minreq = { version = "2.7.0", features = ["json-using-serde"], optional = true } -socks = { version = "0.3.4", optional = true} - diff --git a/vendored/rust-jsonrpc/clippy.toml b/vendored/rust-jsonrpc/clippy.toml deleted file mode 100644 index 11d46a73f..000000000 --- a/vendored/rust-jsonrpc/clippy.toml +++ /dev/null @@ -1 +0,0 @@ -msrv = "1.48.0" diff --git a/vendored/rust-jsonrpc/contrib/test.sh b/vendored/rust-jsonrpc/contrib/test.sh deleted file mode 100755 index da2a9e420..000000000 --- a/vendored/rust-jsonrpc/contrib/test.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -FEATURES="simple_http simple_tcp simple_uds proxy" - -cargo --version -rustc --version - -# Some tests require certain toolchain types. -NIGHTLY=false -if cargo --version | grep nightly; then - NIGHTLY=true -fi - -# Pin dependencies as required if we are using MSRV toolchain. -if cargo --version | grep "1\.48"; then - # 1.0.157 uses syn 2.0 which requires edition 2021 - cargo update -p serde --precise 1.0.156 -fi - -# Make all cargo invocations verbose -export CARGO_TERM_VERBOSE=true - -# Defaults / sanity checks -cargo build -cargo test - -if [ "$DO_LINT" = true ] -then - cargo clippy --all-features --all-targets -- -D warnings -fi - -if [ "$DO_FEATURE_MATRIX" = true ]; then - cargo build --no-default-features - cargo test --no-default-features - - # All features - cargo build --no-default-features --features="$FEATURES" - cargo test --no-default-features --features="$FEATURES" - - # Single features - for feature in ${FEATURES} - do - cargo test --no-default-features --features="$feature" - done -fi - -# Build the docs if told to (this only works with the nightly toolchain) -if [ "$DO_DOCSRS" = true ]; then - RUSTDOCFLAGS="--cfg docsrs -D warnings -D rustdoc::broken-intra-doc-links" cargo +nightly doc --all-features -fi - -# Build the docs with a stable toolchain, in unison with the DO_DOCSRS command -# above this checks that we feature guarded docs imports correctly. -if [ "$DO_DOCS" = true ]; then - RUSTDOCFLAGS="-D warnings" cargo +stable doc --all-features -fi - -# Run formatter if told to. -if [ "$DO_FMT" = true ]; then - if [ "$NIGHTLY" = false ]; then - echo "DO_FMT requires a nightly toolchain (consider using RUSTUP_TOOLCHAIN)" - exit 1 - fi - rustup component add rustfmt - cargo fmt --check -fi diff --git a/vendored/rust-jsonrpc/fuzz/.gitignore b/vendored/rust-jsonrpc/fuzz/.gitignore deleted file mode 100644 index 572e03bdf..000000000 --- a/vendored/rust-jsonrpc/fuzz/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ - -target -corpus -artifacts diff --git a/vendored/rust-jsonrpc/fuzz/Cargo.toml b/vendored/rust-jsonrpc/fuzz/Cargo.toml deleted file mode 100644 index 759926cb0..000000000 --- a/vendored/rust-jsonrpc/fuzz/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "jsonrpc-fuzz" -edition = "2018" -version = "0.0.1" -authors = ["Generated by fuzz/generate-files.sh"] -publish = false - -[package.metadata] -cargo-fuzz = true - -[dependencies] -honggfuzz = { version = "0.5.55", default-features = false } -jsonrpc = { path = "..", features = ["minreq_http"] } - -[[bin]] -name = "minreq_http" -path = "fuzz_targets/minreq_http.rs" - -[[bin]] -name = "simple_http" -path = "fuzz_targets/simple_http.rs" diff --git a/vendored/rust-jsonrpc/fuzz/cycle.sh b/vendored/rust-jsonrpc/fuzz/cycle.sh deleted file mode 100755 index 294f32b0d..000000000 --- a/vendored/rust-jsonrpc/fuzz/cycle.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -# Continuosly cycle over fuzz targets running each for 1 hour. -# It uses chrt SCHED_IDLE so that other process takes priority. -# -# For hfuzz options see https://github.com/google/honggfuzz/blob/master/docs/USAGE.md - -set -e -REPO_DIR=$(git rev-parse --show-toplevel) -# shellcheck source=./fuzz-util.sh -source "$REPO_DIR/fuzz/fuzz-util.sh" - -while : -do - for targetFile in $(listTargetFiles); do - targetName=$(targetFileToName "$targetFile") - echo "Fuzzing target $targetName ($targetFile)" - - # fuzz for one hour - HFUZZ_RUN_ARGS='--run_time 3600' chrt -i 0 cargo hfuzz run "$targetName" - # minimize the corpus - HFUZZ_RUN_ARGS="-i hfuzz_workspace/$targetName/input/ -P -M" chrt -i 0 cargo hfuzz run "$targetName" - done -done diff --git a/vendored/rust-jsonrpc/fuzz/fuzz-util.sh b/vendored/rust-jsonrpc/fuzz/fuzz-util.sh deleted file mode 100755 index 804e0da92..000000000 --- a/vendored/rust-jsonrpc/fuzz/fuzz-util.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env bash - -REPO_DIR=$(git rev-parse --show-toplevel) - -listTargetFiles() { - pushd "$REPO_DIR/fuzz" > /dev/null || exit 1 - find fuzz_targets/ -type f -name "*.rs" - popd > /dev/null || exit 1 -} - -targetFileToName() { - echo "$1" \ - | sed 's/^fuzz_targets\///' \ - | sed 's/\.rs$//' \ - | sed 's/\//_/g' -} - -targetFileToHFuzzInputArg() { - baseName=$(basename "$1") - dirName="${baseName%.*}" - if [ -d "hfuzz_input/$dirName" ]; then - echo "HFUZZ_INPUT_ARGS=\"-f hfuzz_input/$FILE/input\"" - fi -} - -listTargetNames() { - for target in $(listTargetFiles); do - targetFileToName "$target" - done -} - -# Utility function to avoid CI failures on Windows -checkWindowsFiles() { - incorrectFilenames=$(find . -type f -name "*,*" -o -name "*:*" -o -name "*<*" -o -name "*>*" -o -name "*|*" -o -name "*\?*" -o -name "*\**" -o -name "*\"*" | wc -l) - if [ "$incorrectFilenames" -gt 0 ]; then - echo "Bailing early because there is a Windows-incompatible filename in the tree." - exit 2 - fi -} - -# Checks whether a fuzz case output some report, and dumps it in hex -checkReport() { - reportFile="hfuzz_workspace/$1/HONGGFUZZ.REPORT.TXT" - if [ -f "$reportFile" ]; then - cat "$reportFile" - for CASE in "hfuzz_workspace/$1/SIG"*; do - xxd -p -c10000 < "$CASE" - done - exit 1 - fi -} diff --git a/vendored/rust-jsonrpc/fuzz/fuzz.sh b/vendored/rust-jsonrpc/fuzz/fuzz.sh deleted file mode 100755 index 30e07fa23..000000000 --- a/vendored/rust-jsonrpc/fuzz/fuzz.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -set -ex - -REPO_DIR=$(git rev-parse --show-toplevel) - -# shellcheck source=./fuzz-util.sh -source "$REPO_DIR/fuzz/fuzz-util.sh" - -# Check that input files are correct Windows file names -checkWindowsFiles - -if [ "$1" == "" ]; then - targetFiles="$(listTargetFiles)" -else - targetFiles=fuzz_targets/"$1".rs -fi - -cargo --version -rustc --version - -# Testing -cargo install --force honggfuzz --no-default-features -for targetFile in $targetFiles; do - targetName=$(targetFileToName "$targetFile") - echo "Fuzzing target $targetName ($targetFile)" - if [ -d "hfuzz_input/$targetName" ]; then - HFUZZ_INPUT_ARGS="-f hfuzz_input/$targetName/input\"" - else - HFUZZ_INPUT_ARGS="" - fi - RUSTFLAGS="--cfg=jsonrpc_fuzz" HFUZZ_RUN_ARGS="--run_time 30 --exit_upon_crash -v $HFUZZ_INPUT_ARGS" cargo hfuzz run "$targetName" - - checkReport "$targetName" -done diff --git a/vendored/rust-jsonrpc/fuzz/fuzz_targets/minreq_http.rs b/vendored/rust-jsonrpc/fuzz/fuzz_targets/minreq_http.rs deleted file mode 100644 index 68eee3af1..000000000 --- a/vendored/rust-jsonrpc/fuzz/fuzz_targets/minreq_http.rs +++ /dev/null @@ -1,62 +0,0 @@ -extern crate jsonrpc; - -// Note, tests are empty if "jsonrpc_fuzz" is not set but still show up in output of `cargo test --workspace`. - -#[allow(unused_variables)] // `data` is not used when "jsonrpc_fuzz" is not set. -fn do_test(data: &[u8]) { - #[cfg(jsonrpc_fuzz)] - { - use std::io; - - use jsonrpc::minreq_http::MinreqHttpTransport; - use jsonrpc::minreq_http::FUZZ_TCP_SOCK; - use jsonrpc::Client; - - *FUZZ_TCP_SOCK.lock().unwrap() = Some(io::Cursor::new(data.to_vec())); - - let t = MinreqHttpTransport::builder() - .url("localhost:123") - .expect("parse url") - .basic_auth("".to_string(), None) - .build(); - - let client = Client::with_transport(t); - let request = client.build_request("uptime", &[]); - let _ = client.send_request(request); - } -} - -fn main() { - loop { - honggfuzz::fuzz!(|data| { - do_test(data); - }); - } -} - -#[cfg(test)] -mod tests { - fn extend_vec_from_hex(hex: &str) -> Vec { - let mut out = vec![]; - let mut b = 0; - for (idx, c) in hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'..=b'F' => b |= c - b'A' + 10, - b'a'..=b'f' => b |= c - b'a' + 10, - b'0'..=b'9' => b |= c - b'0', - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - out.push(b); - b = 0; - } - } - out - } - - #[test] - fn duplicate_crash() { - super::do_test(&extend_vec_from_hex("00")); - } -} diff --git a/vendored/rust-jsonrpc/fuzz/fuzz_targets/simple_http.rs b/vendored/rust-jsonrpc/fuzz/fuzz_targets/simple_http.rs deleted file mode 100644 index b27cc6a01..000000000 --- a/vendored/rust-jsonrpc/fuzz/fuzz_targets/simple_http.rs +++ /dev/null @@ -1,62 +0,0 @@ -extern crate jsonrpc; - -// Note, tests are if empty "jsonrpc_fuzz" is not set but still show up in output of `cargo test --workspace`. - -#[allow(unused_variables)] // `data` is not used when "jsonrpc_fuzz" is not set. -fn do_test(data: &[u8]) { - #[cfg(jsonrpc_fuzz)] - { - use std::io; - - use jsonrpc::simple_http::SimpleHttpTransport; - use jsonrpc::simple_http::FUZZ_TCP_SOCK; - use jsonrpc::Client; - - *FUZZ_TCP_SOCK.lock().unwrap() = Some(io::Cursor::new(data.to_vec())); - - let t = SimpleHttpTransport::builder() - .url("localhost:123") - .expect("parse url") - .auth("", None) - .build(); - - let client = Client::with_transport(t); - let request = client.build_request("uptime", &[]); - let _ = client.send_request(request); - } -} - -fn main() { - loop { - honggfuzz::fuzz!(|data| { - do_test(data); - }); - } -} - -#[cfg(test)] -mod tests { - fn extend_vec_from_hex(hex: &str) -> Vec { - let mut out = vec![]; - let mut b = 0; - for (idx, c) in hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'..=b'F' => b |= c - b'A' + 10, - b'a'..=b'f' => b |= c - b'a' + 10, - b'0'..=b'9' => b |= c - b'0', - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - out.push(b); - b = 0; - } - } - out - } - - #[test] - fn duplicate_crash() { - super::do_test(&extend_vec_from_hex("00")); - } -} diff --git a/vendored/rust-jsonrpc/fuzz/generate-files.sh b/vendored/rust-jsonrpc/fuzz/generate-files.sh deleted file mode 100755 index a93b5d05f..000000000 --- a/vendored/rust-jsonrpc/fuzz/generate-files.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env bash - -set -e - -REPO_DIR=$(git rev-parse --show-toplevel) - -# shellcheck source=./fuzz-util.sh -source "$REPO_DIR/fuzz/fuzz-util.sh" - -# 1. Generate fuzz/Cargo.toml -cat > "$REPO_DIR/fuzz/Cargo.toml" <> "$REPO_DIR/fuzz/Cargo.toml" < "$REPO_DIR/.github/workflows/fuzz.yml" <executed_\${{ matrix.fuzz_target }} - - uses: actions/upload-artifact@v2 - with: - name: executed_\${{ matrix.fuzz_target }} - path: executed_\${{ matrix.fuzz_target }} - - verify-execution: - if: \${{ !github.event.act }} - needs: fuzz - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/download-artifact@v2 - - name: Display structure of downloaded files - run: ls -R - - run: find executed_* -type f -exec cat {} + | sort > executed - - run: source ./fuzz/fuzz-util.sh && listTargetNames | sort | diff - executed -EOF diff --git a/vendored/rust-jsonrpc/integration_test/Cargo.toml b/vendored/rust-jsonrpc/integration_test/Cargo.toml deleted file mode 100644 index ab7d3575f..000000000 --- a/vendored/rust-jsonrpc/integration_test/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "integration_test" -version = "0.1.0" -authors = ["Steven Roose , Tobin C. Harding "] -edition = "2018" - -[dependencies] -jsonrpc = { path = "..", features = ["minreq_http"] } -lazy_static = "1.4.0" -log = "0.4" -backtrace = "0.3.50" -serde_json = { version = "1.0", features = ["raw_value"] } diff --git a/vendored/rust-jsonrpc/integration_test/run.sh b/vendored/rust-jsonrpc/integration_test/run.sh deleted file mode 100755 index c894902f9..000000000 --- a/vendored/rust-jsonrpc/integration_test/run.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh - -BITCOIND_PATH="${BITCOIND_PATH:-bitcoind}" -TESTDIR=/tmp/rust_bitcoincore_rpc_test - -rm -rf ${TESTDIR} -mkdir -p ${TESTDIR}/1 ${TESTDIR}/2 - -# To kill any remaining open bitcoind. -killall -9 bitcoind - -${BITCOIND_PATH} -regtest \ - -datadir=${TESTDIR}/1 \ - -port=12348 \ - -server=0 \ - -printtoconsole=0 & -PID1=$! - -# Make sure it's listening on its p2p port. -sleep 3 - -BLOCKFILTERARG="" -if ${BITCOIND_PATH} -version | grep -q "v\(2\|0\.19\|0.2\)"; then - BLOCKFILTERARG="-blockfilterindex=1" -fi - -FALLBACKFEEARG="" -if ${BITCOIND_PATH} -version | grep -q "v\(2\|0\.2\)"; then - FALLBACKFEEARG="-fallbackfee=0.00001000" -fi - -${BITCOIND_PATH} -regtest $BLOCKFILTERARG $FALLBACKFEEARG \ - -datadir=${TESTDIR}/2 \ - -connect=127.0.0.1:12348 \ - -rpcport=12349 \ - -server=1 \ - -txindex=1 \ - -printtoconsole=0 & -PID2=$! - -# Let it connect to the other node. -sleep 5 - -RPC_URL=http://localhost:12349 \ - RPC_COOKIE=${TESTDIR}/2/regtest/.cookie \ - cargo run - -RESULT=$? - -kill -9 $PID1 $PID2 - -exit $RESULT diff --git a/vendored/rust-jsonrpc/integration_test/src/main.rs b/vendored/rust-jsonrpc/integration_test/src/main.rs deleted file mode 100644 index 43e7181f1..000000000 --- a/vendored/rust-jsonrpc/integration_test/src/main.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! # rust-bitcoincore-rpc integration test -//! -//! The test methods are named to mention the methods tested. -//! Individual test methods don't use any methods not tested before or -//! mentioned in the test method name. -//! -//! The goal of this test is not to test the correctness of the server, but -//! to test the serialization of arguments and deserialization of responses. -//! - -#![deny(unused)] -#![allow(deprecated)] - -#[macro_use] -extern crate lazy_static; - -use std::cell::RefCell; -use std::time::Duration; -use std::{fs, mem, panic}; - -use backtrace::Backtrace; - -use jsonrpc::http::minreq_http; -use jsonrpc::{Client, Request}; - -struct StdLogger; - -impl log::Log for StdLogger { - fn enabled(&self, metadata: &log::Metadata) -> bool { - metadata.target().contains("jsonrpc") || metadata.target().contains("bitcoincore_rpc") - } - - fn log(&self, record: &log::Record) { - if self.enabled(record.metadata()) { - println!("[{}][{}]: {}", record.level(), record.metadata().target(), record.args()); - } - } - - fn flush(&self) {} -} - -static LOGGER: StdLogger = StdLogger; - -fn get_rpc_url() -> String { - return std::env::var("RPC_URL").expect("RPC_URL must be set"); -} - -fn get_auth() -> (String, Option) { - if let Ok(cookie) = std::env::var("RPC_COOKIE") { - let contents = - fs::read_to_string(&cookie).expect(&format!("failed to read cookie file: {}", cookie)); - let mut split = contents.split(':'); - let user = split.next().expect("failed to get username from cookie file"); - let pass = split.next().map_or("".to_string(), |s| s.to_string()); - return (user.to_string(), Some(pass)); - } else if let Ok(user) = std::env::var("RPC_USER") { - return (user, std::env::var("RPC_PASS").ok()); - } else { - panic!("Either RPC_COOKIE or RPC_USER + RPC_PASS must be set."); - }; -} - -fn make_client() -> Client { - let (user, pass) = get_auth(); - let tp = minreq_http::Builder::new() - .timeout(Duration::from_secs(1)) - .url(&get_rpc_url()) - .unwrap() - .basic_auth(user, pass) - .build(); - Client::with_transport(tp) -} - -lazy_static! { - static ref CLIENT: Client = make_client(); -} - -thread_local! { - static LAST_PANIC: RefCell> = RefCell::new(None); -} - -/// Here we will collect all the results of the individual tests, preserving ordering. -/// Ideally this would be preset with capacity, but static prevents this. -static mut RESULTS: Vec<(&'static str, bool)> = Vec::new(); - -macro_rules! run_test { - ($method:ident) => { - println!("Running {}...", stringify!($method)); - let result = panic::catch_unwind(|| { - $method(&*CLIENT); - }); - if result.is_err() { - let (msg, bt) = LAST_PANIC.with(|b| b.borrow_mut().take()).unwrap(); - println!("{}", msg); - println!("{:?}", bt); - println!("--"); - } - - unsafe { - RESULTS.push((stringify!($method), result.is_ok())); - } - }; -} - -fn main() { - log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::max())).unwrap(); - - // let default_hook = std::panic::take_hook() - std::panic::set_hook(Box::new(|panic_info| { - let bt = Backtrace::new(); - LAST_PANIC.with(move |b| b.borrow_mut().replace((panic_info.to_string(), bt))); - })); - - run_test!(test_get_network_info); - - // Print results - println!(""); - println!(""); - println!("Summary:"); - let mut error_count = 0; - for (name, success) in mem::replace(unsafe { &mut RESULTS }, Vec::new()).into_iter() { - if !success { - println!(" - {}: FAILED", name); - error_count += 1; - } else { - println!(" - {}: PASSED", name); - } - } - - println!(""); - - if error_count == 0 { - println!("All tests succesful!"); - } else { - println!("{} tests failed", error_count); - std::process::exit(1); - } -} - -fn test_get_network_info(cl: &Client) { - let request = Request { - method: "getnetworkinfo".into(), - params: &[], - id: serde_json::json!(1), - jsonrpc: Some("2.0"), - }; - - let _ = cl.send_request(request).unwrap(); -} diff --git a/vendored/rust-jsonrpc/src/client.rs b/vendored/rust-jsonrpc/src/client.rs index 5e9006027..57c53253d 100644 --- a/vendored/rust-jsonrpc/src/client.rs +++ b/vendored/rust-jsonrpc/src/client.rs @@ -5,17 +5,17 @@ //! Support for connecting to JSONRPC servers over HTTP, sending requests, //! and parsing responses -use std::borrow::Cow; -use std::collections::HashMap; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::sync::atomic; +use std::{ + borrow::Cow, + collections::HashMap, + fmt, + hash::{Hash, Hasher}, + sync::atomic, +}; -use serde_json::value::RawValue; -use serde_json::Value; +use serde_json::{value::RawValue, Value}; -use crate::error::Error; -use crate::{Request, Response}; +use crate::{error::Error, Request, Response}; /// An interface for a transport over which to use the JSONRPC protocol. pub trait Transport: Send + Sync + 'static { @@ -96,8 +96,10 @@ impl Client { } } // Match responses to the requests. - let results = - requests.iter().map(|r| by_id.remove(&HashableValue(Cow::Borrowed(&r.id)))).collect(); + let results = requests + .iter() + .map(|r| by_id.remove(&HashableValue(Cow::Borrowed(&r.id)))) + .collect(); // Since we're also just producing the first duplicate ID, we can also just produce the // first incorrect ID in case there are multiple. @@ -202,10 +204,7 @@ impl<'a> Hash for HashableValue<'a> { mod tests { use super::*; - use std::borrow::Cow; - use std::collections::HashSet; - use std::str::FromStr; - use std::sync; + use std::{borrow::Cow, collections::HashSet, str::FromStr, sync}; struct DummyTransport; impl Transport for DummyTransport { @@ -236,10 +235,12 @@ mod tests { let val = HashableValue(Cow::Owned(Value::from_str("null").unwrap())); let t = HashableValue(Cow::Owned(Value::from_str("true").unwrap())); let f = HashableValue(Cow::Owned(Value::from_str("false").unwrap())); - let ns = - HashableValue(Cow::Owned(Value::from_str("[0, -0, 123.4567, -100000000]").unwrap())); - let m = - HashableValue(Cow::Owned(Value::from_str("{ \"field\": 0, \"field\": -0 }").unwrap())); + let ns = HashableValue(Cow::Owned( + Value::from_str("[0, -0, 123.4567, -100000000]").unwrap(), + )); + let m = HashableValue(Cow::Owned( + Value::from_str("{ \"field\": 0, \"field\": -0 }").unwrap(), + )); let mut coll = HashSet::new(); diff --git a/vendored/rust-jsonrpc/src/error.rs b/vendored/rust-jsonrpc/src/error.rs index 5d276af9e..f56641137 100644 --- a/vendored/rust-jsonrpc/src/error.rs +++ b/vendored/rust-jsonrpc/src/error.rs @@ -190,10 +190,10 @@ pub fn result_to_response( #[cfg(test)] mod tests { - use super::StandardError::{ - InternalError, InvalidParams, InvalidRequest, MethodNotFound, ParseError, + use super::{ + result_to_response, standard_error, + StandardError::{InternalError, InvalidParams, InvalidRequest, MethodNotFound, ParseError}, }; - use super::{result_to_response, standard_error}; use serde_json; #[test] diff --git a/vendored/rust-jsonrpc/src/http/minreq_http.rs b/vendored/rust-jsonrpc/src/http/minreq_http.rs deleted file mode 100644 index 96b5a81c3..000000000 --- a/vendored/rust-jsonrpc/src/http/minreq_http.rs +++ /dev/null @@ -1,292 +0,0 @@ -//! This module implements the [`crate::client::Transport`] trait using [`minreq`] -//! as the underlying HTTP transport. -//! -//! [minreq]: - -#[cfg(jsonrpc_fuzz)] -use std::io::{self, Read, Write}; -#[cfg(jsonrpc_fuzz)] -use std::sync::Mutex; -use std::time::Duration; -use std::{error, fmt}; - -use crate::client::Transport; -use crate::{Request, Response}; - -const DEFAULT_URL: &str = "http://localhost"; -const DEFAULT_PORT: u16 = 8332; // the default RPC port for bitcoind. -#[cfg(not(jsonrpc_fuzz))] -const DEFAULT_TIMEOUT_SECONDS: u64 = 15; -#[cfg(jsonrpc_fuzz)] -const DEFAULT_TIMEOUT_SECONDS: u64 = 1; - -/// An HTTP transport that uses [`minreq`] and is useful for running a bitcoind RPC client. -#[derive(Clone, Debug)] -pub struct MinreqHttpTransport { - /// URL of the RPC server. - url: String, - /// timeout only supports second granularity. - timeout: Duration, - /// The value of the `Authorization` HTTP header, i.e., a base64 encoding of 'user:password'. - basic_auth: Option, -} - -impl Default for MinreqHttpTransport { - fn default() -> Self { - MinreqHttpTransport { - url: format!("{}:{}", DEFAULT_URL, DEFAULT_PORT), - timeout: Duration::from_secs(DEFAULT_TIMEOUT_SECONDS), - basic_auth: None, - } - } -} - -impl MinreqHttpTransport { - /// Constructs a new [`MinreqHttpTransport`] with default parameters. - pub fn new() -> Self { - MinreqHttpTransport::default() - } - - /// Returns a builder for [`MinreqHttpTransport`]. - pub fn builder() -> Builder { - Builder::new() - } - - fn request(&self, req: impl serde::Serialize) -> Result - where - R: for<'a> serde::de::Deserialize<'a>, - { - let req = match &self.basic_auth { - Some(auth) => minreq::Request::new(minreq::Method::Post, &self.url) - .with_timeout(self.timeout.as_secs()) - .with_header("Authorization", auth) - .with_json(&req)?, - None => minreq::Request::new(minreq::Method::Post, &self.url) - .with_timeout(self.timeout.as_secs()) - .with_json(&req)?, - }; - - // Send the request and parse the response. If the response is an error that does not - // contain valid JSON in its body (for instance if the bitcoind HTTP server work queue - // depth is exceeded), return the raw HTTP error so users can match against it. - let resp = req.send()?; - match resp.json() { - Ok(json) => Ok(json), - Err(minreq_err) => { - if resp.status_code != 200 { - Err(Error::Http(HttpError { - status_code: resp.status_code, - body: resp.as_str().unwrap_or("").to_string(), - })) - } else { - Err(Error::Minreq(minreq_err)) - } - } - } - } -} - -impl Transport for MinreqHttpTransport { - fn send_request(&self, req: Request) -> Result { - Ok(self.request(req)?) - } - - fn send_batch(&self, reqs: &[Request]) -> Result, crate::Error> { - Ok(self.request(reqs)?) - } - - fn fmt_target(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.url) - } -} - -/// Builder for simple bitcoind [`MinreqHttpTransport`]. -#[derive(Clone, Debug)] -pub struct Builder { - tp: MinreqHttpTransport, -} - -impl Builder { - /// Constructs a new [`Builder`] with default configuration and the URL to use. - pub fn new() -> Builder { - Builder { - tp: MinreqHttpTransport::new(), - } - } - - /// Sets the timeout after which requests will abort if they aren't finished. - pub fn timeout(mut self, timeout: Duration) -> Self { - self.tp.timeout = timeout; - self - } - - /// Sets the URL of the server to the transport. - pub fn url(mut self, url: &str) -> Result { - self.tp.url = url.to_owned(); - Ok(self) - } - - /// Adds authentication information to the transport. - pub fn basic_auth(mut self, user: String, pass: Option) -> Self { - let mut s = user; - s.push(':'); - if let Some(ref pass) = pass { - s.push_str(pass.as_ref()); - } - self.tp.basic_auth = Some(format!("Basic {}", &base64::encode(s.as_bytes()))); - self - } - - /// Adds authentication information to the transport using a cookie string ('user:pass'). - /// - /// Does no checking on the format of the cookie string, just base64 encodes whatever is passed in. - /// - /// # Examples - /// - /// ```no_run - /// # use jsonrpc::minreq_http::MinreqHttpTransport; - /// # use std::fs::{self, File}; - /// # use std::path::Path; - /// # let cookie_file = Path::new("~/.bitcoind/.cookie"); - /// let mut file = File::open(cookie_file).expect("couldn't open cookie file"); - /// let mut cookie = String::new(); - /// fs::read_to_string(&mut cookie).expect("couldn't read cookie file"); - /// let client = MinreqHttpTransport::builder().cookie_auth(cookie); - /// ``` - pub fn cookie_auth>(mut self, cookie: S) -> Self { - self.tp.basic_auth = Some(format!("Basic {}", &base64::encode(cookie.as_ref().as_bytes()))); - self - } - - /// Builds the final [`MinreqHttpTransport`]. - pub fn build(self) -> MinreqHttpTransport { - self.tp - } -} - -impl Default for Builder { - fn default() -> Self { - Builder::new() - } -} - -/// An HTTP error. -#[derive(Debug)] -pub struct HttpError { - /// Status code of the error response. - pub status_code: i32, - /// Raw body of the error response. - pub body: String, -} - -impl fmt::Display for HttpError { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "status: {}, body: {}", self.status_code, self.body) - } -} - -impl error::Error for HttpError {} - -/// Error that can happen when sending requests. In case of error, a JSON error is returned if the -/// body of the response could be parsed as such. Otherwise, an HTTP error is returned containing -/// the status code and the raw body. -#[non_exhaustive] -#[derive(Debug)] -pub enum Error { - /// JSON parsing error. - Json(serde_json::Error), - /// Minreq error. - Minreq(minreq::Error), - /// HTTP error that does not contain valid JSON as body. - Http(HttpError), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::Json(ref e) => write!(f, "parsing JSON failed: {}", e), - Error::Minreq(ref e) => write!(f, "minreq: {}", e), - Error::Http(ref e) => write!(f, "http ({})", e), - } - } -} - -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - use self::Error::*; - - match *self { - Json(ref e) => Some(e), - Minreq(ref e) => Some(e), - Http(ref e) => Some(e), - } - } -} - -impl From for Error { - fn from(e: serde_json::Error) -> Self { - Error::Json(e) - } -} - -impl From for Error { - fn from(e: minreq::Error) -> Self { - Error::Minreq(e) - } -} - -impl From for crate::Error { - fn from(e: Error) -> crate::Error { - match e { - Error::Json(e) => crate::Error::Json(e), - e => crate::Error::Transport(Box::new(e)), - } - } -} - -/// Global mutex used by the fuzzing harness to inject data into the read end of the TCP stream. -#[cfg(jsonrpc_fuzz)] -pub static FUZZ_TCP_SOCK: Mutex>>> = Mutex::new(None); - -#[cfg(jsonrpc_fuzz)] -#[derive(Clone, Debug)] -struct TcpStream; - -#[cfg(jsonrpc_fuzz)] -mod impls { - use super::*; - - impl Read for TcpStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - match *FUZZ_TCP_SOCK.lock().unwrap() { - Some(ref mut cursor) => io::Read::read(cursor, buf), - None => Ok(0), - } - } - } - impl Write for TcpStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - io::sink().write(buf) - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Client; - - #[test] - fn construct() { - let tp = Builder::new() - .timeout(Duration::from_millis(100)) - .url("http://localhost:22") - .unwrap() - .basic_auth("user".to_string(), None) - .build(); - let _ = Client::with_transport(tp); - } -} diff --git a/vendored/rust-jsonrpc/src/http/mod.rs b/vendored/rust-jsonrpc/src/http/mod.rs index 318d8c29f..5f400e102 100644 --- a/vendored/rust-jsonrpc/src/http/mod.rs +++ b/vendored/rust-jsonrpc/src/http/mod.rs @@ -3,9 +3,6 @@ #[cfg(feature = "simple_http")] pub mod simple_http; -#[cfg(feature = "minreq_http")] -pub mod minreq_http; - /// The default TCP port to use for connections. /// Set to 8332, the default RPC port for bitcoind. pub const DEFAULT_PORT: u16 = 8332; diff --git a/vendored/rust-jsonrpc/src/http/simple_http.rs b/vendored/rust-jsonrpc/src/http/simple_http.rs index 7335726ec..7f0c55cd9 100644 --- a/vendored/rust-jsonrpc/src/http/simple_http.rs +++ b/vendored/rust-jsonrpc/src/http/simple_http.rs @@ -6,19 +6,21 @@ #[cfg(feature = "proxy")] use socks::Socks5Stream; -use std::io::{BufRead, BufReader, Read, Write}; #[cfg(not(jsonrpc_fuzz))] use std::net::TcpStream; -use std::net::{SocketAddr, ToSocketAddrs}; -use std::sync::{Arc, Mutex, MutexGuard}; -use std::time::Duration; -use std::{error, fmt, io, net, num}; +use std::{ + error, fmt, io, + io::{BufRead, BufReader, Read, Write}, + net, + net::{SocketAddr, ToSocketAddrs}, + num, + sync::{Arc, Mutex, MutexGuard}, + time::Duration, +}; -use crate::client::Transport; -use crate::http::DEFAULT_PORT; #[cfg(feature = "proxy")] use crate::http::DEFAULT_PROXY_PORT; -use crate::{Request, Response}; +use crate::{client::Transport, http::DEFAULT_PORT, Request, Response}; /// Absolute maximum content length allowed before cutting off the response. const FINAL_RESP_ALLOC: u64 = 1024 * 1024 * 1024; @@ -198,7 +200,9 @@ impl SimpleHttpTransport { }); } if !header_buf.as_bytes()[..12].is_ascii() { - return Err(Error::HttpResponseNonAsciiHello(header_buf.as_bytes()[..12].to_vec())); + return Err(Error::HttpResponseNonAsciiHello( + header_buf.as_bytes()[..12].to_vec(), + )); } if !header_buf.starts_with("HTTP/1.1 ") { return Err(Error::HttpResponseBadHello { @@ -331,7 +335,10 @@ fn check_url(url: &str) -> Result<(SocketAddr, String), Error> { match addr.next() { Some(a) => Ok((a, path.to_owned())), - None => Err(Error::url(url, "invalid hostname: error extracting socket address")), + None => Err(Error::url( + url, + "invalid hostname: error extracting socket address", + )), } } @@ -345,7 +352,13 @@ impl Transport for SimpleHttpTransport { } fn fmt_target(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "http://{}:{}{}", self.addr.ip(), self.addr.port(), self.path) + write!( + f, + "http://{}:{}{}", + self.addr.ip(), + self.addr.port(), + self.path + ) } } @@ -388,7 +401,10 @@ impl Builder { /// Adds authentication information to the transport using a cookie string ('user:pass'). pub fn cookie_auth>(mut self, cookie: S) -> Self { - self.tp.basic_auth = Some(format!("Basic {}", &base64::encode(cookie.as_ref().as_bytes()))); + self.tp.basic_auth = Some(format!( + "Basic {}", + &base64::encode(cookie.as_ref().as_bytes()) + )); self } @@ -532,7 +548,11 @@ impl fmt::Display for Error { ref actual, ref needed, } => { - write!(f, "HTTP response too short: length {}, needed {}.", actual, needed) + write!( + f, + "HTTP response too short: length {}, needed {}.", + actual, needed + ) } HttpResponseNonAsciiHello(ref bytes) => { write!(f, "HTTP response started with non-ASCII {:?}", bytes) @@ -541,19 +561,32 @@ impl fmt::Display for Error { ref actual, ref expected, } => { - write!(f, "HTTP response started with `{}`; expected `{}`.", actual, expected) + write!( + f, + "HTTP response started with `{}`; expected `{}`.", + actual, expected + ) } HttpResponseBadStatus(ref status, ref err) => { - write!(f, "HTTP response had bad status code `{}`: {}.", status, err) + write!( + f, + "HTTP response had bad status code `{}`: {}.", + status, err + ) } HttpResponseBadContentLength(ref len, ref err) => { - write!(f, "HTTP response had bad content length `{}`: {}.", len, err) + write!( + f, + "HTTP response had bad content length `{}`: {}.", + len, err + ) } - HttpResponseContentLengthTooLarge { - length, - max, - } => { - write!(f, "HTTP response content length {} exceeds our max {}.", length, max) + HttpResponseContentLengthTooLarge { length, max } => { + write!( + f, + "HTTP response content length {} exceeds our max {}.", + length, max + ) } HttpErrorCode(c) => write!(f, "unexpected HTTP code: {}", c), IncompleteResponse { @@ -576,25 +609,15 @@ impl error::Error for Error { use self::Error::*; match *self { - InvalidUrl { - .. - } - | HttpResponseTooShort { - .. - } + InvalidUrl { .. } + | HttpResponseTooShort { .. } | HttpResponseNonAsciiHello(..) - | HttpResponseBadHello { - .. - } + | HttpResponseBadHello { .. } | HttpResponseBadStatus(..) | HttpResponseBadContentLength(..) - | HttpResponseContentLengthTooLarge { - .. - } + | HttpResponseContentLengthTooLarge { .. } | HttpErrorCode(_) - | IncompleteResponse { - .. - } => None, + | IncompleteResponse { .. } => None, SocketError(ref e) => Some(e), Json(ref _e) => todo!(), //Some(e), } @@ -690,11 +713,18 @@ mod tests { let addr: net::SocketAddr = ("localhost", 80).to_socket_addrs().unwrap().next().unwrap(); let tp = Builder::new().url("http://localhost/").unwrap().build(); assert_eq!(tp.addr, addr); - let addr: net::SocketAddr = ("localhost", 443).to_socket_addrs().unwrap().next().unwrap(); + let addr: net::SocketAddr = ("localhost", 443) + .to_socket_addrs() + .unwrap() + .next() + .unwrap(); let tp = Builder::new().url("https://localhost/").unwrap().build(); assert_eq!(tp.addr, addr); - let addr: net::SocketAddr = - ("localhost", super::DEFAULT_PORT).to_socket_addrs().unwrap().next().unwrap(); + let addr: net::SocketAddr = ("localhost", super::DEFAULT_PORT) + .to_socket_addrs() + .unwrap() + .next() + .unwrap(); let tp = Builder::new().url("localhost").unwrap().build(); assert_eq!(tp.addr, addr); @@ -709,13 +739,18 @@ mod tests { ]; for u in &valid_urls { let (addr, path) = check_url(u).unwrap(); - let builder = Builder::new().url(u).unwrap_or_else(|_| panic!("error for: {}", u)); + let builder = Builder::new() + .url(u) + .unwrap_or_else(|_| panic!("error for: {}", u)); assert_eq!(builder.tp.addr, addr); assert_eq!(builder.tp.path, path); assert_eq!(builder.tp.timeout, DEFAULT_TIMEOUT); assert_eq!(builder.tp.basic_auth, None); #[cfg(feature = "proxy")] - assert_eq!(builder.tp.proxy_addr, SocketAddr::from_str("127.0.0.1:9050").unwrap()); + assert_eq!( + builder.tp.proxy_addr, + SocketAddr::from_str("127.0.0.1:9050").unwrap() + ); } let invalid_urls = [ @@ -775,9 +810,11 @@ mod tests { #[test] fn request_to_closed_socket() { use serde_json::{Number, Value}; - use std::net::{Shutdown, TcpListener}; - use std::sync::mpsc; - use std::thread; + use std::{ + net::{Shutdown, TcpListener}, + sync::mpsc, + thread, + }; let (tx, rx) = mpsc::sync_channel(1); @@ -805,7 +842,9 @@ mod tests { stream.write_all(b"HTTP/1.1 200\r\n").unwrap(); stream.write_all(b"Content-Length: ").unwrap(); - stream.write_all(response_str.len().to_string().as_bytes()).unwrap(); + stream + .write_all(response_str.len().to_string().as_bytes()) + .unwrap(); stream.write_all(b"\r\n").unwrap(); stream.write_all(b"\r\n").unwrap(); stream.write_all(response_str.as_bytes()).unwrap(); diff --git a/vendored/rust-jsonrpc/src/lib.rs b/vendored/rust-jsonrpc/src/lib.rs index 73d197261..c3b27245c 100644 --- a/vendored/rust-jsonrpc/src/lib.rs +++ b/vendored/rust-jsonrpc/src/lib.rs @@ -13,14 +13,6 @@ pub extern crate serde; /// Re-export `serde_json` crate. pub extern crate serde_json; -/// Re-export `base64` crate. -#[cfg(feature = "base64")] -pub extern crate base64; - -/// Re-export `minreq` crate if the feature is set. -#[cfg(feature = "minreq")] -pub extern crate minreq; - pub mod client; pub mod error; pub mod http; @@ -28,20 +20,13 @@ pub mod http; #[cfg(feature = "simple_http")] pub use http::simple_http; -#[cfg(feature = "minreq_http")] -pub use http::minreq_http; - -#[cfg(feature = "simple_tcp")] -pub mod simple_tcp; - -#[cfg(all(feature = "simple_uds", not(windows)))] -pub mod simple_uds; - use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; -pub use crate::client::{Client, Transport}; -pub use crate::error::Error; +pub use crate::{ + client::{Client, Transport}, + error::Error, +}; /// Shorthand method to convert an argument into a boxed [`serde_json::value::RawValue`]. /// diff --git a/vendored/rust-jsonrpc/src/simple_tcp.rs b/vendored/rust-jsonrpc/src/simple_tcp.rs deleted file mode 100644 index 8eed9fa70..000000000 --- a/vendored/rust-jsonrpc/src/simple_tcp.rs +++ /dev/null @@ -1,187 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! This module implements a synchronous transport over a raw [`std::net::TcpListener`]. -//! Note that it does not handle TCP over Unix Domain Sockets, see `simple_uds` for this. - -use std::io::{BufRead, BufReader, BufWriter, Write}; -use std::{error, fmt, io, net, time}; - -use crate::client::Transport; -use crate::{Request, Response}; - -#[derive(Debug, Clone)] -/// Simple synchronous TCP transport. -pub struct TcpTransport { - /// The internet socket address to connect to. - pub addr: net::SocketAddr, - /// The read and write timeout to use for this connection. - pub timeout: Option, -} - -impl TcpTransport { - /// Creates a new `TcpTransport` without timeouts. - pub fn new(addr: net::SocketAddr) -> TcpTransport { - TcpTransport { - addr, - timeout: None, - } - } - - fn request(&self, req: impl serde::Serialize) -> Result - where - R: for<'a> serde::de::Deserialize<'a>, - { - let sock = net::TcpStream::connect(self.addr)?; - sock.set_read_timeout(self.timeout)?; - sock.set_write_timeout(self.timeout)?; - - let mut message_writer = BufWriter::new(sock.try_clone().unwrap()); - let message_w = serde_json::to_string(&req).unwrap(); - let message_w = message_w.into_bytes(); - message_writer.write_all(&message_w)?; - message_writer.flush()?; - //serde_json::to_writer(&mut sock, &req)?; - - // NOTE: we don't check the id there, so it *must* be synchronous - // memo reader and writer are changed because the serde import is custom, therefore we do - // bnot have serde::json::to_writer or serde_json::from_reader - - let mut message_reader = BufReader::new(sock); - let mut message_w = String::new(); - let _ = message_reader.read_line(&mut message_w); - - let resp: R = serde_json::Deserializer::from_str(&message_w) - .into_iter() - .next() - .ok_or(Error::Timeout)??; - Ok(resp) - } -} - -impl Transport for TcpTransport { - fn send_request(&self, req: Request) -> Result { - Ok(self.request(req)?) - } - - fn send_batch(&self, reqs: &[Request]) -> Result, crate::Error> { - Ok(self.request(reqs)?) - } - - fn fmt_target(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.addr) - } -} - -/// Error that can occur while using the TCP transport. -#[derive(Debug)] -pub enum Error { - /// An error occurred on the socket layer. - SocketError(io::Error), - /// We didn't receive a complete response till the deadline ran out. - Timeout, - /// JSON parsing error. - Json(serde_json::Error), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - use Error::*; - - match *self { - SocketError(ref e) => write!(f, "couldn't connect to host: {}", e), - Timeout => f.write_str("didn't receive response data in time, timed out."), - Json(ref e) => write!(f, "JSON error: {}", e), - } - } -} - -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - use self::Error::*; - - match *self { - SocketError(ref e) => Some(e), - Timeout => None, - Json(ref _e) => todo!(), //Some(e), - } - } -} - -impl From for Error { - fn from(e: io::Error) -> Self { - Error::SocketError(e) - } -} - -impl From for Error { - fn from(e: serde_json::Error) -> Self { - Error::Json(e) - } -} - -impl From for crate::Error { - fn from(e: Error) -> crate::Error { - match e { - Error::Json(e) => crate::Error::Json(e), - e => crate::Error::Transport(Box::new(e)), - } - } -} - -#[cfg(test)] -mod tests { - use std::{ - io::{Read, Write}, - thread, - }; - - use super::*; - use crate::Client; - - // Test a dummy request / response over a raw TCP transport - #[test] - fn sanity_check_tcp_transport() { - let addr: net::SocketAddr = - net::SocketAddrV4::new(net::Ipv4Addr::new(127, 0, 0, 1), 0).into(); - let server = net::TcpListener::bind(addr).unwrap(); - let addr = server.local_addr().unwrap(); - let dummy_req = Request { - method: "arandommethod", - params: &[], - id: serde_json::Value::Number(4242242.into()), - jsonrpc: Some("2.0"), - }; - let dummy_req_ser = serde_json::to_vec(&dummy_req).unwrap(); - let dummy_resp = Response { - result: None, - error: None, - id: serde_json::Value::Number(4242242.into()), - jsonrpc: Some("2.0".into()), - }; - let dummy_resp_ser = serde_json::to_vec(&dummy_resp).unwrap(); - - let client_thread = thread::spawn(move || { - let transport = TcpTransport { - addr, - timeout: Some(time::Duration::from_secs(5)), - }; - let client = Client::with_transport(transport); - - client.send_request(dummy_req.clone()).unwrap() - }); - - let (mut stream, _) = server.accept().unwrap(); - stream.set_read_timeout(Some(time::Duration::from_secs(5))).unwrap(); - let mut recv_req = vec![0; dummy_req_ser.len()]; - let mut read = 0; - while read < dummy_req_ser.len() { - read += stream.read(&mut recv_req[read..]).unwrap(); - } - assert_eq!(recv_req, dummy_req_ser); - - stream.write_all(&dummy_resp_ser).unwrap(); - stream.flush().unwrap(); - let recv_resp = client_thread.join().unwrap(); //line 184 - assert_eq!(serde_json::to_vec(&recv_resp).unwrap(), dummy_resp_ser); - } -} diff --git a/vendored/rust-jsonrpc/src/simple_uds.rs b/vendored/rust-jsonrpc/src/simple_uds.rs deleted file mode 100644 index a1afdf583..000000000 --- a/vendored/rust-jsonrpc/src/simple_uds.rs +++ /dev/null @@ -1,182 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! This module implements a synchronous transport over a raw [`std::net::TcpListener`]. - -use std::os::unix::net::UnixStream; -use std::{error, fmt, io, path, time}; - -use crate::client::Transport; -use crate::{Request, Response}; - -/// Simple synchronous UDS transport. -#[derive(Debug, Clone)] -pub struct UdsTransport { - /// The path to the Unix Domain Socket. - pub sockpath: path::PathBuf, - /// The read and write timeout to use. - pub timeout: Option, -} - -impl UdsTransport { - /// Creates a new [`UdsTransport`] without timeouts to use. - pub fn new>(sockpath: P) -> UdsTransport { - UdsTransport { - sockpath: sockpath.as_ref().to_path_buf(), - timeout: None, - } - } - - fn request(&self, req: impl serde::Serialize) -> Result - where - R: for<'a> serde::de::Deserialize<'a>, - { - let mut sock = UnixStream::connect(&self.sockpath)?; - sock.set_read_timeout(self.timeout)?; - sock.set_write_timeout(self.timeout)?; - - serde_json::to_writer(&mut sock, &req)?; - - // NOTE: we don't check the id there, so it *must* be synchronous - let resp: R = serde_json::Deserializer::from_reader(&mut sock) - .into_iter() - .next() - .ok_or(Error::Timeout)??; - Ok(resp) - } -} - -impl Transport for UdsTransport { - fn send_request(&self, req: Request) -> Result { - Ok(self.request(req)?) - } - - fn send_batch(&self, reqs: &[Request]) -> Result, crate::error::Error> { - Ok(self.request(reqs)?) - } - - fn fmt_target(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.sockpath.to_string_lossy()) - } -} - -/// Error that can occur while using the UDS transport. -#[derive(Debug)] -pub enum Error { - /// An error occurred on the socket layer. - SocketError(io::Error), - /// We didn't receive a complete response till the deadline ran out. - Timeout, - /// JSON parsing error. - Json(serde_json::Error), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - use Error::*; - - match *self { - SocketError(ref e) => write!(f, "couldn't connect to host: {}", e), - Timeout => f.write_str("didn't receive response data in time, timed out."), - Json(ref e) => write!(f, "JSON error: {}", e), - } - } -} - -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - use self::Error::*; - - match *self { - SocketError(ref e) => Some(e), - Timeout => None, - Json(ref e) => Some(e), - } - } -} - -impl From for Error { - fn from(e: io::Error) -> Self { - Error::SocketError(e) - } -} - -impl From for Error { - fn from(e: serde_json::Error) -> Self { - Error::Json(e) - } -} - -impl From for crate::error::Error { - fn from(e: Error) -> crate::error::Error { - match e { - Error::Json(e) => crate::error::Error::Json(e), - e => crate::error::Error::Transport(Box::new(e)), - } - } -} - -#[cfg(test)] -mod tests { - use std::{ - fs, - io::{Read, Write}, - os::unix::net::UnixListener, - process, thread, - }; - - use super::*; - use crate::Client; - - // Test a dummy request / response over an UDS - #[test] - fn sanity_check_uds_transport() { - let socket_path: path::PathBuf = format!("uds_scratch_{}.socket", process::id()).into(); - // Any leftover? - fs::remove_file(&socket_path).unwrap_or(()); - - let server = UnixListener::bind(&socket_path).unwrap(); - let dummy_req = Request { - method: "getinfo", - params: &[], - id: serde_json::Value::Number(111.into()), - jsonrpc: Some("2.0"), - }; - let dummy_req_ser = serde_json::to_vec(&dummy_req).unwrap(); - let dummy_resp = Response { - result: None, - error: None, - id: serde_json::Value::Number(111.into()), - jsonrpc: Some("2.0".into()), - }; - let dummy_resp_ser = serde_json::to_vec(&dummy_resp).unwrap(); - - let cli_socket_path = socket_path.clone(); - let client_thread = thread::spawn(move || { - let transport = UdsTransport { - sockpath: cli_socket_path, - timeout: Some(time::Duration::from_secs(5)), - }; - let client = Client::with_transport(transport); - - client.send_request(dummy_req.clone()).unwrap() - }); - - let (mut stream, _) = server.accept().unwrap(); - stream.set_read_timeout(Some(time::Duration::from_secs(5))).unwrap(); - let mut recv_req = vec![0; dummy_req_ser.len()]; - let mut read = 0; - while read < dummy_req_ser.len() { - read += stream.read(&mut recv_req[read..]).unwrap(); - } - assert_eq!(recv_req, dummy_req_ser); - - stream.write_all(&dummy_resp_ser).unwrap(); - stream.flush().unwrap(); - let recv_resp = client_thread.join().unwrap(); - assert_eq!(serde_json::to_vec(&recv_resp).unwrap(), dummy_resp_ser); - - // Clean up - drop(server); - fs::remove_file(&socket_path).unwrap(); - } -}