depolymerization

wire gateway for Bitcoin/Ethereum
Log | Files | Refs | Submodules | README | LICENSE

commit e11c589766c1fc8df0245d190213aa2d8791beb2
parent 92c4d2638fd272de0b60d759bdbc9f1ada01e27c
Author: Antoine A <>
Date:   Fri, 17 Dec 2021 14:17:19 +0100

btc-wire: rpc cleanup

Diffstat:
MCargo.lock | 13+------------
Mbtc-wire/Cargo.toml | 5++++-
Mbtc-wire/src/bin/btc-wire-cli.rs | 12++++++------
Mbtc-wire/src/bin/test.rs | 43++++++++++++++++++++-----------------------
Mbtc-wire/src/lib.rs | 17+++++++----------
Mbtc-wire/src/main.rs | 45+++++++++++++++++++++------------------------
Mbtc-wire/src/rpc.rs | 178+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mbtc-wire/src/rpc_utils.rs | 34+++++++++++++---------------------
8 files changed, 168 insertions(+), 179 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -120,17 +120,6 @@ dependencies = [ ] [[package]] -name = "bitcoincore-rpc-json" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce91de73c61f5776cf938bfa88378c5b404a70e3369b761dacbe6024fea79dd" -dependencies = [ - "bitcoin", - "serde", - "serde_json", -] - -[[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -164,7 +153,7 @@ dependencies = [ "argh", "base64", "bech32", - "bitcoincore-rpc-json", + "bitcoin", "criterion", "fastrand", "owo-colors", diff --git a/btc-wire/Cargo.toml b/btc-wire/Cargo.toml @@ -9,7 +9,10 @@ fail = [] [dependencies] # Typed bitcoin rpc types -bitcoincore-rpc-json = "0.14.0" +bitcoin = { version = "0.27.1", features = [ + "std", + "use-serde", +], default-features = false } # Cli args argh = "0.1.6" # Bech32 encoding and decoding diff --git a/btc-wire/src/bin/btc-wire-cli.rs b/btc-wire/src/bin/btc-wire-cli.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; -use bitcoincore_rpc_json::bitcoin::{Address, Amount}; +use bitcoin::{Address, Amount, Network}; use btc_wire::{ rpc::BtcRpc, - rpc_utils::{default_data_dir, Network}, + rpc_utils::{default_data_dir, rpc_dir}, test::rand_key, }; @@ -75,7 +75,7 @@ struct App { impl App { pub fn start(data_dir: Option<PathBuf>) -> Self { let data_dir = data_dir.unwrap_or_else(default_data_dir); - let network = Network::RegTest; + let network = Network::Regtest; let client = BtcRpc::common(&data_dir, network); Self { @@ -97,7 +97,7 @@ impl App { pub fn next_block(&self, wallet: &str) { match self.network { - Network::RegTest => { + Network::Regtest => { // Manually mine a block let (_, addr) = self.auto_wallet(wallet); self.client.generate(1, &addr).unwrap(); @@ -113,7 +113,7 @@ impl App { for wallet in ["wire", "client", "reserve"] { self.client.create_wallet(wallet).ok(); } - if self.network == Network::RegTest { + if self.network == Network::Regtest { let (client, client_addr) = self.auto_wallet("client"); client.generate(101, &client_addr).unwrap(); } @@ -128,7 +128,7 @@ fn main() { let path = args .datadir .unwrap_or_else(default_data_dir) - .join(Network::RegTest.dir()); + .join(rpc_dir(Network::Regtest)); if path.exists() { std::fs::remove_dir_all(path).unwrap(); } diff --git a/btc-wire/src/bin/test.rs b/btc-wire/src/bin/test.rs @@ -1,13 +1,10 @@ use core::panic; use std::{collections::HashSet, iter::repeat_with, panic::AssertUnwindSafe}; -use bitcoincore_rpc_json::{ - bitcoin::{Address, Amount, Txid}, - GetTransactionResultDetailCategory as Category, -}; +use bitcoin::{Address, Amount, Network, Txid}; use btc_wire::{ - rpc::{self, BtcRpc}, - rpc_utils::{default_data_dir, Network, CLIENT, WIRE}, + rpc::{self, BtcRpc, Category}, + rpc_utils::{default_data_dir, rpc_dir, CLIENT, WIRE}, test::rand_key, BounceErr, }; @@ -20,19 +17,21 @@ pub fn main() { let test_amount = Amount::from_sat(1500); let data_dir = default_data_dir(); // Network check - let network = Network::RegTest; + let network = Network::Bitcoin; match network { - Network::MainNet => { + Network::Bitcoin => { panic!("Do not run tests on the mainnet, you are going to loose money") } - Network::TestNet => println!("{}", "Running on testnet, slow network mining".yellow()), - Network::RegTest => println!("Running on regtest, fast manual mining"), + Network::Testnet | Network::Signet => { + println!("{}", "Running on testnet, slow network mining".yellow()) + } + Network::Regtest => println!("Running on regtest, fast manual mining"), } // Wallet check { let existing_wallets: HashSet<String> = - std::fs::read_dir(data_dir.join(network.dir()).join("wallets")) + std::fs::read_dir(data_dir.join(rpc_dir(network)).join("wallets")) .unwrap() .filter_map(|it| it.ok()) .map(|it| it.file_name().to_string_lossy().to_string()) @@ -74,7 +73,7 @@ pub fn main() { let next_block = || { match network { - Network::RegTest => { + Network::Regtest => { // Manually mine a block reserve_rpc.generate(1, &reserve_addr).unwrap(); } @@ -89,7 +88,7 @@ pub fn main() { let mut count = 0; while txs .iter() - .any(|id| rpc.get_tx(id).unwrap().tx.info.confirmations <= 0) + .any(|id| rpc.get_tx(id).unwrap().confirmations <= 0) { next_block(); if count > 3 { @@ -122,8 +121,8 @@ pub fn main() { .yellow() ); match network { - Network::MainNet | Network::TestNet => { - if network == Network::MainNet { + Network::Bitcoin | Network::Testnet | Network::Signet => { + if network == Network::Bitcoin { println!("Send coins to this address: {}", client_addr); } else { println!("Request coins from a faucet such as https://bitcoinfaucet.uo1.net/send.php to this address: {}", client_addr); @@ -133,7 +132,7 @@ pub fn main() { client_rpc.wait_for_new_block(0).ok(); } } - Network::RegTest => { + Network::Regtest => { println!("Add 50B to client wallet"); client_rpc .generate(101 /* Need 100 blocks to validate */, &reserve_addr) @@ -207,13 +206,13 @@ pub fn main() { wait_for_tx(&client_rpc, &[send_id]); let bounce_id = wire_rpc.bounce(&send_id, &bounce_fee).unwrap(); wait_for_tx(&wire_rpc, &[bounce_id]); - let bounce_tx_fee = wire_rpc.get_tx(&bounce_id).unwrap().tx.details[0] + let bounce_tx_fee = wire_rpc.get_tx(&bounce_id).unwrap().details[0] .fee .unwrap() .abs() .to_unsigned() .unwrap(); - let send_tx_fee = client_rpc.get_tx(&send_id).unwrap().tx.details[0] + let send_tx_fee = client_rpc.get_tx(&send_id).unwrap().details[0] .fee .unwrap() .abs() @@ -275,13 +274,13 @@ pub fn main() { let bounce_id = wire_rpc.bounce(&send_id, &bounce_fee).unwrap(); wait_for_tx(&wire_rpc, &[bounce_id]); let after = client_rpc.get_balance().unwrap(); - let bounce_tx_fee = wire_rpc.get_tx(&bounce_id).unwrap().tx.details[0] + let bounce_tx_fee = wire_rpc.get_tx(&bounce_id).unwrap().details[0] .fee .unwrap() .abs() .to_unsigned() .unwrap(); - let send_tx_fee = client_rpc.get_tx(&send_id).unwrap().tx.details[0] + let send_tx_fee = client_rpc.get_tx(&send_id).unwrap().details[0] .fee .unwrap() .abs() @@ -298,9 +297,7 @@ pub fn main() { fn tx_exist(rpc: &BtcRpc, id: &Txid, min_confirmation: i32, detail: Category) -> rpc::Result<bool> { let result = rpc.list_since_block(None, 1, false).unwrap(); let found = result.transactions.into_iter().any(|tx| { - tx.detail.category == detail - && tx.info.confirmations >= min_confirmation - && tx.info.txid == *id + tx.detail.category == detail && tx.confirmations >= min_confirmation && tx.txid == *id }); Ok(found) } diff --git a/btc-wire/src/lib.rs b/btc-wire/src/lib.rs @@ -1,10 +1,7 @@ use std::str::FromStr; -use bitcoincore_rpc_json::{ - bitcoin::{hashes::hex::FromHex, Address, Amount, Network, Txid}, - GetTransactionResultDetailCategory, ScriptPubkeyType, -}; -use rpc::{BtcRpc, GetTransactionFull}; +use bitcoin::{hashes::hex::FromHex, Address, Amount, Network, Txid}; +use rpc::{BtcRpc, Category, TransactionFull}; use rpc_utils::{segwit_min_amount, sender_address}; use segwit::{decode_segwit_msg, encode_segwit_key}; @@ -68,7 +65,7 @@ impl BtcRpc { pub fn get_tx_segwit_key( &self, id: &Txid, - ) -> Result<(GetTransactionFull, [u8; 32]), GetSegwitErr> { + ) -> Result<(TransactionFull, [u8; 32]), GetSegwitErr> { let full = self.get_tx(id)?; let addresses: Vec<String> = full @@ -104,14 +101,14 @@ impl BtcRpc { pub fn get_tx_op_return( &self, id: &Txid, - ) -> Result<(GetTransactionFull, Vec<u8>), GetOpReturnErr> { + ) -> Result<(TransactionFull, Vec<u8>), GetOpReturnErr> { let full = self.get_tx(id)?; let op_return_out = full .decoded .vout .iter() - .find(|it| it.script_pub_key.type_ == ScriptPubkeyType::NullData) + .find(|it| it.script_pub_key.asm.starts_with("OP_RETURN")) .ok_or(GetOpReturnErr::MissingOpReturn)?; let hex = op_return_out.script_pub_key.asm.split_once(' ').unwrap().1; @@ -128,8 +125,8 @@ impl BtcRpc { /// address as a best-effort gesture. pub fn bounce(&self, id: &Txid, bounce_fee: &Amount) -> Result<Txid, BounceErr> { let full = self.get_tx(id)?; - let detail = &full.tx.details[0]; - if detail.category != GetTransactionResultDetailCategory::Receive { + let detail = &full.details[0]; + if detail.category != Category::Receive { return Err(BounceErr::NotAReceiveTransaction); } diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs @@ -1,8 +1,7 @@ - -use bitcoincore_rpc_json::{bitcoin::{Address, SignedAmount, Amount as BtcAmount, BlockHash, Txid, hashes::Hash}, GetTransactionResultDetailCategory}; +use bitcoin::{hashes::Hash, Address, Amount as BtcAmount, BlockHash, SignedAmount, Txid, Network}; use btc_wire::{ - rpc::BtcRpc, - rpc_utils::{default_data_dir, sender_address, Network}, + rpc::{BtcRpc, Category}, + rpc_utils::{default_data_dir, sender_address}, segwit::DecodeSegWitErr, GetOpReturnErr, GetSegwitErr, }; @@ -268,8 +267,8 @@ fn sender(rpc: BtcRpc, mut db: AutoReloadDb, _config: &Config) { let txs = rpc.list_since_block(last_hash.as_ref(), 1, false)?; // Search for a matching unconfirmed transactions for tx in txs.transactions { - if tx.detail.category == GetTransactionResultDetailCategory::Send { - if let Ok((_, bytes)) = rpc.get_tx_op_return(&tx.info.txid) { + if tx.detail.category == Category::Send { + if let Ok((_, bytes)) = rpc.get_tx_op_return(&tx.txid) { let (wtid, _) = decode_info(&bytes); if let Some(pos) = manuals.iter().position(|(_, it)| it == &wtid) { let (id, wtid) = manuals.swap_remove(pos); @@ -313,20 +312,20 @@ fn watcher(rpc: BtcRpc, mut db: AutoReloadDb, config: &Config) { // Get all transactions made since this block let list = rpc.list_since_block(last_hash.as_ref(), config.confirmation, true)?; // Keep only confirmed send and receive transactions - let txs: HashMap<Txid, GetTransactionResultDetailCategory> = list + let txs: HashMap<Txid, Category> = list .transactions .into_iter() .filter_map(|tx| { let cat = tx.detail.category; - (tx.info.confirmations >= config.confirmation as i32 - && (cat == GetTransactionResultDetailCategory::Send || cat == GetTransactionResultDetailCategory::Receive)) - .then(|| (tx.info.txid, cat)) + (tx.confirmations >= config.confirmation as i32 + && (cat == Category::Send || cat == Category::Receive)) + .then(|| (tx.txid, cat)) }) .collect(); for (id, category) in txs { match category { - GetTransactionResultDetailCategory::Send => match rpc.get_tx_op_return(&id) { + Category::Send => match rpc.get_tx_op_return(&id) { Ok((full, bytes)) => { let (wtid, url) = decode_info(&bytes); let mut tx = db.transaction()?; @@ -356,10 +355,9 @@ fn watcher(rpc: BtcRpc, mut db: AutoReloadDb, config: &Config) { } } else { let debit_addr = sender_address(&rpc, &full)?; - let credit_addr = full.tx.details[0].address.as_ref().unwrap(); - let time = full.tx.info.blocktime.unwrap(); - let date = SystemTime::UNIX_EPOCH + Duration::from_secs(time); - let amount = btc_amount_to_taler_amount(&full.tx.amount); + let credit_addr = full.details[0].address.as_ref().unwrap(); + let date = SystemTime::UNIX_EPOCH + Duration::from_secs(full.time); + let amount = btc_amount_to_taler_amount(&full.amount); // Generate a random request_uid let mut request_uid = [0; 64]; OsRng.fill_bytes(&mut request_uid); @@ -379,13 +377,12 @@ fn watcher(rpc: BtcRpc, mut db: AutoReloadDb, config: &Config) { err => warn!("send: {} {}", id, err), }, }, - GetTransactionResultDetailCategory::Receive => match rpc.get_tx_segwit_key(&id) { + Category::Receive => match rpc.get_tx_segwit_key(&id) { Ok((full, reserve_pub)) => { let debit_addr = sender_address(&rpc, &full)?; - let credit_addr = full.tx.details[0].address.as_ref().unwrap(); - let time = full.tx.info.blocktime.unwrap(); - let date = SystemTime::UNIX_EPOCH + Duration::from_secs(time); - let amount = btc_amount_to_taler_amount(&full.tx.amount); + let credit_addr = full.details[0].address.as_ref().unwrap(); + let date = SystemTime::UNIX_EPOCH + Duration::from_secs(full.time); + let amount = btc_amount_to_taler_amount(&full.amount); let nb = db.execute("INSERT INTO tx_in (_date, amount, reserve_pub, debit_acc, credit_acc) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (reserve_pub) DO NOTHING ", &[ &date, &amount.to_string(), &reserve_pub.as_ref(), &btc_payto_url(&debit_addr).as_ref(), &btc_payto_url(credit_addr).as_ref() ])?; @@ -401,7 +398,7 @@ fn watcher(rpc: BtcRpc, mut db: AutoReloadDb, config: &Config) { err => warn!("receive: {} {}", id, err), }, }, - GetTransactionResultDetailCategory::Generate | GetTransactionResultDetailCategory::Immature | GetTransactionResultDetailCategory::Orphan => {} + Category::Generate | Category::Immature | Category::Orphan => {} } } // Move last_hash forward if no error have been caught @@ -455,9 +452,9 @@ fn main() { let config = taler_config::Config::from_path("test.conf"); let config: &'static Config = Box::leak(Box::new(config)); let network = match config.btc_chain.as_str() { - "main" => Network::MainNet, - "test" => Network::TestNet, - "regtest" => Network::RegTest, + "main" => Network::Bitcoin, + "test" => Network::Testnet, + "regtest" => Network::Regtest, chain => { error!("Unsupported chain {}", chain); std::process::exit(1); diff --git a/btc-wire/src/rpc.rs b/btc-wire/src/rpc.rs @@ -1,9 +1,5 @@ -use crate::rpc_utils::{rpc_url, Network}; -use bitcoincore_rpc_json::{ - bitcoin::{hashes::hex::ToHex, Address, Amount, BlockHash, Txid, Wtxid}, - BlockRef, FundRawTransactionResult, GetRawTransactionResultVin, GetTransactionResult, - ListSinceBlockResult, LoadWalletResult, ScriptPubkeyType, SignRawTransactionResult, -}; +use crate::rpc_utils::{rpc_dir, rpc_url}; +use bitcoin::{hashes::hex::ToHex, Address, Amount, BlockHash, Network, SignedAmount, Txid}; use serde_json::{json, Value}; use std::{ fmt::Debug, @@ -56,7 +52,7 @@ pub struct BtcRpc { impl BtcRpc { pub fn common(data_dir: &Path, network: Network) -> Self { - let path = data_dir.join(network.dir()).join(".cookie"); + let path = data_dir.join(rpc_dir(network)).join(".cookie"); let cookie = std::fs::read(path).unwrap(); let agent = ureq::builder() @@ -74,7 +70,7 @@ impl BtcRpc { } pub fn wallet(data_dir: &Path, network: Network, wallet: &str) -> Self { - let path = data_dir.join(network.dir()).join(".cookie"); + let path = data_dir.join(rpc_dir(network)).join(".cookie"); let cookie = std::fs::read(path).unwrap(); let agent = ureq::builder() @@ -127,11 +123,11 @@ impl BtcRpc { } } - pub fn load_wallet(&self, name: &str) -> Result<LoadWalletResult> { + pub fn load_wallet(&self, name: &str) -> Result<Wallet> { self.call("loadwallet", &[name]) } - pub fn create_wallet(&self, name: &str) -> Result<LoadWalletResult> { + pub fn create_wallet(&self, name: &str) -> Result<Wallet> { self.call("createwallet", &[name]) } @@ -143,7 +139,7 @@ impl BtcRpc { self.call("generatetoaddress", &(nb, address)) } - pub fn wait_for_new_block(&self, timeout: u64) -> Result<BlockRef> { + pub fn wait_for_new_block(&self, timeout: u64) -> Result<()> { self.call("waitfornewblock", &[timeout]) } @@ -201,11 +197,11 @@ impl BtcRpc { ], ) .unwrap(); - let funded: FundRawTransactionResult = self.call("fundrawtransaction", &[hex]).unwrap(); - let signed: SignRawTransactionResult = self - .call("signrawtransactionwithwallet", &[&funded.hex.to_hex()]) + let funded: HexWrapper = self.call("fundrawtransaction", &[hex]).unwrap(); + let signed: HexWrapper = self + .call("signrawtransactionwithwallet", &[&funded.hex]) .unwrap(); - self.call("sendrawtransaction", &[&signed.hex.to_hex()]) + self.call("sendrawtransaction", &[&signed.hex]) } pub fn list_since_block( @@ -213,104 +209,126 @@ impl BtcRpc { hash: Option<&BlockHash>, confirmation: u8, include_remove: bool, - ) -> Result<ListSinceBlockResult> { + ) -> Result<ListSinceBlock> { self.call("listsinceblock", &(hash, confirmation, (), include_remove)) } - pub fn get_tx(&self, id: &Txid) -> Result<GetTransactionFull> { + pub fn get_tx(&self, id: &Txid) -> Result<TransactionFull> { self.call("gettransaction", &(id, (), true)) } - pub fn get_raw(&self, id: &Txid) -> Result<GetRawTransactionResult22> { + pub fn get_raw(&self, id: &Txid) -> Result<RawTransaction> { self.call("getrawtransaction", &(id, true)) } } -/// v22.0 replace "reqSigs" and "addresses" for the saner "address" -#[derive(Clone, PartialEq, Eq, Debug, serde::Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct GetRawTransactionResultVoutScriptPubKey22 { + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Wallet { + pub name: String, + pub warning: Option<String>, +} + +#[derive(Debug, serde::Deserialize)] +pub struct VoutScriptPubKey { pub asm: String, - #[serde(with = "bitcoincore_rpc_json::serde_hex")] - pub hex: Vec<u8>, - #[serde(rename = "type")] - pub type_: ScriptPubkeyType, + // nulldata do not have an address pub address: Option<Address>, } -#[derive(Clone, PartialEq, Eq, Debug, serde::Deserialize)] +#[derive(Debug, serde::Deserialize)] #[serde(rename_all = "camelCase")] -pub struct GetRawTransactionResultVout22 { - #[serde(with = "bitcoincore_rpc_json::bitcoin::util::amount::serde::as_btc")] +pub struct Vout { + #[serde(with = "bitcoin::util::amount::serde::as_btc")] pub value: Amount, pub n: u32, - pub script_pub_key: GetRawTransactionResultVoutScriptPubKey22, + pub script_pub_key: VoutScriptPubKey, } -#[derive(Clone, PartialEq, Eq, Debug, serde::Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct GetRawTransactionResult22 { - #[serde(rename = "in_active_chain")] - pub in_active_chain: Option<bool>, - #[serde(with = "bitcoincore_rpc_json::serde_hex")] - pub hex: Vec<u8>, - pub txid: Txid, - pub hash: Wtxid, - pub size: usize, - pub vsize: usize, - pub version: u32, - pub locktime: u32, - pub vin: Vec<GetRawTransactionResultVin>, - pub vout: Vec<GetRawTransactionResultVout22>, - pub blockhash: Option<BlockHash>, - pub confirmations: Option<u32>, - pub time: Option<usize>, - pub blocktime: Option<usize>, +#[derive(Debug, serde::Deserialize)] +pub struct Vin { + pub sequence: u32, + /// Not provided for coinbase txs. + pub txid: Option<Txid>, + /// Not provided for coinbase txs. + pub vout: Option<u32>, } -/// Decoded raw transtion from"gettransaction" verbose does not return field already given in the simple form -#[derive(Clone, PartialEq, Eq, Debug, serde::Deserialize)] -pub struct TransactionDecoded { - pub txid: Txid, - pub hash: Wtxid, - pub size: usize, - pub vsize: usize, - pub version: u32, - pub locktime: u32, - pub vin: Vec<GetRawTransactionResultVin>, - pub vout: Vec<GetRawTransactionResultVout22>, +/// Enum to represent the category of a transaction. +#[derive(Copy, PartialEq, Eq, Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Category { + Send, + Receive, + Generate, + Immature, + Orphan, +} + +#[derive(Debug, serde::Deserialize)] +pub struct TransactionDetail { + pub address: Option<Address>, + pub category: Category, + #[serde(with = "bitcoin::util::amount::serde::as_btc")] + pub amount: SignedAmount, + pub vout: u32, + #[serde(default, with = "bitcoin::util::amount::serde::as_btc::opt")] + pub fee: Option<SignedAmount>, + /// Ony for send transaction + pub abandoned: Option<bool>, } -/// "gettransaction" with decoded raw transaction -#[derive(Clone, PartialEq, Eq, Debug, serde::Deserialize)] -pub struct GetTransactionFull { +#[derive(Debug, serde::Deserialize)] +pub struct ListTransaction { + pub confirmations: i32, + pub txid: Txid, + pub time: u64, #[serde(flatten)] - pub tx: GetTransactionResult, - pub decoded: TransactionDecoded, + pub detail: TransactionDetail, +} + +#[derive(Debug, serde::Deserialize)] +pub struct ListSinceBlock { + pub transactions: Vec<ListTransaction>, + #[serde(default)] + pub removed: Vec<ListTransaction>, + pub lastblock: BlockHash, +} + +#[derive(Debug, serde::Deserialize)] +pub struct RawTransaction { + pub vin: Vec<Vin>, + pub vout: Vec<Vout>, +} + +#[derive(Debug, serde::Deserialize)] +pub struct TransactionFull { + pub confirmations: i32, + pub time: u64, + #[serde(with = "bitcoin::util::amount::serde::as_btc")] + pub amount: SignedAmount, + #[serde(default, with = "bitcoin::util::amount::serde::as_btc::opt")] + pub fee: Option<SignedAmount>, + pub details: Vec<TransactionDetail>, + pub decoded: RawTransaction, +} + +#[derive(Debug, serde::Deserialize)] +pub struct HexWrapper { + pub hex: String, } /// Bitcoin RPC error codes <https://github.com/bitcoin/bitcoin/blob/master/src/rpc/protocol.h> -#[derive( - Debug, Clone, Copy, PartialEq, Eq, serde_repr::Serialize_repr, serde_repr::Deserialize_repr, -)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde_repr::Deserialize_repr)] #[repr(i32)] pub enum ErrorCode { - // Standard JSON-RPC 2.0 errors - // RPC_INVALID_REQUEST is internally mapped to HTTP_BAD_REQUEST (400). - // It should not be used for application-layer errors. RpcInvalidRequest = -32600, - // RPC_METHOD_NOT_FOUND is internally mapped to HTTP_NOT_FOUND (404). - // It should not be used for application-layer errors. RpcMethodNotFound = -32601, RpcInvalidParams = -32602, - // RPC_INTERNAL_ERROR should only be used for genuine errors in bitcoind - // (for example datadir corruption). RpcInternalError = -32603, RpcParseError = -32700, - // General application defined errors /// std::exception thrown in command handling RpcMiscError = -1, - /// Unexpected type was passed as parameter RpcTypeError = -3, /// Invalid address or key @@ -333,7 +351,6 @@ pub enum ErrorCode { RpcInWarmup = -28, /// RPC method is deprecated RpcMethodDeprecated = -32, - // P2P client errors /// Bitcoin is not connected RpcClientNotConnected = -9, /// Still downloading initial blocks @@ -350,10 +367,8 @@ pub enum ErrorCode { RpcClientP2pDisabled = -31, /// Max number of outbound or block-relay connections already open RpcClientNodeCapacityReached = -34, - // Chain errors - RpcClientMempoolDisabled = -33, /// No mempool instance found - // Wallet errors + RpcClientMempoolDisabled = -33, /// Unspecified problem with wallet (key not found etc.) RpcWalletError = -4, /// Not enough funds in wallet or account @@ -378,7 +393,6 @@ pub enum ErrorCode { RpcWalletNotSpecified = -19, /// This same wallet is already loaded RpcWalletAlreadyLoaded = -35, - // Unused reserved codes, kept around for backwards compatibility. Do not reuse. /// Server is in safe mode, and command is not allowed in safe mode RpcForbiddenBySafeMode = -2, } diff --git a/btc-wire/src/rpc_utils.rs b/btc-wire/src/rpc_utils.rs @@ -1,35 +1,27 @@ use std::{path::PathBuf, str::FromStr}; -use bitcoincore_rpc_json::bitcoin::{Address, Amount}; +use bitcoin::{Address, Amount, Network}; -use crate::rpc::{self, BtcRpc, GetTransactionFull}; +use crate::rpc::{self, BtcRpc, TransactionFull}; pub const CLIENT: &str = "client"; pub const WIRE: &str = "wire"; -/// Bitcoin networks -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Network { - MainNet, - TestNet, - RegTest, -} - -impl Network { - pub fn dir(&self) -> &'static str { - match self { - Network::MainNet => "main", - Network::TestNet => "testnet3", - Network::RegTest => "regtest", - } +pub fn rpc_dir(network: Network) -> &'static str { + match network { + Network::Bitcoin => "main", + Network::Testnet => "testnet3", + Network::Regtest => "regtest", + _ => unreachable!("Unsupported network"), } } pub fn rpc_url(network: Network) -> String { let port = match network { - Network::MainNet => 8332, - Network::TestNet => 18332, - Network::RegTest => 18443, + Network::Bitcoin => 8332, + Network::Testnet => 18332, + Network::Regtest => 18443, + _ => unreachable!("Unsupported network"), }; format!("http://127.0.0.1:{}", port) } @@ -63,7 +55,7 @@ pub fn check_address(addr: &str) -> bool { } /// Get the first sender address from a raw transaction -pub fn sender_address(rpc: &BtcRpc, full: &GetTransactionFull) -> rpc::Result<Address> { +pub fn sender_address(rpc: &BtcRpc, full: &TransactionFull) -> rpc::Result<Address> { let first = &full.decoded.vin[0]; let tx = rpc.get_raw(&first.txid.unwrap())?; Ok(tx