diff --git a/clippy-on-all-workspaces.sh b/clippy-on-all-workspaces.sh new file mode 100755 index 0000000000..0853783c03 --- /dev/null +++ b/clippy-on-all-workspaces.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +WORKSPACES="benches/Cargo.toml common/Cargo.toml protocols/Cargo.toml roles/Cargo.toml +utils/Cargo.toml" + +for workspace in $WORKSPACES; do + echo "Executing clippy on: $workspace" + cargo clippy --manifest-path="$workspace" -- -D warnings -A dead-code + if [ $? -ne 0 ]; then + echo "Clippy found some errors in: $workspace" + exit 1 + fi +done + +echo "Clippy success!" + diff --git a/protocols/v2/roles-logic-sv2/src/handlers/job_declaration.rs b/protocols/v2/roles-logic-sv2/src/handlers/job_declaration.rs index 40781e6c00..604f785ac6 100644 --- a/protocols/v2/roles-logic-sv2/src/handlers/job_declaration.rs +++ b/protocols/v2/roles-logic-sv2/src/handlers/job_declaration.rs @@ -158,9 +158,7 @@ where Ok(JobDeclaration::SubmitSolution(message)) => { info!("Received SubmitSolution"); debug!("SubmitSolution: {:?}", message); - self_ - .safe_lock(|x| x.handle_submit_solution(message)) - .map_err(|e| crate::Error::PoisonLock(e.to_string()))? + Self::handle_submit_solution(self_, message) } Ok(_) => todo!(), @@ -184,5 +182,8 @@ where &mut self, message: ProvideMissingTransactionsSuccess, ) -> Result; - fn handle_submit_solution(&mut self, message: SubmitSolutionJd) -> Result; + fn handle_submit_solution( + self_: Arc>, + message: SubmitSolutionJd, + ) -> Result; } diff --git a/roles/jd-server/Cargo.toml b/roles/jd-server/Cargo.toml index 592bd66faf..dc77e3125e 100644 --- a/roles/jd-server/Cargo.toml +++ b/roles/jd-server/Cargo.toml @@ -12,7 +12,6 @@ binary_sv2 = { version = "^0.1.6", path = "../../protocols/v2/binary-sv2/binary- buffer_sv2 = { version = "^0.1.2", path = "../../utils/buffer" } codec_sv2 = { version = "*", path = "../../protocols/v2/codec-sv2", features = ["noise_sv2"] } const_sv2 = { version = "^0.1.2", path = "../../protocols/v2/const-sv2" } -jsonrpc = { version = "^0.16.0", path = "vendored/rust-jsonrpc"} network_helpers = { version = "0.1", path = "../../utils/network-helpers", features = ["with_tokio"] } noise_sv2 = { version = "*", path = "../../protocols/v2/noise-sv2" } rand = "0.8.4" @@ -31,4 +30,9 @@ key-utils = { version = "^1.0.0", path = "../../utils/key-utils" } secp256k1 = { version = "0.27.0", default-features = false, features =["bitcoin_hashes","alloc","rand","rand-std"] } siphasher = "1" hex = "0.4.3" - +#hyper = { version = "0.14.28", default-features = false, features = ["client"]} +base64 = "0.21.5" +hyper = { version = "1.1.0", features = ["full"] } +hyper-util = { version = "0.1", features = ["full"] } +http-body-util = "0.1" +bytes = "1" 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 849e4f8f03..b1efffbd03 100644 --- a/roles/jd-server/src/lib/job_declarator/message_handler.rs +++ b/roles/jd-server/src/lib/job_declarator/message_handler.rs @@ -1,5 +1,6 @@ -use std::{convert::TryInto, io::Cursor}; -use stratum_common::bitcoin::{hashes::Hash, psbt::serialize::Deserialize, Block, Transaction}; +use std::{convert::TryInto, io::Cursor, sync::Arc}; + +use stratum_common::bitcoin::Transaction; use binary_sv2::ShortTxId; use roles_logic_sv2::{ @@ -10,15 +11,14 @@ use roles_logic_sv2::{ ProvideMissingTransactions, ProvideMissingTransactionsSuccess, SubmitSolutionJd, }, parsers::JobDeclaration, - utils::{merkle_root_from_path, u256_to_block_hash}, + utils::Mutex, }; pub type SendTo = SendTo_, ()>; use roles_logic_sv2::{errors::Error, parsers::PoolMessages as AllMessages}; use stratum_common::bitcoin::consensus::Decodable; -use tracing::warn; +//use tracing::warn; use crate::lib::job_declarator::signed_token; -use stratum_common::bitcoin::consensus::encode::serialize; use super::JobDeclaratorDownstream; @@ -180,62 +180,71 @@ impl ParseClientJobDeclarationMessages for JobDeclaratorDownstream { } } - fn handle_submit_solution(&mut self, message: SubmitSolutionJd) -> Result { + fn handle_submit_solution( + _self: Arc>, + message: SubmitSolutionJd<'_>, + ) -> Result { //TODO: implement logic for success or error - let (last_declare, mut tx_list, _) = match self.declared_mining_job.take() { - Some((last_declare, tx_list, _x)) => (last_declare, tx_list, _x), - None => { - warn!("Received solution but no job available"); - return Ok(SendTo::None(None)); - } - }; - let coinbase_pre = last_declare.coinbase_prefix.to_vec(); - let extranonce = message.extranonce.to_vec(); - let coinbase_suf = last_declare.coinbase_suffix.to_vec(); - let mut path: Vec> = vec![]; - for tx in &tx_list { - let id = tx.txid(); - let id = id.as_ref().to_vec(); - path.push(id); - } - let merkle_root = - merkle_root_from_path(&coinbase_pre[..], &coinbase_suf[..], &extranonce[..], &path) - .expect("Invalid coinbase"); - let merkle_root = Hash::from_inner(merkle_root.try_into().unwrap()); - - let prev_blockhash = u256_to_block_hash(message.prev_hash.into_static()); - let header = stratum_common::bitcoin::blockdata::block::BlockHeader { - version: last_declare.version as i32, - prev_blockhash, - merkle_root, - time: message.ntime, - bits: message.nbits, - nonce: message.nonce, - }; - - let coinbase = [coinbase_pre, extranonce, coinbase_suf].concat(); - let coinbase = Transaction::deserialize(&coinbase[..]).unwrap(); - tx_list.insert(0, coinbase); - - let mut block = Block { - header, - txdata: tx_list.clone(), - }; - - block.header.merkle_root = block.compute_merkle_root().unwrap(); - - let serialized_block = serialize(&block); - let hexdata = hex::encode(serialized_block); - - // TODO This line blok everything!! - self.mempool - .safe_lock(|x| { - if let Some(client) = x.get_client() { - client.submit_block(hexdata).unwrap(); - } - }) - .unwrap(); - - Ok(SendTo::None(None)) + //let (last_declare, mut tx_list, _) = match self_.safe_lock(|x| x.declared_mining_job.take()).unwrap() { + // Some((last_declare, tx_list, _x)) => (last_declare, tx_list, _x), + // None => { + // warn!("Received solution but no job available"); + // return Ok(SendTo::None(None)); + // } + //}; + //let coinbase_pre = last_declare.coinbase_prefix.to_vec(); + //let extranonce = message.extranonce.to_vec(); + //let coinbase_suf = last_declare.coinbase_suffix.to_vec(); + //let mut path: Vec> = vec![]; + //for tx in &tx_list { + // let id = tx.txid(); + // let id = id.as_ref().to_vec(); + // path.push(id); + //} + //let merkle_root = + // merkle_root_from_path(&coinbase_pre[..], &coinbase_suf[..], &extranonce[..], &path) + // .expect("Invalid coinbase"); + //let merkle_root = Hash::from_inner(merkle_root.try_into().unwrap()); + + //let prev_blockhash = u256_to_block_hash(message.prev_hash.into_static()); + //let header = stratum_common::bitcoin::blockdata::block::BlockHeader { + // version: last_declare.version as i32, + // prev_blockhash, + // merkle_root, + // time: message.ntime, + // bits: message.nbits, + // nonce: message.nonce, + //}; + + //let coinbase = [coinbase_pre, extranonce, coinbase_suf].concat(); + //let coinbase = Transaction::deserialize(&coinbase[..]).unwrap(); + //tx_list.insert(0, coinbase); + + //let mut block = Block { + // header, + // txdata: tx_list.clone(), + //}; + + //block.header.merkle_root = block.compute_merkle_root().unwrap(); + + //let serialized_block = serialize(&block); + //let hexdata = hex::encode(serialized_block); + + //// TODO This line blok everything!! + //let client = self_.safe_lock(|y| + // y + // .mempool + // .safe_lock(|x| { + // if let Some(client) = x.get_client() { + // client//.submit_block(hexdata).await; + // } else { + // todo!() + // } + // }) + // .unwrap()).unwrap(); + //client.submit_block(hexdata).await; + let m = JobDeclaration::SubmitSolution(message.clone().into_static()); + + Ok(SendTo::RelayNewMessage(m)) } } diff --git a/roles/jd-server/src/lib/job_declarator/mod.rs b/roles/jd-server/src/lib/job_declarator/mod.rs index c041d2134b..b46920f6d8 100644 --- a/roles/jd-server/src/lib/job_declarator/mod.rs +++ b/roles/jd-server/src/lib/job_declarator/mod.rs @@ -13,15 +13,20 @@ use roles_logic_sv2::{ common_messages_sv2::SetupConnectionSuccess, handlers::job_declaration::{ParseClientJobDeclarationMessages, SendTo}, job_declaration_sv2::DeclareMiningJob, - parsers::PoolMessages as JdsMessages, - utils::{Id, Mutex}, + parsers::{JobDeclaration, PoolMessages as JdsMessages}, + utils::{merkle_root_from_path, u256_to_block_hash, Id, Mutex}, }; use secp256k1::{KeyPair, Message as SecpMessage, Secp256k1}; use std::{collections::HashMap, convert::TryInto, sync::Arc}; use tokio::net::TcpListener; use tracing::{error, info}; -use stratum_common::bitcoin::{consensus::Encodable, Transaction}; +use stratum_common::bitcoin::{ + consensus::{encode::serialize, Encodable}, + hashes::Hash, + psbt::serialize::Deserialize, + Block, Transaction, +}; #[derive(Debug)] pub struct JobDeclaratorDownstream { @@ -78,7 +83,11 @@ impl JobDeclaratorDownstream { sender.send(sv2_frame.into()).await.map_err(|_| ())?; Ok(()) } - pub fn start(self_mutex: Arc>, tx_status: status::Sender) { + pub fn start( + self_mutex: Arc>, + tx_status: status::Sender, + submit_solution_sender: Sender, + ) { let recv = self_mutex.safe_lock(|s| s.receiver.clone()).unwrap(); tokio::spawn(async move { loop { @@ -102,6 +111,68 @@ impl JobDeclaratorDownstream { Self::send(self_mutex.clone(), message).await.unwrap(); } Ok(SendTo::None(_)) => (), + Ok(SendTo::RelayNewMessage(JobDeclaration::SubmitSolution( + message, + ))) => { + //TODO: implement logic for success or error + let (last_declare, mut tx_list, _) = match self_mutex + .safe_lock(|x| x.declared_mining_job.take()) + .unwrap() + { + Some((last_declare, tx_list, _x)) => { + (last_declare, tx_list, _x) + } + None => { + //warn!("Received solution but no job available"); + todo!() + } + }; + let coinbase_pre = last_declare.coinbase_prefix.to_vec(); + let extranonce = message.extranonce.to_vec(); + let coinbase_suf = last_declare.coinbase_suffix.to_vec(); + let mut path: Vec> = vec![]; + for tx in &tx_list { + let id = tx.txid(); + let id = id.as_ref().to_vec(); + path.push(id); + } + let merkle_root = merkle_root_from_path( + &coinbase_pre[..], + &coinbase_suf[..], + &extranonce[..], + &path, + ) + .expect("Invalid coinbase"); + let merkle_root = Hash::from_inner(merkle_root.try_into().unwrap()); + + let prev_blockhash = + u256_to_block_hash(message.prev_hash.into_static()); + let header = + stratum_common::bitcoin::blockdata::block::BlockHeader { + version: last_declare.version as i32, + prev_blockhash, + merkle_root, + time: message.ntime, + bits: message.nbits, + nonce: message.nonce, + }; + + let coinbase = [coinbase_pre, extranonce, coinbase_suf].concat(); + let coinbase = Transaction::deserialize(&coinbase[..]).unwrap(); + tx_list.insert(0, coinbase); + + let mut block = Block { + header, + txdata: tx_list.clone(), + }; + + block.header.merkle_root = block.compute_merkle_root().unwrap(); + + let serialized_block = serialize(&block); + let hexdata = hex::encode(serialized_block); + + let _ = submit_solution_sender.send(hexdata).await; + } Err(e) => { error!("{:?}", e); handle_result!( @@ -155,16 +226,18 @@ impl JobDeclarator { config: Configuration, status_tx: crate::status::Sender, mempool: Arc>, + sender: Sender, ) { let self_ = Arc::new(Mutex::new(Self {})); info!("JD INITIALIZED"); - Self::accept_incoming_connection(self_, config, status_tx, mempool).await; + Self::accept_incoming_connection(self_, config, status_tx, mempool, sender).await; } async fn accept_incoming_connection( _self_: Arc>, config: Configuration, status_tx: crate::status::Sender, mempool: Arc>, + submit_solution_sender: Sender, ) { let listner = TcpListener::bind(&config.listen_jd_address).await.unwrap(); while let Ok((stream, _)) = listner.accept().await { @@ -205,7 +278,11 @@ impl JobDeclarator { mempool.clone(), ))); - JobDeclaratorDownstream::start(jddownstream, status_tx.clone()); + JobDeclaratorDownstream::start( + jddownstream, + status_tx.clone(), + submit_solution_sender.clone(), + ); } else { error!("Can not connect {:?}", addr); } diff --git a/roles/jd-server/src/lib/mempool/error.rs b/roles/jd-server/src/lib/mempool/error.rs new file mode 100644 index 0000000000..24779ec582 --- /dev/null +++ b/roles/jd-server/src/lib/mempool/error.rs @@ -0,0 +1,38 @@ +use super::RpcError; +use tokio::task::JoinError; + +// TODO this should be includede in JdsError + +#[derive(Debug)] +pub enum JdsMempoolError { + EmptyMempool, + NoClient, + Rpc(RpcError), + PoisonLock(String), + TokioJoinError(JoinError), +} + +pub fn handle_error(error: JdsMempoolError) { + match error { + JdsMempoolError::EmptyMempool => println!("Empty mempool!"), + JdsMempoolError::NoClient => println!("RPC Client not found"), + JdsMempoolError::Rpc(a) => match a { + RpcError::JsonRpc(m) => { + let id = m.id; + let error = m.error.unwrap(); + let code = error.code; + let message = error.message; + println!( + "RPC error: id {:?}, code {:?}, message {:?}", + id, code, message + ); + } + RpcError::Deserialization(e) => println!("Deserialization error: {:?}", e), + RpcError::Serialization(e) => println!("Serialization error: {:?}", e), + RpcError::Http(e) => println!("Http error: {:?}", e), + RpcError::Other(e) => println!("Other error: {:?}", e), + }, + JdsMempoolError::PoisonLock(e) => println!("Poison lock error: {:?}", e), + JdsMempoolError::TokioJoinError(e) => println!("Tokio Join error: {:?}", e), + } +} diff --git a/roles/jd-server/src/lib/mempool/hex_iterator.rs b/roles/jd-server/src/lib/mempool/hex_iterator.rs deleted file mode 100644 index 92d05582c7..0000000000 --- a/roles/jd-server/src/lib/mempool/hex_iterator.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::{io, io::Error}; -/// Iterator over a hex-encoded string slice which decodes hex and yields bytes. -pub struct HexIterator<'a> { - /// The `Bytes` iterator whose next two bytes will be decoded to yield - /// the next byte. - iter: std::str::Bytes<'a>, -} - -impl<'a> HexIterator<'a> { - /// Constructs a new `HexIterator` from a string slice. - /// - /// # Errors - /// - /// If the input string is of odd length. - pub fn new(s: &'a str) -> Result, Error> { - if s.len() % 2 != 0 { - panic!("the length must be even!"); - //Err(Error::OddLengthString(s.len())) - } else { - Ok(HexIterator { iter: s.bytes() }) - } - } -} -impl<'a> Iterator for HexIterator<'a> { - type Item = Result; - - fn next(&mut self) -> Option> { - let hi = self.iter.next()?; - let lo = self.iter.next().unwrap(); - Some(chars_to_hex(hi, lo)) - } - - fn size_hint(&self) -> (usize, Option) { - let (min, max) = self.iter.size_hint(); - (min / 2, max.map(|x| x / 2)) - } -} - -impl<'a> io::Read for HexIterator<'a> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let mut bytes_read = 0usize; - for dst in buf { - match self.next() { - Some(Ok(src)) => { - *dst = src; - bytes_read += 1; - } - _ => break, - } - } - Ok(bytes_read) - } -} - -fn chars_to_hex(hi: u8, lo: u8) -> Result { - let hih = (hi as char).to_digit(16).ok_or(HexError::InvalidChar(hi))?; - let loh = (lo as char).to_digit(16).ok_or(HexError::InvalidChar(lo))?; - - let ret = (hih << 4) + loh; - Ok(ret as u8) -} - -pub enum HexError { - /// Non-hexadecimal character. - InvalidChar(u8), - // Purported hex string had odd length. - //OddLengthString(usize), - // Tried to parse fixed-length hash from a string with the wrong type (expected, got). - //InvalidLength(usize, usize), -} diff --git a/roles/jd-server/src/lib/mempool/mini_rpc_client.rs b/roles/jd-server/src/lib/mempool/mini_rpc_client.rs new file mode 100644 index 0000000000..a446cfb8bc --- /dev/null +++ b/roles/jd-server/src/lib/mempool/mini_rpc_client.rs @@ -0,0 +1,207 @@ +// TODO +// - WHEN UPGRADING TO 1.0.1 Client is in hyper-utils: +// Struct hyper_util::client::legacy::Client +// - use https for security reasons +// - manage id in RpcResult messages +use base64::Engine; +use bytes::Bytes; +use hex::decode; +use http_body_util::{BodyExt, Full}; +use hyper::{ + header::{AUTHORIZATION, CONTENT_TYPE}, + Request, +}; +use hyper_util::{ + client::legacy::{connect::HttpConnector, Client}, + rt::TokioExecutor, +}; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use stratum_common::bitcoin::{consensus::encode::deserialize as consensus_decode, Transaction}; + +use super::BlockHash; + +#[derive(Clone, Debug)] +pub struct MiniRpcClient { + client: Client>, + //url: &'a str, + url: String, + auth: Auth, +} + +impl MiniRpcClient { + pub fn new(url: String, auth: Auth) -> MiniRpcClient { + let client: Client<_, Full> = Client::builder(TokioExecutor::new()).build_http(); + MiniRpcClient { client, url, auth } + } + + pub async fn get_raw_transaction( + &self, + txid: &String, + block_hash: Option<&BlockHash>, + ) -> Result { + let response = match block_hash { + Some(hash) => { + self.send_json_rpc_request("getrawtransaction", json!([txid, false, hash])) + } + None => self.send_json_rpc_request("getrawtransaction", json!([txid, false])), + } + .await; + match response { + Ok(result_hex) => { + let result_deserialized: JsonRpcResult = serde_json::from_str(&result_hex) + .map_err(|e| { + RpcError::Deserialization(e.to_string()) // TODO manage message ids + })?; + let transaction_hex: String = result_deserialized + .result + .ok_or_else(|| RpcError::Other("Result not found".to_string()))?; + let transaction_bytes = decode(transaction_hex).expect("Decoding failed"); + Ok(consensus_decode(&transaction_bytes).expect("Deserialization failed")) + } + Err(error) => Err(error), + } + } + + pub async fn get_raw_mempool_verbose(&self) -> Result, RpcError> { + let response = self.send_json_rpc_request("getrawmempool", json!([])).await; + match response { + Ok(result_hex) => { + let result_deserialized: JsonRpcResult> = + serde_json::from_str(&result_hex).map_err(|e| { + RpcError::Deserialization(e.to_string()) // TODO manage message ids + })?; + let mempool: Vec = result_deserialized + .result + .ok_or_else(|| RpcError::Other("Result not found".to_string()))?; + Ok(mempool) + } + Err(error) => Err(error), + } + } + + pub async fn submit_block(&self, block_hex: String) -> Result<(), RpcError> { + let response = self + .send_json_rpc_request("submitblock", json!([block_hex])) + .await; + + match response { + Ok(_) => Ok(()), + Err(error) => Err(error), + } + } + + async fn send_json_rpc_request( + &self, + method: &str, + params: serde_json::Value, + ) -> Result { + let client = &self.client; + let (username, password) = self.auth.clone().get_user_pass(); + let request = JsonRpcRequest { + jsonrpc: "2.0".to_string(), + method: method.to_string(), + params, + id: 1, //TODO manage message ids + }; + + let request_body = match serde_json::to_string(&request) { + Ok(body) => body, + Err(e) => return Err(RpcError::Serialization(e.to_string())), + }; + + let req = Request::builder() + .method("POST") + .uri(self.url.as_str()) + .header(CONTENT_TYPE, "application/json") + .header( + AUTHORIZATION, + format!( + "Basic {}", + base64::engine::general_purpose::STANDARD + .encode(format!("{}:{}", username, password)) + ), + ) + .body(Full::::from(request_body)) + .map_err(|e| RpcError::Http(e.to_string()))?; + + let response = client + .request(req) + .await + .map_err(|e| RpcError::Http(e.to_string()))?; + + let status = response.status(); + let body = response + .into_body() + .collect() + .await + .map_err(|e| RpcError::Http(e.to_string()))? + .to_bytes() + .to_vec(); + + if status.is_success() { + String::from_utf8(body).map_err(|e| { + RpcError::Deserialization(e.to_string()) // TODO manage message ids + }) + } else { + let error_result: Result, _> = serde_json::from_slice(&body); + match error_result { + Ok(error_response) => Err(error_response.into()), + Err(e) => Err(RpcError::Deserialization(e.to_string())), + } + } + } +} + +#[derive(Clone, Debug)] +pub struct Auth { + username: String, + password: String, +} + +impl Auth { + pub fn get_user_pass(self) -> (String, String) { + (self.username, self.password) + } + pub fn new(username: String, password: String) -> Auth { + Auth { username, password } + } +} + +#[derive(Debug, Serialize)] +struct JsonRpcRequest { + jsonrpc: String, + method: String, + params: serde_json::Value, + id: u64, +} + +#[derive(Debug, Deserialize)] +pub struct JsonRpcResult { + result: Option, + pub error: Option, + pub id: u64, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct JsonRpcError { + pub code: i32, + pub message: String, +} + +#[derive(Debug, Deserialize)] +pub enum RpcError { + // TODO this type is slightly incorrect, as the JsonRpcError evaluates a generic that is meant + // for the result field of JsonRpcResult struct. This should be corrected + JsonRpc(JsonRpcResult), + Deserialization(String), + Serialization(String), + Http(String), + Other(String), +} + +impl From> for RpcError { + fn from(error: JsonRpcResult) -> Self { + Self::JsonRpc(error) + } +} diff --git a/roles/jd-server/src/lib/mempool/mod.rs b/roles/jd-server/src/lib/mempool/mod.rs index 39b0096a31..abfe797f3f 100644 --- a/roles/jd-server/src/lib/mempool/mod.rs +++ b/roles/jd-server/src/lib/mempool/mod.rs @@ -1,9 +1,12 @@ -pub mod hex_iterator; -pub mod rpc_client; +pub mod error; +pub mod mini_rpc_client; +use async_channel::Receiver; use bitcoin::blockdata::transaction::Transaction; use hashbrown::HashMap; use roles_logic_sv2::utils::Mutex; -use rpc_client::{Auth, RpcApi, RpcClient}; +//use rpc_client::{Auth, RpcApi, RpcClient}; +use crate::mempool::error::JdsMempoolError; +use mini_rpc_client::RpcError; use serde::{Deserialize, Serialize}; use std::{convert::TryInto, sync::Arc}; use stratum_common::{bitcoin, bitcoin::hash_types::Txid}; @@ -26,26 +29,42 @@ pub struct TransacrtionWithHash { #[derive(Clone, Debug)] pub struct JDsMempool { pub mempool: Vec, - auth: Auth, + auth: mini_rpc_client::Auth, url: String, + receiver: Receiver, } impl JDsMempool { - pub fn get_client(&self) -> Option { + pub fn get_client(&self) -> Option { let url = self.url.as_str(); if url.contains("http") { - Some(RpcClient::new(url, self.auth.clone()).unwrap()) + let client = mini_rpc_client::MiniRpcClient::new(url.to_string(), self.auth.clone()); + Some(client) } else { None } } - pub fn new(url: String, username: String, password: String) -> Self { - let auth = Auth::UserPass(username, password); + + pub fn get_transaction_list(self_: Arc>) -> Vec { + let tx_list = self_.safe_lock(|x| x.mempool.clone()).unwrap(); + let tx_list_: Vec = tx_list.iter().map(|n| n.id).collect(); + tx_list_ + //.map_err(|e| JdsMempoolError::PoisonLock(e.to_string()))? + //.ok_or(JdsMempoolError::NoClient)?; + } + pub fn new( + url: String, + username: String, + password: String, + receiver: Receiver, + ) -> Self { + let auth = mini_rpc_client::Auth::new(username, password); let empty_mempool: Vec = Vec::new(); JDsMempool { mempool: empty_mempool, auth, url, + receiver, } } @@ -53,13 +72,16 @@ impl JDsMempool { let mut mempool_ordered: Vec = Vec::new(); let client = self_ .safe_lock(|x| x.get_client()) - .unwrap() + .map_err(|e| JdsMempoolError::PoisonLock(e.to_string()))? .ok_or(JdsMempoolError::NoClient)?; let new_mempool: Result, JdsMempoolError> = tokio::task::spawn(async move { - let mempool: Vec = client.get_raw_mempool_verbose().unwrap(); + let mempool: Vec = client + .get_raw_mempool_verbose() + .await + .map_err(JdsMempoolError::Rpc)?; for id in &mempool { - let tx: Result = client.get_raw_transaction(id, None); + let tx: Result = client.get_raw_transaction(id, None).await; if let Ok(tx) = tx { let id = tx.txid(); mempool_ordered.push(TransacrtionWithHash { id, tx }); @@ -72,7 +94,7 @@ impl JDsMempool { } }) .await - .unwrap(); + .map_err(JdsMempoolError::TokioJoinError)?; match new_mempool { Ok(new_mempool_) => { @@ -85,6 +107,24 @@ impl JDsMempool { } } + pub async fn on_submit(self_: Arc>) -> Result<(), JdsMempoolError> { + let receiver: Receiver = self_ + .safe_lock(|x| x.receiver.clone()) + .map_err(|e| JdsMempoolError::PoisonLock(e.to_string()))?; + let client = self_ + .safe_lock(|x| x.get_client()) + .map_err(|e| JdsMempoolError::PoisonLock(e.to_string()))? + .ok_or(JdsMempoolError::NoClient)?; + + while let Ok(block_hex) = receiver.recv().await { + match mini_rpc_client::MiniRpcClient::submit_block(&client, block_hex).await { + Ok(_) => return Ok(()), + Err(e) => JdsMempoolError::Rpc(e), + }; + } + Ok(()) + } + pub fn to_short_ids(&self, nonce: u64) -> Option> { let mut ret = HashMap::new(); for tx in &self.mempool { @@ -101,9 +141,3 @@ impl JDsMempool { Some(ret) } } - -#[derive(Debug)] -pub enum JdsMempoolError { - EmptyMempool, - NoClient, -} diff --git a/roles/jd-server/src/lib/mempool/rpc_client.rs b/roles/jd-server/src/lib/mempool/rpc_client.rs deleted file mode 100644 index b422af5cd3..0000000000 --- a/roles/jd-server/src/lib/mempool/rpc_client.rs +++ /dev/null @@ -1,207 +0,0 @@ -use crate::lib::mempool::{hex_iterator::HexIterator, Amount, BlockHash}; -use bitcoin::{blockdata::transaction::Transaction, consensus::Decodable}; -use jsonrpc::{error::Error as JsonRpcError, Client as JosnRpcClient}; -use serde::Deserialize; -use stratum_common::bitcoin; - -#[derive(Clone, Debug)] -pub enum Auth { - //None, - UserPass(String, String), - //CookieFile(PathBuf), -} - -impl Auth { - pub fn get_user_pass(self) -> (Option, Option) { - match self { - Auth::UserPass(u, p) => (Some(u), Some(p)), - } - } -} - -pub struct RpcClient { - client: JosnRpcClient, //jsonrpc::client::Client, -} - -impl RpcClient { - pub fn new(url: &str, auth: Auth) -> Result { - let (user, pass) = auth.get_user_pass(); - jsonrpc::client::Client::simple_http(url, user, pass) - .map(|client| RpcClient { client }) - .map_err(|e| BitcoincoreRpcError::JsonRpc(e.into())) - } - pub fn submit_block( - &self, - submit_block: String, - ) -> Result, BitcoincoreRpcError> { - self.call( - "submitblock", - &[serde_json::to_value(submit_block).unwrap()], - ) - } -} - -pub trait RpcApi: Sized { - /// Call a `cmd` rpc with given `args` list - fn call serde::de::Deserialize<'a>>( - &self, - cmd: &str, - args: &[serde_json::Value], - ) -> Result; - - fn get_raw_mempool_verbose(&self) -> Result, BitcoincoreRpcError> { - self.call("getrawmempool", &[]) - } - - fn get_raw_transaction( - &self, - txid: &String, - block_hash: Option<&BlockHash>, - ) -> Result { - let mut args = [ - into_json(txid)?, - into_json(false)?, - opt_into_json(block_hash)?, - ]; - let hex: String = self - .call( - "getrawtransaction", - handle_defaults(&mut args, &[serde_json::Value::Null]), - ) - .map_err(|_| JsonRpcError::EmptyBatch)?; - let mut reader = - HexIterator::new(&hex).unwrap_or_else(|_| panic!("Can not decode hex {}", hex)); - let object = Decodable::consensus_decode(&mut reader).expect("Can not decode transaction"); - Ok(object) - } -} - -/// Shorthand for converting a variable into a serde_json::Value. -fn into_json(val: T) -> Result -where - T: serde::ser::Serialize, -{ - Ok(serde_json::to_value(val)?) -} - -/// Shorthand for converting an Option into an Option. -fn opt_into_json(opt: Option) -> Result -where - T: serde::ser::Serialize, -{ - match opt { - Some(val) => Ok(into_json(val)?), - None => Ok(serde_json::Value::Null), - } -} - -impl RpcApi for RpcClient { - /// Call an `cmd` rpc with given `args` list - fn call serde::de::Deserialize<'a>>( - &self, - cmd: &str, - args: &[serde_json::Value], - ) -> RResult { - let raw_args: Vec<_> = args - .iter() - .map(|a| { - 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); - - let resp = self.client.send_request(req).map_err(JsonRpcError::from); - Ok(resp?.result()?) - } -} - -pub type RResult = Result; - -/// The error type for errors produced in this library. -#[derive(Debug)] -pub enum BitcoincoreRpcError { - JsonRpc(jsonrpc::error::Error), - //Hex(hex::Error), - Json(serde_json::error::Error), - //BitcoinSerialization(bitcoin::consensus::encode::Error), - //Secp256k1(secp256k1::Error), - //Io(io::Error), - //InvalidAmount(bitcoin::util::amount::ParseAmountError), - //InvalidCookieFile, - // The JSON result had an unexpected structure. - //UnexpectedStructure, - // The daemon returned an error string. - //ReturnedError(String), -} - -impl From for BitcoincoreRpcError { - fn from(e: jsonrpc::error::Error) -> BitcoincoreRpcError { - BitcoincoreRpcError::JsonRpc(e) - } -} - -/// Handle default values in the argument list -/// -/// Substitute `Value::Null`s with corresponding values from `defaults` table, -/// except when they are trailing, in which case just skip them altogether -/// in returned list. -/// -/// Note, that `defaults` corresponds to the last elements of `args`. -/// -/// ```norust -/// arg1 arg2 arg3 arg4 -/// def1 def2 -/// ``` -/// -/// Elements of `args` without corresponding `defaults` value, won't -/// be substituted, because they are required. -fn handle_defaults<'a>( - args: &'a mut [serde_json::Value], - defaults: &[serde_json::Value], -) -> &'a [serde_json::Value] { - assert!(args.len() >= defaults.len()); - - // Pass over the optional arguments in backwards order, filling in defaults after the first - // non-null optional argument has been observed. - let mut first_non_null_optional_idx = None; - for i in 0..defaults.len() { - let args_i = args.len() - 1 - i; - let defaults_i = defaults.len() - 1 - i; - if args[args_i] == serde_json::Value::Null { - if first_non_null_optional_idx.is_some() { - if defaults[defaults_i] == serde_json::Value::Null { - panic!("Missing `default` for argument idx {}", args_i); - } - args[args_i] = defaults[defaults_i].clone(); - } - } else if first_non_null_optional_idx.is_none() { - first_non_null_optional_idx = Some(args_i); - } - } - - let required_num = args.len() - defaults.len(); - - if let Some(i) = first_non_null_optional_idx { - &args[..i + 1] - } else { - &args[..required_num] - } -} - -#[derive(Deserialize)] -pub struct GetMempoolEntryResultFees { - /// Transaction fee in BTC - //#[serde(with = "bitcoin::amount::serde::as_btc")] - pub base: Amount, - /// Transaction fee with fee deltas used for mining priority in BTC - //#[serde(with = "bitcoin::amount::serde::as_btc")] - pub modified: Amount, - /// Modified fees (see above) of in-mempool ancestors (including this one) in BTC - //#[serde(with = "bitcoin::amount::serde::as_btc")] - pub ancestor: Amount, - /// Modified fees (see above) of in-mempool descendants (including this one) in BTC - //#[serde(with = "bitcoin::amount::serde::as_btc")] - pub descendant: Amount, -} diff --git a/roles/jd-server/src/main.rs b/roles/jd-server/src/main.rs index 6082e6ecf4..c8e41a6839 100644 --- a/roles/jd-server/src/main.rs +++ b/roles/jd-server/src/main.rs @@ -1,5 +1,5 @@ #![allow(special_module_name)] -use async_channel::unbounded; +use async_channel::{unbounded, Receiver, Sender}; use codec_sv2::{StandardEitherFrame, StandardSv2Frame}; use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; use roles_logic_sv2::{ @@ -169,21 +169,40 @@ 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 (submit_solution_sender, submit_solution_receiver): (Sender, Receiver) = + unbounded(); let mempool = Arc::new(Mutex::new(mempool::JDsMempool::new( url.clone(), username, password, + submit_solution_receiver, ))); let mempool_cloned_ = mempool.clone(); if url.contains("http") { task::spawn(async move { loop { - let _ = mempool::JDsMempool::update_mempool(mempool_cloned_.clone()).await; + let result: Result<(), mempool::error::JdsMempoolError> = + mempool::JDsMempool::update_mempool(mempool_cloned_.clone()).await; + if let Err(a) = result { + mempool::error::handle_error(a); + }; + let transactions = + mempool::JDsMempool::get_transaction_list(mempool_cloned_.clone()); + dbg!(transactions); // TODO this should be configurable by the user tokio::time::sleep(Duration::from_millis(10000)).await; } }); }; + let mempool_cloned__ = mempool.clone(); + task::spawn(async move { + loop { + let result = mempool::JDsMempool::on_submit(mempool_cloned__.clone()).await; + if let Err(a) = result { + mempool::error::handle_error(a); + }; + } + }); let (status_tx, status_rx) = unbounded(); info!("Jds INITIALIZING with config: {:?}", &args.config_path); @@ -191,7 +210,9 @@ async fn main() { let cloned = config.clone(); let sender = status::Sender::Downstream(status_tx.clone()); let mempool_cloned = mempool.clone(); - task::spawn(async move { JobDeclarator::start(cloned, sender, mempool_cloned).await }); + task::spawn(async move { + JobDeclarator::start(cloned, sender, mempool_cloned, submit_solution_sender).await + }); // Start the error handling loop // See `./status.rs` and `utils/error_handling` for information on how this operates diff --git a/roles/jd-server/vendored/rust-jsonrpc/CHANGELOG.md b/roles/jd-server/vendored/rust-jsonrpc/CHANGELOG.md deleted file mode 100644 index 03ad9f82b1..0000000000 --- a/roles/jd-server/vendored/rust-jsonrpc/CHANGELOG.md +++ /dev/null @@ -1,78 +0,0 @@ -# 0.16.0 - 2023-06-29 - -* Re-export the `minreq` crate when the feature is set - [#102](https://github.com/apoelstra/rust-jsonrpc/pull/102) -* Don't treat HTTP errors with no JSON as JSON parsing errors - [#103](https://github.com/apoelstra/rust-jsonrpc/pull/103) - -# 0.15.0 - 2023-05-28 - -* Add new transport that uses `minreq` - [#94](https://github.com/apoelstra/rust-jsonrpc/pull/94) -* Bump MSRV to rust 1.48.0 - [#91](https://github.com/apoelstra/rust-jsonrpc/pull/91) - -# 0.14.1 - 2023-04-03 - -* simple_http: fix "re-open socket on write failure" behavior - [#84](https://github.com/apoelstra/rust-jsonrpc/pull/84) - [#86](https://github.com/apoelstra/rust-jsonrpc/pull/86) -* simple_http: add "host" header (required by HTTP 1.1) - [#85](https://github.com/apoelstra/rust-jsonrpc/pull/85) -* simple_http: add ability to replace URL/path; minor ergonomic improvements - [#89](https://github.com/apoelstra/rust-jsonrpc/pull/89) - -# 0.14.0 - 2022-11-28 - -This release significantly improves our `simple_http` client, though at the -apparent cost of a performance regression when making repeated RPC calls to -a local bitcoind. We are unsure what to make of this, since our code now uses -fewer sockets, less memory and does less redundant processing. - -The highlights are: - -* Support JSON replies that span multiple lines - [#70](https://github.com/apoelstra/rust-jsonrpc/pull/69) -* Add feature-gated support for using a SOCKS proxy - [#70](https://github.com/apoelstra/rust-jsonrpc/pull/70) -* Fix resource exhaustive bug on MacOS by reusing sockets - [#72](https://github.com/apoelstra/rust-jsonrpc/pull/72) - [#76](https://github.com/apoelstra/rust-jsonrpc/pull/76) - -As well as improvements to our code quality and test infrastructure. - -# 0.13.0 - 2022-07-21 "Edition 2018 Release" - -This release increases the MSRV to 1.41.1, bringing with it a bunch of new language features. - -Some highlights: - -- The MSRV bump [#58](https://github.com/apoelstra/rust-jsonrpc/pull/58) -- Add IPv6 support [#63](https://github.com/apoelstra/rust-jsonrpc/pull/63) -- Remove `serder_derive` dependency [#61](https://github.com/apoelstra/rust-jsonrpc/pull/61) - -# 0.12.1 - 2022-01-20 - -## Features - -* A new set of transports were added for JSONRPC over raw TCP sockets (one using `SocketAddr`, and - one UNIX-only using Unix Domain Sockets) - -## Bug fixes - -* The `Content-Type` HTTP header is now correctly set to `application/json` -* The `Connection: Close` HTTP header is now sent for requests - -# 0.12.0 - 2020-12-16 - -* Remove `http` and `hyper` dependencies -* Implement our own simple HTTP transport for Bitcoin Core -* But allow use of generic transports - -# 0.11.0 - 2019-04-05 - -* [Clean up the API](https://github.com/apoelstra/rust-jsonrpc/pull/19) -* [Set the content-type header to json]((https://github.com/apoelstra/rust-jsonrpc/pull/21) -* [Allow no `result` field in responses](https://github.com/apoelstra/rust-jsonrpc/pull/16) -* [Add batch request support](https://github.com/apoelstra/rust-jsonrpc/pull/24) - diff --git a/roles/jd-server/vendored/rust-jsonrpc/Cargo.toml b/roles/jd-server/vendored/rust-jsonrpc/Cargo.toml deleted file mode 100644 index 344bc7e0dc..0000000000 --- a/roles/jd-server/vendored/rust-jsonrpc/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "jsonrpc" -version = "0.16.0" -authors = ["Andrew Poelstra "] -license = "CC0-1.0" -homepage = "https://github.com/apoelstra/rust-jsonrpc/" -repository = "https://github.com/apoelstra/rust-jsonrpc/" -documentation = "https://docs.rs/jsonrpc/" -description = "Rust support for the JSON-RPC 2.0 protocol" -keywords = [ "protocol", "json", "http", "jsonrpc" ] -readme = "README.md" -edition = "2018" - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] - -[features] -default = [ "simple_http" ] -# A bare-minimum HTTP transport. -simple_http = [ "base64" ] - - -[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 } diff --git a/roles/jd-server/vendored/rust-jsonrpc/LICENSE b/roles/jd-server/vendored/rust-jsonrpc/LICENSE deleted file mode 100644 index 6ca207ef00..0000000000 --- a/roles/jd-server/vendored/rust-jsonrpc/LICENSE +++ /dev/null @@ -1,122 +0,0 @@ -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. - diff --git a/roles/jd-server/vendored/rust-jsonrpc/README.md b/roles/jd-server/vendored/rust-jsonrpc/README.md deleted file mode 100644 index ece3c413e5..0000000000 --- a/roles/jd-server/vendored/rust-jsonrpc/README.md +++ /dev/null @@ -1,58 +0,0 @@ -[![Status](https://travis-ci.org/apoelstra/rust-jsonrpc.png?branch=master)](https://travis-ci.org/apoelstra/rust-jsonrpc) - -# Rust Version compatibility - -This library is compatible with Rust **1.48.0** or higher. - -However, if you want to use the library with 1.41, you will need to pin a couple of -our dependencies: - -``` -cargo update -p serde --precise 1.0.156 -cargo update -p syn --precise 1.0.107 -``` - -before building. (And if your code is a library, your downstream users will need to -run these commands, and so on.) - -# Rust JSONRPC Client - -Rudimentary support for sending JSONRPC 2.0 requests and receiving responses. - -As an example, hit a local bitcoind JSON-RPC endpoint and call the `uptime` command. - -```rust -use jsonrpc::Client; -use jsonrpc::simple_http::{self, SimpleHttpTransport}; - -fn client(url: &str, user: &str, pass: &str) -> Result { - let t = SimpleHttpTransport::builder() - .url(url)? - .auth(user, Some(pass)) - .build(); - - Ok(Client::with_transport(t)) -} - -// Demonstrate an example JSON-RCP call against bitcoind. -fn main() { - let client = client("localhost:18443", "user", "pass").expect("failed to create client"); - let request = client.build_request("uptime", &[]); - let response = client.send_request(request).expect("send_request failed"); - - // For other commands this would be a struct matching the returned json. - let result: u64 = response.result().expect("response is an error, use check_error"); - println!("bitcoind uptime: {}", result); -} -``` - -## Githooks - -To assist devs in catching errors _before_ running CI we provide some githooks. If you do not -already have locally configured githooks you can use the ones in this repository by running, in the -root directory of the repository: -``` -git config --local core.hooksPath githooks/ -``` - -Alternatively add symlinks in your `.git/hooks` directory to any of the githooks we provide. diff --git a/roles/jd-server/vendored/rust-jsonrpc/src/client.rs b/roles/jd-server/vendored/rust-jsonrpc/src/client.rs deleted file mode 100644 index 57c53253dc..0000000000 --- a/roles/jd-server/vendored/rust-jsonrpc/src/client.rs +++ /dev/null @@ -1,268 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! # Client support -//! -//! Support for connecting to JSONRPC servers over HTTP, sending requests, -//! and parsing responses - -use std::{ - borrow::Cow, - collections::HashMap, - fmt, - hash::{Hash, Hasher}, - sync::atomic, -}; - -use serde_json::{value::RawValue, Value}; - -use crate::{error::Error, Request, Response}; - -/// An interface for a transport over which to use the JSONRPC protocol. -pub trait Transport: Send + Sync + 'static { - /// Sends an RPC request over the transport. - fn send_request(&self, _: Request) -> Result; - /// Sends a batch of RPC requests over the transport. - fn send_batch(&self, _: &[Request]) -> Result, Error>; - /// Formats the target of this transport. I.e. the URL/socket/... - fn fmt_target(&self, f: &mut fmt::Formatter) -> fmt::Result; -} - -/// A JSON-RPC client. -/// -/// Creates a new Client using one of the transport-specific constructors e.g., -/// [`Client::simple_http`] for a bare-minimum HTTP transport. -pub struct Client { - pub(crate) transport: Box, - nonce: atomic::AtomicUsize, -} - -impl Client { - /// Creates a new client with the given transport. - pub fn with_transport(transport: T) -> Client { - Client { - transport: Box::new(transport), - nonce: atomic::AtomicUsize::new(1), - } - } - - /// Builds a request. - /// - /// To construct the arguments, one can use one of the shorthand methods - /// [`crate::arg`] or [`crate::try_arg`]. - pub fn build_request<'a>(&self, method: &'a str, params: &'a [Box]) -> Request<'a> { - let nonce = self.nonce.fetch_add(1, atomic::Ordering::Relaxed); - Request { - method, - params, - id: serde_json::Value::from(nonce), - jsonrpc: Some("2.0"), - } - } - - /// Sends a request to a client. - pub fn send_request(&self, request: Request) -> Result { - self.transport.send_request(request) - } - - /// Sends a batch of requests to the client. - /// - /// Note that the requests need to have valid IDs, so it is advised to create the requests - /// with [`Client::build_request`]. - /// - /// # Returns - /// - /// The return vector holds the response for the request at the corresponding index. If no - /// response was provided, it's [`None`]. - pub fn send_batch(&self, requests: &[Request]) -> Result>, Error> { - if requests.is_empty() { - return Err(Error::EmptyBatch); - } - - // If the request body is invalid JSON, the response is a single response object. - // We ignore this case since we are confident we are producing valid JSON. - let responses = self.transport.send_batch(requests)?; - if responses.len() > requests.len() { - return Err(Error::WrongBatchResponseSize); - } - - //TODO(stevenroose) check if the server preserved order to avoid doing the mapping - - // First index responses by ID and catch duplicate IDs. - let mut by_id = HashMap::with_capacity(requests.len()); - for resp in responses.into_iter() { - let id = HashableValue(Cow::Owned(resp.id.clone())); - if let Some(dup) = by_id.insert(id, resp) { - return Err(Error::BatchDuplicateResponseId(dup.id)); - } - } - // Match responses to the requests. - 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. - if let Some(id) = by_id.keys().next() { - return Err(Error::WrongBatchResponseId((*id.0).clone())); - } - - Ok(results) - } - - /// Makes a request and deserializes the response. - /// - /// To construct the arguments, one can use one of the shorthand methods - /// [`crate::arg`] or [`crate::try_arg`]. - pub fn call serde::de::Deserialize<'a>>( - &self, - method: &str, - args: &[Box], - ) -> Result { - let request = self.build_request(method, args); - let id = request.id.clone(); - - let response = self.send_request(request)?; - if response.jsonrpc.is_some() && response.jsonrpc != Some(From::from("2.0")) { - return Err(Error::VersionMismatch); - } - if response.id != id { - return Err(Error::NonceMismatch); - } - - response.result() - } -} - -impl fmt::Debug for crate::Client { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "jsonrpc::Client(")?; - self.transport.fmt_target(f)?; - write!(f, ")") - } -} - -impl From for Client { - fn from(t: T) -> Client { - Client::with_transport(t) - } -} - -/// Newtype around `Value` which allows hashing for use as hashmap keys, -/// this is needed for batch requests. -/// -/// The reason `Value` does not support `Hash` or `Eq` by itself -/// is that it supports `f64` values; but for batch requests we -/// will only be hashing the "id" field of the request/response -/// pair, which should never need decimal precision and therefore -/// never use `f64`. -#[derive(Clone, PartialEq, Debug)] -struct HashableValue<'a>(pub Cow<'a, Value>); - -impl<'a> Eq for HashableValue<'a> {} - -impl<'a> Hash for HashableValue<'a> { - fn hash(&self, state: &mut H) { - match *self.0.as_ref() { - Value::Null => "null".hash(state), - Value::Bool(false) => "false".hash(state), - Value::Bool(true) => "true".hash(state), - Value::Number(ref n) => { - "number".hash(state); - if let Some(n) = n.as_i64() { - n.hash(state); - } else if let Some(n) = n.as_u64() { - n.hash(state); - } else { - n.to_string().hash(state); - } - } - Value::String(ref s) => { - "string".hash(state); - s.hash(state); - } - Value::Array(ref v) => { - "array".hash(state); - v.len().hash(state); - for obj in v { - HashableValue(Cow::Borrowed(obj)).hash(state); - } - } - Value::Object(ref m) => { - "object".hash(state); - m.len().hash(state); - for (key, val) in m { - key.hash(state); - HashableValue(Cow::Borrowed(val)).hash(state); - } - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use std::{borrow::Cow, collections::HashSet, str::FromStr, sync}; - - struct DummyTransport; - impl Transport for DummyTransport { - fn send_request(&self, _: Request) -> Result { - Err(Error::NonceMismatch) - } - fn send_batch(&self, _: &[Request]) -> Result, Error> { - Ok(vec![]) - } - fn fmt_target(&self, _: &mut fmt::Formatter) -> fmt::Result { - Ok(()) - } - } - - #[test] - fn sanity() { - let client = Client::with_transport(DummyTransport); - assert_eq!(client.nonce.load(sync::atomic::Ordering::Relaxed), 1); - let req1 = client.build_request("test", &[]); - assert_eq!(client.nonce.load(sync::atomic::Ordering::Relaxed), 2); - let req2 = client.build_request("test", &[]); - assert_eq!(client.nonce.load(sync::atomic::Ordering::Relaxed), 3); - assert!(req1.id != req2.id); - } - - #[test] - fn hash_value() { - 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 mut coll = HashSet::new(); - - assert!(!coll.contains(&val)); - coll.insert(val.clone()); - assert!(coll.contains(&val)); - - assert!(!coll.contains(&t)); - assert!(!coll.contains(&f)); - coll.insert(t.clone()); - assert!(coll.contains(&t)); - assert!(!coll.contains(&f)); - coll.insert(f.clone()); - assert!(coll.contains(&t)); - assert!(coll.contains(&f)); - - assert!(!coll.contains(&ns)); - coll.insert(ns.clone()); - assert!(coll.contains(&ns)); - - assert!(!coll.contains(&m)); - coll.insert(m.clone()); - assert!(coll.contains(&m)); - } -} diff --git a/roles/jd-server/vendored/rust-jsonrpc/src/error.rs b/roles/jd-server/vendored/rust-jsonrpc/src/error.rs deleted file mode 100644 index f56641137b..0000000000 --- a/roles/jd-server/vendored/rust-jsonrpc/src/error.rs +++ /dev/null @@ -1,243 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! # Error handling -//! -//! Some useful methods for creating Error objects. - -use std::{error, fmt}; - -use serde::{Deserialize, Serialize}; - -use crate::Response; - -/// A library error -#[derive(Debug)] -#[non_exhaustive] -pub enum Error { - /// A transport error - Transport(Box), - /// Json error - Json(serde_json::Error), - /// Error response - Rpc(RpcError), - /// Response to a request did not have the expected nonce - NonceMismatch, - /// Response to a request had a jsonrpc field other than "2.0" - VersionMismatch, - /// Batches can't be empty - EmptyBatch, - /// Too many responses returned in batch - WrongBatchResponseSize, - /// Batch response contained a duplicate ID - BatchDuplicateResponseId(serde_json::Value), - /// Batch response contained an ID that didn't correspond to any request ID - WrongBatchResponseId(serde_json::Value), -} - -impl From for Error { - fn from(e: serde_json::Error) -> Error { - Error::Json(e) - } -} - -impl From for Error { - fn from(e: RpcError) -> Error { - Error::Rpc(e) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Error::*; - - match *self { - Transport(ref e) => write!(f, "transport error: {}", e), - Json(ref e) => write!(f, "JSON decode error: {}", e), - Rpc(ref r) => write!(f, "RPC error response: {:?}", r), - BatchDuplicateResponseId(ref v) => write!(f, "duplicate RPC batch response ID: {}", v), - WrongBatchResponseId(ref v) => write!(f, "wrong RPC batch response ID: {}", v), - NonceMismatch => write!(f, "nonce of response did not match nonce of request"), - VersionMismatch => write!(f, "`jsonrpc` field set to non-\"2.0\""), - EmptyBatch => write!(f, "batches can't be empty"), - WrongBatchResponseSize => write!(f, "too many responses returned in batch"), - } - } -} - -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - use self::Error::*; - - match *self { - Rpc(_) - | NonceMismatch - | VersionMismatch - | EmptyBatch - | WrongBatchResponseSize - | BatchDuplicateResponseId(_) - | WrongBatchResponseId(_) => None, - Transport(ref e) => Some(&**e), - Json(ref _e) => todo!(), //Some(e), - } - } -} - -/// Standard error responses, as described at at -/// -/// -/// # Documentation Copyright -/// Copyright (C) 2007-2010 by the JSON-RPC Working Group -/// -/// This document and translations of it may be used to implement JSON-RPC, it -/// may be copied and furnished to others, and derivative works that comment -/// on or otherwise explain it or assist in its implementation may be prepared, -/// copied, published and distributed, in whole or in part, without restriction -/// of any kind, provided that the above copyright notice and this paragraph -/// are included on all such copies and derivative works. However, this document -/// itself may not be modified in any way. -/// -/// The limited permissions granted above are perpetual and will not be revoked. -/// -/// This document and the information contained herein is provided "AS IS" and -/// ALL WARRANTIES, EXPRESS OR IMPLIED are DISCLAIMED, INCLUDING BUT NOT LIMITED -/// TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY -/// RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A -/// PARTICULAR PURPOSE. -/// -#[derive(Debug)] -pub enum StandardError { - /// Invalid JSON was received by the server. - /// An error occurred on the server while parsing the JSON text. - ParseError, - /// The JSON sent is not a valid Request object. - InvalidRequest, - /// The method does not exist / is not available. - MethodNotFound, - /// Invalid method parameter(s). - InvalidParams, - /// Internal JSON-RPC error. - InternalError, -} - -/// A JSONRPC error object -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct RpcError { - /// The integer identifier of the error - pub code: i32, - /// A string describing the error - pub message: String, - /// Additional data specific to the error - pub data: Option>, -} - -/// Create a standard error responses -pub fn standard_error( - code: StandardError, - data: Option>, -) -> RpcError { - match code { - StandardError::ParseError => RpcError { - code: -32700, - message: "Parse error".to_string(), - data, - }, - StandardError::InvalidRequest => RpcError { - code: -32600, - message: "Invalid Request".to_string(), - data, - }, - StandardError::MethodNotFound => RpcError { - code: -32601, - message: "Method not found".to_string(), - data, - }, - StandardError::InvalidParams => RpcError { - code: -32602, - message: "Invalid params".to_string(), - data, - }, - StandardError::InternalError => RpcError { - code: -32603, - message: "Internal error".to_string(), - data, - }, - } -} - -/// Converts a Rust `Result` to a JSONRPC response object -pub fn result_to_response( - result: Result, - id: serde_json::Value, -) -> Response { - match result { - Ok(data) => Response { - result: Some( - serde_json::value::RawValue::from_string(serde_json::to_string(&data).unwrap()) - .unwrap(), - ), - error: None, - id, - jsonrpc: Some(String::from("2.0")), - }, - Err(err) => Response { - result: None, - error: Some(err), - id, - jsonrpc: Some(String::from("2.0")), - }, - } -} - -#[cfg(test)] -mod tests { - use super::{ - result_to_response, standard_error, - StandardError::{InternalError, InvalidParams, InvalidRequest, MethodNotFound, ParseError}, - }; - use serde_json; - - #[test] - fn test_parse_error() { - let resp = result_to_response(Err(standard_error(ParseError, None)), From::from(1)); - assert!(resp.result.is_none()); - assert!(resp.error.is_some()); - assert_eq!(resp.id, serde_json::Value::from(1)); - assert_eq!(resp.error.unwrap().code, -32700); - } - - #[test] - fn test_invalid_request() { - let resp = result_to_response(Err(standard_error(InvalidRequest, None)), From::from(1)); - assert!(resp.result.is_none()); - assert!(resp.error.is_some()); - assert_eq!(resp.id, serde_json::Value::from(1)); - assert_eq!(resp.error.unwrap().code, -32600); - } - - #[test] - fn test_method_not_found() { - let resp = result_to_response(Err(standard_error(MethodNotFound, None)), From::from(1)); - assert!(resp.result.is_none()); - assert!(resp.error.is_some()); - assert_eq!(resp.id, serde_json::Value::from(1)); - assert_eq!(resp.error.unwrap().code, -32601); - } - - #[test] - fn test_invalid_params() { - let resp = result_to_response(Err(standard_error(InvalidParams, None)), From::from("123")); - assert!(resp.result.is_none()); - assert!(resp.error.is_some()); - assert_eq!(resp.id, serde_json::Value::from("123")); - assert_eq!(resp.error.unwrap().code, -32602); - } - - #[test] - fn test_internal_error() { - let resp = result_to_response(Err(standard_error(InternalError, None)), From::from(-1)); - assert!(resp.result.is_none()); - assert!(resp.error.is_some()); - assert_eq!(resp.id, serde_json::Value::from(-1)); - assert_eq!(resp.error.unwrap().code, -32603); - } -} diff --git a/roles/jd-server/vendored/rust-jsonrpc/src/http/mod.rs b/roles/jd-server/vendored/rust-jsonrpc/src/http/mod.rs deleted file mode 100644 index 5f400e102c..0000000000 --- a/roles/jd-server/vendored/rust-jsonrpc/src/http/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! HTTP transport modules. - -#[cfg(feature = "simple_http")] -pub mod simple_http; - -/// The default TCP port to use for connections. -/// Set to 8332, the default RPC port for bitcoind. -pub const DEFAULT_PORT: u16 = 8332; - -/// The Default SOCKS5 Port to use for proxy connection. -/// Set to 9050, the default RPC port for tor. -// Currently only used by `simple_http` module, here for consistency. -#[cfg(feature = "proxy")] -pub const DEFAULT_PROXY_PORT: u16 = 9050; diff --git a/roles/jd-server/vendored/rust-jsonrpc/src/http/simple_http.rs b/roles/jd-server/vendored/rust-jsonrpc/src/http/simple_http.rs deleted file mode 100644 index 7f0c55cd9d..0000000000 --- a/roles/jd-server/vendored/rust-jsonrpc/src/http/simple_http.rs +++ /dev/null @@ -1,872 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! This module implements a minimal and non standard conforming HTTP 1.0 -//! round-tripper that works with the bitcoind RPC server. This can be used -//! if minimal dependencies are a goal and synchronous communication is ok. - -#[cfg(feature = "proxy")] -use socks::Socks5Stream; -#[cfg(not(jsonrpc_fuzz))] -use std::net::TcpStream; -use std::{ - error, fmt, io, - io::{BufRead, BufReader, Read, Write}, - net, - net::{SocketAddr, ToSocketAddrs}, - num, - sync::{Arc, Mutex, MutexGuard}, - time::Duration, -}; - -#[cfg(feature = "proxy")] -use crate::http::DEFAULT_PROXY_PORT; -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; - -#[cfg(not(jsonrpc_fuzz))] -const DEFAULT_TIMEOUT: Duration = Duration::from_secs(15); - -#[cfg(jsonrpc_fuzz)] -const DEFAULT_TIMEOUT: Duration = Duration::from_millis(1); - -/// Simple HTTP transport that implements the necessary subset of HTTP for -/// running a bitcoind RPC client. -#[derive(Clone, Debug)] -pub struct SimpleHttpTransport { - addr: net::SocketAddr, - path: String, - timeout: Duration, - /// The value of the `Authorization` HTTP header. - basic_auth: Option, - #[cfg(feature = "proxy")] - proxy_addr: net::SocketAddr, - #[cfg(feature = "proxy")] - proxy_auth: Option<(String, String)>, - sock: Arc>>>, -} - -impl Default for SimpleHttpTransport { - fn default() -> Self { - SimpleHttpTransport { - addr: net::SocketAddr::new( - net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), - DEFAULT_PORT, - ), - path: "/".to_owned(), - timeout: DEFAULT_TIMEOUT, - basic_auth: None, - #[cfg(feature = "proxy")] - proxy_addr: net::SocketAddr::new( - net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), - DEFAULT_PROXY_PORT, - ), - #[cfg(feature = "proxy")] - proxy_auth: None, - sock: Arc::new(Mutex::new(None)), - } - } -} - -impl SimpleHttpTransport { - /// Constructs a new [`SimpleHttpTransport`] with default parameters. - pub fn new() -> Self { - SimpleHttpTransport::default() - } - - /// Returns a builder for [`SimpleHttpTransport`]. - pub fn builder() -> Builder { - Builder::new() - } - - /// Replaces the URL of the transport. - pub fn set_url(&mut self, url: &str) -> Result<(), Error> { - let url = check_url(url)?; - self.addr = url.0; - self.path = url.1; - Ok(()) - } - - /// Replaces only the path part of the URL. - pub fn set_url_path(&mut self, path: String) { - self.path = path; - } - - fn request(&self, req: impl serde::Serialize) -> Result - where - R: for<'a> serde::de::Deserialize<'a>, - { - match self.try_request(req) { - Ok(response) => Ok(response), - Err(err) => { - // No part of this codebase should panic, so unwrapping a mutex lock is fine - *self.sock.lock().expect("poisoned mutex") = None; - Err(err) - } - } - } - - #[cfg(feature = "proxy")] - fn fresh_socket(&self) -> Result { - let stream = if let Some((username, password)) = &self.proxy_auth { - Socks5Stream::connect_with_password( - self.proxy_addr, - self.addr, - username.as_str(), - password.as_str(), - )? - } else { - Socks5Stream::connect(self.proxy_addr, self.addr)? - }; - Ok(stream.into_inner()) - } - - #[cfg(not(feature = "proxy"))] - fn fresh_socket(&self) -> Result { - let stream = TcpStream::connect_timeout(&self.addr, self.timeout)?; - stream.set_read_timeout(Some(self.timeout))?; - stream.set_write_timeout(Some(self.timeout))?; - Ok(stream) - } - - fn try_request(&self, req: impl serde::Serialize) -> Result - where - R: for<'a> serde::de::Deserialize<'a>, - { - // No part of this codebase should panic, so unwrapping a mutex lock is fine - let mut sock_lock: MutexGuard> = self.sock.lock().expect("poisoned mutex"); - if sock_lock.is_none() { - *sock_lock = Some(BufReader::new(self.fresh_socket()?)); - }; - // In the immediately preceding block, we made sure that `sock` is non-`None`, - // so unwrapping here is fine. - let sock: &mut BufReader<_> = sock_lock.as_mut().unwrap(); - - // Serialize the body first so we can set the Content-Length header. - let body = serde_json::to_vec(&req)?; - - let mut request_bytes = Vec::new(); - - request_bytes.write_all(b"POST ")?; - request_bytes.write_all(self.path.as_bytes())?; - request_bytes.write_all(b" HTTP/1.1\r\n")?; - // Write headers - request_bytes.write_all(b"host: ")?; - request_bytes.write_all(self.addr.to_string().as_bytes())?; - request_bytes.write_all(b"\r\n")?; - request_bytes.write_all(b"Content-Type: application/json\r\n")?; - request_bytes.write_all(b"Content-Length: ")?; - request_bytes.write_all(body.len().to_string().as_bytes())?; - request_bytes.write_all(b"\r\n")?; - if let Some(ref auth) = self.basic_auth { - request_bytes.write_all(b"Authorization: ")?; - request_bytes.write_all(auth.as_ref())?; - request_bytes.write_all(b"\r\n")?; - } - // Write body - request_bytes.write_all(b"\r\n")?; - request_bytes.write_all(&body)?; - - // Send HTTP request - let write_success = sock.get_mut().write_all(request_bytes.as_slice()).is_ok() - && sock.get_mut().flush().is_ok(); - - // This indicates the socket is broken so let's retry the send once with a fresh socket - if !write_success { - *sock.get_mut() = self.fresh_socket()?; - sock.get_mut().write_all(request_bytes.as_slice())?; - sock.get_mut().flush()?; - } - - // Parse first HTTP response header line - let mut header_buf = String::new(); - let read_success = sock.read_line(&mut header_buf).is_ok(); - - // This is another possible indication that the socket is broken so let's retry the send once - // with a fresh socket IF the write attempt has not already experienced a failure - if (!read_success || header_buf.is_empty()) && write_success { - *sock.get_mut() = self.fresh_socket()?; - sock.get_mut().write_all(request_bytes.as_slice())?; - sock.get_mut().flush()?; - - sock.read_line(&mut header_buf)?; - } - - if header_buf.len() < 12 { - return Err(Error::HttpResponseTooShort { - actual: header_buf.len(), - needed: 12, - }); - } - if !header_buf.as_bytes()[..12].is_ascii() { - return Err(Error::HttpResponseNonAsciiHello( - header_buf.as_bytes()[..12].to_vec(), - )); - } - if !header_buf.starts_with("HTTP/1.1 ") { - return Err(Error::HttpResponseBadHello { - actual: header_buf[0..9].into(), - expected: "HTTP/1.1 ".into(), - }); - } - let response_code = match header_buf[9..12].parse::() { - Ok(n) => n, - Err(e) => return Err(Error::HttpResponseBadStatus(header_buf[9..12].into(), e)), - }; - - // Parse response header fields - let mut content_length = None; - loop { - header_buf.clear(); - sock.read_line(&mut header_buf)?; - if header_buf == "\r\n" { - break; - } - header_buf.make_ascii_lowercase(); - - const CONTENT_LENGTH: &str = "content-length: "; - if let Some(s) = header_buf.strip_prefix(CONTENT_LENGTH) { - content_length = Some( - s.trim() - .parse::() - .map_err(|e| Error::HttpResponseBadContentLength(s.into(), e))?, - ); - } - } - - if response_code == 401 { - // There is no body in a 401 response, so don't try to read it - return Err(Error::HttpErrorCode(response_code)); - } - - // Read up to `content_length` bytes. Note that if there is no content-length - // header, we will assume an effectively infinite content length, i.e. we will - // just keep reading from the socket until it is closed. - let reader = match content_length { - None => sock.take(FINAL_RESP_ALLOC), - Some(n) if n > FINAL_RESP_ALLOC => { - return Err(Error::HttpResponseContentLengthTooLarge { - length: n, - max: FINAL_RESP_ALLOC, - }); - } - Some(n) => sock.take(n), - }; - - let mut message_reader = BufReader::new(reader).lines(); - let message_r = message_reader.next().unwrap().unwrap(); - // Attempt to parse the response. Don't check the HTTP error code until - // after parsing, since Bitcoin Core will often return a descriptive JSON - // error structure which is more useful than the error code. - match serde_json::from_str(&message_r) { - Ok(s) => { - if content_length.is_some() { - message_reader.for_each(std::mem::drop); - //reader.bytes().count(); // consume any trailing bytes - } - Ok(s) - } - Err(e) => { - // If the response was not 200, assume the parse failed because of that - if response_code != 200 { - Err(Error::HttpErrorCode(response_code)) - } else { - // If it was 200 then probably it was legitimately a parse error - Err(e.into()) - } - } - } - } -} - -/// Does some very basic manual URL parsing because the uri/url crates -/// all have unicode-normalization as a dependency and that's broken. -fn check_url(url: &str) -> Result<(SocketAddr, String), Error> { - // The fallback port in case no port was provided. - // This changes when the http or https scheme was provided. - let mut fallback_port = DEFAULT_PORT; - - // We need to get the hostname and the port. - // (1) Split scheme - let after_scheme = { - let mut split = url.splitn(2, "://"); - let s = split.next().unwrap(); - match split.next() { - None => s, // no scheme present - Some(after) => { - // Check if the scheme is http or https. - if s == "http" { - fallback_port = 80; - } else if s == "https" { - fallback_port = 443; - } else { - return Err(Error::url(url, "scheme should be http or https")); - } - after - } - } - }; - // (2) split off path - let (before_path, path) = { - if let Some(slash) = after_scheme.find('/') { - (&after_scheme[0..slash], &after_scheme[slash..]) - } else { - (after_scheme, "/") - } - }; - // (3) split off auth part - let after_auth = { - let mut split = before_path.splitn(2, '@'); - let s = split.next().unwrap(); - split.next().unwrap_or(s) - }; - - // (4) Parse into socket address. - // At this point we either have or : - // `std::net::ToSocketAddrs` requires `&str` to have : format. - let mut addr = match after_auth.to_socket_addrs() { - Ok(addr) => addr, - Err(_) => { - // Invalid socket address. Try to add port. - format!("{}:{}", after_auth, fallback_port).to_socket_addrs()? - } - }; - - match addr.next() { - Some(a) => Ok((a, path.to_owned())), - None => Err(Error::url( - url, - "invalid hostname: error extracting socket address", - )), - } -} - -impl Transport for SimpleHttpTransport { - 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, - "http://{}:{}{}", - self.addr.ip(), - self.addr.port(), - self.path - ) - } -} - -/// Builder for simple bitcoind [`SimpleHttpTransport`]. -#[derive(Clone, Debug)] -pub struct Builder { - tp: SimpleHttpTransport, -} - -impl Builder { - /// Constructs a new [`Builder`] with default configuration. - pub fn new() -> Builder { - Builder { - tp: SimpleHttpTransport::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.set_url(url)?; - Ok(self) - } - - /// Adds authentication information to the transport. - pub fn auth>(mut self, user: S, pass: Option) -> Self { - let mut auth = user.as_ref().to_owned(); - auth.push(':'); - if let Some(ref pass) = pass { - auth.push_str(pass.as_ref()); - } - self.tp.basic_auth = Some(format!("Basic {}", &base64::encode(auth.as_bytes()))); - self - } - - /// 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 - } - - /// Adds proxy address to the transport for SOCKS5 proxy. - #[cfg(feature = "proxy")] - pub fn proxy_addr>(mut self, proxy_addr: S) -> Result { - // We don't expect path in proxy address. - self.tp.proxy_addr = check_url(proxy_addr.as_ref())?.0; - Ok(self) - } - - /// Adds optional proxy authentication as ('username', 'password'). - #[cfg(feature = "proxy")] - pub fn proxy_auth>(mut self, user: S, pass: S) -> Self { - self.tp.proxy_auth = - Some((user, pass)).map(|(u, p)| (u.as_ref().to_string(), p.as_ref().to_string())); - self - } - - /// Builds the final [`SimpleHttpTransport`]. - pub fn build(self) -> SimpleHttpTransport { - self.tp - } -} - -impl Default for Builder { - fn default() -> Self { - Builder::new() - } -} - -impl crate::Client { - /// Creates a new JSON-RPC client using a bare-minimum HTTP transport. - pub fn simple_http( - url: &str, - user: Option, - pass: Option, - ) -> Result { - let mut builder = Builder::new().url(url)?; - if let Some(user) = user { - builder = builder.auth(user, pass); - } - Ok(crate::Client::with_transport(builder.build())) - } - - /// Creates a new JSON_RPC client using a HTTP-Socks5 proxy transport. - #[cfg(feature = "proxy")] - pub fn http_proxy( - url: &str, - user: Option, - pass: Option, - proxy_addr: &str, - proxy_auth: Option<(&str, &str)>, - ) -> Result { - let mut builder = Builder::new().url(url)?; - if let Some(user) = user { - builder = builder.auth(user, pass); - } - builder = builder.proxy_addr(proxy_addr)?; - if let Some((user, pass)) = proxy_auth { - builder = builder.proxy_auth(user, pass); - } - let tp = builder.build(); - Ok(crate::Client::with_transport(tp)) - } -} - -/// Error that can happen when sending requests. -#[derive(Debug)] -pub enum Error { - /// An invalid URL was passed. - InvalidUrl { - /// The URL passed. - url: String, - /// The reason the URL is invalid. - reason: &'static str, - }, - /// An error occurred on the socket layer. - SocketError(io::Error), - /// The HTTP response was too short to even fit a HTTP 1.1 header. - HttpResponseTooShort { - /// The total length of the response. - actual: usize, - /// Minimum length we can parse. - needed: usize, - }, - /// The HTTP response started with a HTTP/1.1 line which was not ASCII. - HttpResponseNonAsciiHello(Vec), - /// The HTTP response did not start with HTTP/1.1 - HttpResponseBadHello { - /// Actual HTTP-whatever string. - actual: String, - /// The hello string of the HTTP version we support. - expected: String, - }, - /// Could not parse the status value as a number. - HttpResponseBadStatus(String, num::ParseIntError), - /// Could not parse the status value as a number. - HttpResponseBadContentLength(String, num::ParseIntError), - /// The indicated content-length header exceeded our maximum. - HttpResponseContentLengthTooLarge { - /// The length indicated in the content-length header. - length: u64, - /// Our hard maximum on number of bytes we'll try to read. - max: u64, - }, - /// Unexpected HTTP error code (non-200). - HttpErrorCode(u16), - /// Received EOF before getting as many bytes as were indicated by the content-length header. - IncompleteResponse { - /// The content-length header. - content_length: u64, - /// The number of bytes we actually read. - n_read: u64, - }, - /// JSON parsing error. - Json(serde_json::Error), -} - -impl Error { - /// Utility method to create [`Error::InvalidUrl`] variants. - fn url>(url: U, reason: &'static str) -> Error { - Error::InvalidUrl { - url: url.into(), - reason, - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - use Error::*; - - match *self { - InvalidUrl { - ref url, - ref reason, - } => write!(f, "invalid URL '{}': {}", url, reason), - SocketError(ref e) => write!(f, "Couldn't connect to host: {}", e), - HttpResponseTooShort { - ref actual, - ref needed, - } => { - write!( - f, - "HTTP response too short: length {}, needed {}.", - actual, needed - ) - } - HttpResponseNonAsciiHello(ref bytes) => { - write!(f, "HTTP response started with non-ASCII {:?}", bytes) - } - HttpResponseBadHello { - ref actual, - ref 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 - ) - } - HttpResponseBadContentLength(ref len, ref 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 - ) - } - HttpErrorCode(c) => write!(f, "unexpected HTTP code: {}", c), - IncompleteResponse { - content_length, - n_read, - } => { - write!( - f, - "read {} bytes but HTTP response content-length header was {}.", - n_read, content_length - ) - } - 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 { - InvalidUrl { .. } - | HttpResponseTooShort { .. } - | HttpResponseNonAsciiHello(..) - | HttpResponseBadHello { .. } - | HttpResponseBadStatus(..) - | HttpResponseBadContentLength(..) - | HttpResponseContentLengthTooLarge { .. } - | HttpErrorCode(_) - | IncompleteResponse { .. } => None, - SocketError(ref e) => Some(e), - 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)), - } - } -} - -/// 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(()) - } - } - - impl TcpStream { - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - Ok(TcpStream) - } - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - Ok(()) - } - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - Ok(()) - } - } -} - -#[cfg(test)] -mod tests { - use std::net; - #[cfg(feature = "proxy")] - use std::str::FromStr; - - use super::*; - use crate::Client; - - #[test] - fn test_urls() { - let addr: net::SocketAddr = ("localhost", 22).to_socket_addrs().unwrap().next().unwrap(); - let urls = [ - "localhost:22", - "http://localhost:22/", - "https://localhost:22/walletname/stuff?it=working", - "http://me:weak@localhost:22/wallet", - ]; - for u in &urls { - let tp = Builder::new().url(u).unwrap().build(); - assert_eq!(tp.addr, addr); - } - - // Default port and 80 and 443 fill-in. - 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 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 tp = Builder::new().url("localhost").unwrap().build(); - assert_eq!(tp.addr, addr); - - let valid_urls = [ - "localhost", - "127.0.0.1:8080", - "http://127.0.0.1:8080/", - "http://127.0.0.1:8080/rpc/test", - "https://127.0.0.1/rpc/test", - "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8300", - "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", - ]; - 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)); - 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() - ); - } - - let invalid_urls = [ - "127.0.0.1.0:8080", - "httpx://127.0.0.1:8080/", - "ftp://127.0.0.1:8080/rpc/test", - "http://127.0.0./rpc/test", - // NB somehow, Rust's IpAddr accepts "127.0.0" and adds the extra 0.. - ]; - for u in &invalid_urls { - if let Ok(b) = Builder::new().url(u) { - let tp = b.build(); - panic!("expected error for url {}, got {:?}", u, tp); - } - } - } - - #[test] - fn construct() { - let tp = Builder::new() - .timeout(Duration::from_millis(100)) - .url("localhost:22") - .unwrap() - .auth("user", None) - .build(); - let _ = Client::with_transport(tp); - - let _ = Client::simple_http("localhost:22", None, None).unwrap(); - } - - #[cfg(feature = "proxy")] - #[test] - fn construct_with_proxy() { - let tp = Builder::new() - .timeout(Duration::from_millis(100)) - .url("localhost:22") - .unwrap() - .auth("user", None) - .proxy_addr("127.0.0.1:9050") - .unwrap() - .build(); - let _ = Client::with_transport(tp); - - let _ = Client::http_proxy( - "localhost:22", - None, - None, - "127.0.0.1:9050", - Some(("user", "password")), - ) - .unwrap(); - } - - /// Test that the client will detect that a socket is closed and open a fresh one before sending - /// the request - #[cfg(all(not(feature = "proxy"), not(jsonrpc_fuzz)))] - #[test] - fn request_to_closed_socket() { - use serde_json::{Number, Value}; - use std::{ - net::{Shutdown, TcpListener}, - sync::mpsc, - thread, - }; - - let (tx, rx) = mpsc::sync_channel(1); - - thread::spawn(move || { - let server = TcpListener::bind("localhost:0").expect("Binding a Tcp Listener"); - tx.send(server.local_addr().unwrap().port()).unwrap(); - for (request_id, stream) in server.incoming().enumerate() { - let mut stream = stream.unwrap(); - - let buf_reader = BufReader::new(&mut stream); - - let _http_request: Vec<_> = buf_reader - .lines() - .map(|result| result.unwrap()) - .take_while(|line| !line.is_empty()) - .collect(); - - let response = Response { - result: None, - error: None, - id: Value::Number(Number::from(request_id)), - jsonrpc: Some(String::from("2.0")), - }; - let response_str = serde_json::to_string(&response).unwrap(); - - 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(b"\r\n").unwrap(); - stream.write_all(b"\r\n").unwrap(); - stream.write_all(response_str.as_bytes()).unwrap(); - stream.flush().unwrap(); - - stream.shutdown(Shutdown::Both).unwrap(); - } - }); - - // Give the server thread a second to start up and listen - thread::sleep(Duration::from_secs(1)); - - let port = rx.recv().unwrap(); - let client = - Client::simple_http(format!("localhost:{}", port).as_str(), None, None).unwrap(); - let request = client.build_request("test_request", &[]); - let result = client.send_request(request).unwrap(); - assert_eq!(result.id, Value::Number(Number::from(0))); - thread::sleep(Duration::from_secs(1)); - let request = client.build_request("test_request2", &[]); - let result2 = client.send_request(request) - .expect("This second request should not be an Err like `Err(Transport(HttpResponseTooShort { actual: 0, needed: 12 }))`"); - assert_eq!(result2.id, Value::Number(Number::from(1))); - } -} diff --git a/roles/jd-server/vendored/rust-jsonrpc/src/lib.rs b/roles/jd-server/vendored/rust-jsonrpc/src/lib.rs deleted file mode 100644 index c3b27245cb..0000000000 --- a/roles/jd-server/vendored/rust-jsonrpc/src/lib.rs +++ /dev/null @@ -1,207 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! # Rust JSON-RPC Library -//! -//! Rust support for the JSON-RPC 2.0 protocol. - -#![cfg_attr(docsrs, feature(doc_auto_cfg))] -// Coding conventions -#![warn(missing_docs)] - -/// Re-export `serde` crate. -pub extern crate serde; -/// Re-export `serde_json` crate. -pub extern crate serde_json; - -pub mod client; -pub mod error; -pub mod http; - -#[cfg(feature = "simple_http")] -pub use http::simple_http; - -use serde::{Deserialize, Serialize}; -use serde_json::value::RawValue; - -pub use crate::{ - client::{Client, Transport}, - error::Error, -}; - -/// Shorthand method to convert an argument into a boxed [`serde_json::value::RawValue`]. -/// -/// Since serializers rarely fail, it's probably easier to use [`arg`] instead. -pub fn try_arg(arg: T) -> Result, serde_json::Error> { - RawValue::from_string(serde_json::to_string(&arg)?) -} - -/// Shorthand method to convert an argument into a boxed [`serde_json::value::RawValue`]. -/// -/// This conversion should not fail, so to avoid returning a [`Result`], -/// in case of an error, the error is serialized as the return value. -pub fn arg(arg: T) -> Box { - match try_arg(arg) { - Ok(v) => v, - Err(e) => RawValue::from_string(format!("<>", e)) - .unwrap_or_else(|_| { - RawValue::from_string("<>".to_owned()).unwrap() - }), - } -} - -/// A JSONRPC request object. -#[derive(Debug, Clone, Serialize)] -pub struct Request<'a> { - /// The name of the RPC call. - pub method: &'a str, - /// Parameters to the RPC call. - pub params: &'a [Box], - /// Identifier for this request, which should appear in the response. - pub id: serde_json::Value, - /// jsonrpc field, MUST be "2.0". - pub jsonrpc: Option<&'a str>, -} - -/// A JSONRPC response object. -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Response { - /// A result if there is one, or [`None`]. - pub result: Option>, - /// An error if there is one, or [`None`]. - pub error: Option, - /// Identifier for this response, which should match that of the request. - pub id: serde_json::Value, - /// jsonrpc field, MUST be "2.0". - pub jsonrpc: Option, -} - -impl Response { - /// Extracts the result from a response. - pub fn result serde::de::Deserialize<'a>>(&self) -> Result { - if let Some(ref e) = self.error { - return Err(Error::Rpc(e.clone())); - } - - if let Some(ref res) = self.result { - serde_json::from_str(res.get()).map_err(Error::Json) - } else { - serde_json::from_value(serde_json::Value::Null).map_err(Error::Json) - } - } - - /// Returns the RPC error, if there was one, but does not check the result. - pub fn check_error(self) -> Result<(), Error> { - if let Some(e) = self.error { - Err(Error::Rpc(e)) - } else { - Ok(()) - } - } - - /// Returns whether or not the `result` field is empty. - pub fn is_none(&self) -> bool { - self.result.is_none() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use serde_json::value::RawValue; - - #[test] - fn response_is_none() { - let joanna = Response { - result: Some(RawValue::from_string(serde_json::to_string(&true).unwrap()).unwrap()), - error: None, - id: From::from(81), - jsonrpc: Some(String::from("2.0")), - }; - - let bill = Response { - result: None, - error: None, - id: From::from(66), - jsonrpc: Some(String::from("2.0")), - }; - - assert!(!joanna.is_none()); - assert!(bill.is_none()); - } - - #[test] - fn response_extract() { - let obj = vec!["Mary", "had", "a", "little", "lamb"]; - let response = Response { - result: Some(RawValue::from_string(serde_json::to_string(&obj).unwrap()).unwrap()), - error: None, - id: serde_json::Value::Null, - jsonrpc: Some(String::from("2.0")), - }; - let recovered1: Vec = response.result().unwrap(); - assert!(response.clone().check_error().is_ok()); - let recovered2: Vec = response.result().unwrap(); - assert_eq!(obj, recovered1); - assert_eq!(obj, recovered2); - } - - #[test] - fn null_result() { - let s = r#"{"result":null,"error":null,"id":"test"}"#; - let response: Response = serde_json::from_str(s).unwrap(); - let recovered1: Result<(), _> = response.result(); - let recovered2: Result<(), _> = response.result(); - assert!(recovered1.is_ok()); - assert!(recovered2.is_ok()); - - let recovered1: Result = response.result(); - let recovered2: Result = response.result(); - assert!(recovered1.is_err()); - assert!(recovered2.is_err()); - } - - #[test] - fn batch_response() { - // from the jsonrpc.org spec example - let s = r#"[ - {"jsonrpc": "2.0", "result": 7, "id": "1"}, - {"jsonrpc": "2.0", "result": 19, "id": "2"}, - {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}, - {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"}, - {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"} - ]"#; - let batch_response: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(batch_response.len(), 5); - } - - #[test] - fn test_arg() { - macro_rules! test_arg { - ($val:expr, $t:ty) => {{ - let val1: $t = $val; - let arg = super::arg(val1.clone()); - let val2: $t = serde_json::from_str(arg.get()).expect(stringify!($val)); - assert_eq!(val1, val2, "failed test for {}", stringify!($val)); - }}; - } - - test_arg!(true, bool); - test_arg!(42, u8); - test_arg!(42, usize); - test_arg!(42, isize); - test_arg!(vec![42, 35], Vec); - test_arg!(String::from("test"), String); - - #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] - struct Test { - v: String, - } - test_arg!( - Test { - v: String::from("test"), - }, - Test - ); - } -} diff --git a/utils/Cargo.lock b/utils/Cargo.lock index b7d0aa18f6..e7402a554e 100644 --- a/utils/Cargo.lock +++ b/utils/Cargo.lock @@ -52,6 +52,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" + [[package]] name = "aho-corasick" version = "1.1.2" @@ -79,7 +85,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" dependencies = [ "concurrent-queue", - "event-listener 4.0.1", + "event-listener 4.0.3", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -91,11 +97,11 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ - "async-lock 3.2.0", + "async-lock 3.3.0", "async-task", "concurrent-queue", "fastrand 2.0.1", - "futures-lite 2.1.0", + "futures-lite 2.2.0", "slab", ] @@ -107,10 +113,10 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.1.1", "async-executor", - "async-io 2.2.2", - "async-lock 3.2.0", + "async-io 2.3.0", + "async-lock 3.3.0", "blocking", - "futures-lite 2.1.0", + "futures-lite 2.2.0", "once_cell", ] @@ -136,18 +142,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.2.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" +checksum = "fb41eb19024a91746eba0773aa5e16036045bbf45733766661099e182ea6a744" dependencies = [ - "async-lock 3.2.0", + "async-lock 3.3.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.1.0", + "futures-lite 2.2.0", "parking", - "polling 3.3.1", - "rustix 0.38.28", + "polling 3.3.2", + "rustix 0.38.30", "slab", "tracing", "windows-sys 0.52.0", @@ -164,11 +170,11 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ - "event-listener 4.0.1", + "event-listener 4.0.3", "event-listener-strategy", "pin-project-lite", ] @@ -201,20 +207,9 @@ dependencies = [ [[package]] name = "async-task" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d90cd0b264dfdd8eb5bad0a2c217c1f88fa96a8573f40e7b12de23fb468f46" - -[[package]] -name = "async-trait" -version = "0.1.74" +version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "atomic-waker" @@ -295,9 +290,18 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "block-buffer" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] [[package]] name = "blocking" @@ -306,15 +310,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ "async-channel 2.1.1", - "async-lock 3.2.0", + "async-lock 3.3.0", "async-task", "fastrand 2.0.1", "futures-io", - "futures-lite 2.1.0", + "futures-lite 2.2.0", "piper", "tracing", ] +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +dependencies = [ + "sha2", +] + [[package]] name = "buffer_sv2" version = "0.1.2" @@ -433,9 +446,9 @@ version = "0.1.2" [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -478,35 +491,28 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.16" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset", ] [[package]] name = "crossbeam-utils" -version = "0.8.17" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-common" @@ -556,6 +562,15 @@ dependencies = [ "binary_codec_sv2", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "either" version = "1.9.0" @@ -584,9 +599,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "4.0.1" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f2cdcf274580f2d63697192d744727b3198894b1bf02923643bf59e2c26712" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" dependencies = [ "concurrent-queue", "parking", @@ -599,7 +614,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 4.0.1", + "event-listener 4.0.3", "pin-project-lite", ] @@ -630,9 +645,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -645,9 +660,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -655,15 +670,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -672,9 +687,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" @@ -693,9 +708,9 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" +checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" dependencies = [ "fastrand 2.0.1", "futures-core", @@ -706,9 +721,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", @@ -717,21 +732,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -757,9 +772,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -800,6 +815,16 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hashbrown" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" +dependencies = [ + "ahash", + "autocfg", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -811,9 +836,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" [[package]] name = "iai" @@ -845,7 +870,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.4", "libc", "windows-sys 0.48.0", ] @@ -867,13 +892,23 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] +[[package]] +name = "key-utils" +version = "1.0.0" +dependencies = [ + "bs58", + "secp256k1", + "serde", + "toml", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -891,9 +926,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "linux-raw-sys" @@ -903,9 +938,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -928,18 +963,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "miniz_oxide" @@ -967,7 +993,6 @@ version = "0.1.0" dependencies = [ "async-channel 1.9.0", "async-std", - "async-trait", "binary_sv2", "codec_sv2", "futures", @@ -1003,15 +1028,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.4", "libc", ] [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -1132,14 +1157,14 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.1" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +checksum = "545c980a3880efd47b2e262f6a4bb6daad6555cf3367aa9c4e52895f69537a41" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.28", + "rustix 0.38.30", "tracing", "windows-sys 0.52.0", ] @@ -1175,18 +1200,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1223,9 +1248,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" dependencies = [ "either", "rayon-core", @@ -1233,9 +1258,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -1252,9 +1277,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", @@ -1264,9 +1289,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" dependencies = [ "aho-corasick", "memchr", @@ -1301,14 +1326,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", - "linux-raw-sys 0.4.12", + "linux-raw-sys 0.4.13", "windows-sys 0.52.0", ] @@ -1355,9 +1380,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] @@ -1374,9 +1399,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", @@ -1385,9 +1410,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", @@ -1402,6 +1427,19 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1422,9 +1460,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" @@ -1454,9 +1492,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.41" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -1512,6 +1550,15 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.5.6" +source = "git+https://github.com/diondokter/toml-rs?rev=c4161aa#c4161aa70202b3992dbec79b76e7a8659713b604" +dependencies = [ + "hashbrown", + "serde", +] + [[package]] name = "tracing" version = "0.1.40" @@ -1573,9 +1620,9 @@ dependencies = [ [[package]] name = "value-bag" -version = "1.4.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe" +checksum = "7cdbaf5e132e593e9fc1de6a15bbec912395b11fb9719e061cf64f804524c503" [[package]] name = "version_check" @@ -1607,9 +1654,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1617,9 +1664,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", @@ -1632,9 +1679,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" dependencies = [ "cfg-if", "js-sys", @@ -1644,9 +1691,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1654,9 +1701,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", @@ -1667,15 +1714,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen",