diff --git a/src/rpc/adapter/rawtransactions.rs b/src/rpc/adapter/rawtransactions.rs index c8d6007..397b2d9 100644 --- a/src/rpc/adapter/rawtransactions.rs +++ b/src/rpc/adapter/rawtransactions.rs @@ -2,28 +2,88 @@ use crate::utils::encode_to_hex; use crate::Client; -use bitcoin::{consensus::encode::deserialize_hex, BlockHash, Transaction, Txid}; +use bitcoin::{consensus::encode::deserialize_hex, hex::DisplayHex, BlockHash, Transaction, Txid}; use bitcoincore_rpc::{Error, RpcApi}; +use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; use std::str::FromStr; +#[derive(Clone, PartialEq, Eq, Debug, Deserialize)] +pub enum GetrawtransactionReturn { + NoneVerbose(String), + Verbose(Box), +} +impl Serialize for GetrawtransactionReturn { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + GetrawtransactionReturn::NoneVerbose(stri) => serializer.serialize_str(stri), + GetrawtransactionReturn::Verbose(strct) => { + let mut state = serializer.serialize_struct("GetRawTransactionResult", 14)?; + + if strct.in_active_chain.is_some() { + state.serialize_field("in_active_chain", &strct.in_active_chain)?; + } + state + .serialize_field("hex", &strct.hex.to_hex_string(bitcoin::hex::Case::Lower))?; + state.serialize_field("txid", &strct.txid)?; + state.serialize_field("hash", &strct.hash)?; + state.serialize_field("size", &strct.size)?; + state.serialize_field("vsize", &strct.vsize)?; + state.serialize_field("version", &strct.version)?; + state.serialize_field("locktime", &strct.locktime)?; + + #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] + struct Vin { + sequence: u32, + } + let vins: Vec = strct + .vin + .iter() + .map(|vin| Vin { + sequence: vin.sequence, + }) + .collect(); + state.serialize_field("vin", &vins)?; + + state.serialize_field("vout", &strct.vout)?; + if strct.blockhash.is_some() { + state.serialize_field("blockhash", &strct.blockhash)?; + } + if strct.confirmations.is_some() { + state.serialize_field("confirmations", &strct.confirmations)?; + } + if strct.time.is_some() { + state.serialize_field("time", &strct.time)?; + } + if strct.blocktime.is_some() { + state.serialize_field("blocktime", &strct.blocktime)?; + } + state.end() + } + } + } +} pub fn getrawtransaction( client: &Client, txid: String, verbose: Option, blockhash: Option, -) -> Result { +) -> Result { let txid = Txid::from_str(&txid).unwrap(); - let res: String = match verbose { + let res: GetrawtransactionReturn = match verbose { None | Some(false) => { let tx = client.get_raw_transaction(&txid, blockhash.as_ref())?; - encode_to_hex(&tx) + GetrawtransactionReturn::NoneVerbose(encode_to_hex(&tx)) } Some(true) => { - let tx = client.get_raw_transaction_info(&txid, blockhash.as_ref())?; + let tx: bitcoincore_rpc::json::GetRawTransactionResult = + client.get_raw_transaction_info(&txid, blockhash.as_ref())?; - serde_json::to_string_pretty(&tx).unwrap() + GetrawtransactionReturn::Verbose(Box::new(tx)) } }; @@ -67,11 +127,13 @@ pub fn signrawtransactionwithwallet( mod tests { use crate::{ ledger, + rpc::adapter::GetrawtransactionReturn, utils::{decode_from_hex, encode_to_hex}, Client, RpcApiWrapper, }; use bitcoin::{ - absolute::LockTime, transaction::Version, Amount, OutPoint, Transaction, TxIn, TxOut, Txid, + absolute::LockTime, consensus::Decodable, transaction::Version, Amount, OutPoint, + Transaction, TxIn, TxOut, Txid, }; use bitcoincore_rpc::RpcApi; @@ -96,9 +158,45 @@ mod tests { let tx = client.get_raw_transaction(&txid, None).unwrap(); let encoded_tx = super::getrawtransaction(&client, txid.to_string(), None, None).unwrap(); - let encoded_tx = decode_from_hex(encoded_tx).unwrap(); - assert_eq!(tx, encoded_tx); + if let GetrawtransactionReturn::NoneVerbose(encoded_tx) = encoded_tx { + assert_eq!(tx, decode_from_hex(encoded_tx).unwrap()); + } else { + panic!(""); + } + } + + #[test] + fn getrawtransactionverbose() { + let client = Client::new("getrawtransaction", bitcoincore_rpc::Auth::None).unwrap(); + + let address = client.get_new_address(None, None).unwrap().assume_checked(); + let txid = client + .send_to_address( + &address, + Amount::from_sat(0x45), + None, + None, + None, + None, + None, + None, + ) + .unwrap(); + + let tx = client.get_raw_transaction(&txid, None).unwrap(); + + let encoded_tx = + super::getrawtransaction(&client, txid.to_string(), Some(true), None).unwrap(); + + if let GetrawtransactionReturn::Verbose(encoded_tx) = encoded_tx { + assert_eq!( + tx, + Transaction::consensus_decode(&mut encoded_tx.hex.as_slice()).unwrap() + ); + } else { + panic!("Should be verbose variant"); + } } #[test] diff --git a/src/rpc/traits.rs b/src/rpc/traits.rs index 7080ebe..d20c2e7 100644 --- a/src/rpc/traits.rs +++ b/src/rpc/traits.rs @@ -3,9 +3,10 @@ //! This crate implements [`jsonrpsee`] traits, using [`adapter`] functions. //! This is the entry point for the RPC calls. -use super::adapter; +use super::adapter::{self, GetrawtransactionReturn}; use crate::Client; -use bitcoin::BlockHash; +use bitcoin::{BlockHash, Txid}; +use bitcoincore_rpc::json::GetTransactionResult; use jsonrpsee::core::async_trait; use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::ErrorObjectOwned; @@ -57,7 +58,7 @@ pub trait Rpc { txid: String, verbose: Option, blockhash: Option, - ) -> Result; + ) -> Result; #[method(name = "sendrawtransaction")] async fn sendrawtransaction( @@ -79,7 +80,7 @@ pub trait Rpc { txid: String, include_watchonly: Option, verbose: Option, - ) -> Result; + ) -> Result; #[method(name = "sendtoaddress")] async fn sendtoaddress( @@ -93,7 +94,7 @@ pub trait Rpc { conf_target: Option, estimate_mode: Option<&str>, avoid_reuse: Option, - ) -> Result; + ) -> Result; #[method(name = "fundrawtransaction")] async fn fundrawtransaction( @@ -165,7 +166,7 @@ impl RpcServer for Client { txid: String, verbose: Option, blockhash: Option, - ) -> Result { + ) -> Result { to_jsonrpsee_error(adapter::getrawtransaction(self, txid, verbose, blockhash)) } @@ -190,7 +191,7 @@ impl RpcServer for Client { txid: String, include_watchonly: Option, verbose: Option, - ) -> Result { + ) -> Result { to_jsonrpsee_error(adapter::gettransaction( self, txid, @@ -210,7 +211,7 @@ impl RpcServer for Client { conf_target: Option, estimate_mode: Option<&str>, avoid_reuse: Option, - ) -> Result { + ) -> Result { to_jsonrpsee_error(adapter::sendtoaddress( self, address,