From 6b477f2687a91dcb66d778a645283b34390236b0 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 14 Apr 2025 08:31:56 +1000 Subject: [PATCH] Implement all methods in raw_transactions section Implement all the methods in the `raw_transactions` section of the API docs. Integration most of them leaving some for another day. This was a long running patch and as such a bunch of other unrelated stuff snuck in - such is the nature of this crate. --- client/src/client_sync/mod.rs | 20 +- client/src/client_sync/v17/mod.rs | 12 + .../src/client_sync/v17/raw_transactions.rs | 194 ++++- client/src/client_sync/v17/wallet.rs | 5 +- client/src/client_sync/v18/mod.rs | 16 + .../src/client_sync/v18/raw_transactions.rs | 49 ++ client/src/client_sync/v19/mod.rs | 15 + client/src/client_sync/v20.rs | 15 + client/src/client_sync/v21/mod.rs | 15 + client/src/client_sync/v22/mod.rs | 15 + client/src/client_sync/v23/mod.rs | 15 + client/src/client_sync/v24.rs | 15 + client/src/client_sync/v25.rs | 15 + client/src/client_sync/v26/mod.rs | 17 + .../src/client_sync/v26/raw_transactions.rs | 29 + client/src/client_sync/v27.rs | 16 + client/src/client_sync/v28/mod.rs | 15 + integration_test/tests/generating.rs | 5 +- integration_test/tests/network.rs | 2 +- integration_test/tests/raw_transactions.rs | 680 ++++++++++++++++-- integration_test/tests/wallet.rs | 6 +- types/src/lib.rs | 120 +++- types/src/model/mod.rs | 11 +- types/src/model/raw_transactions.rs | 187 ++++- types/src/model/wallet.rs | 27 +- types/src/psbt/README.md | 9 + types/src/psbt/error.rs | 216 ++++++ types/src/psbt/mod.rs | 294 ++++++++ types/src/v17/blockchain/into.rs | 15 +- types/src/v17/blockchain/mod.rs | 18 +- types/src/v17/mod.rs | 52 +- types/src/v17/raw_transactions/error.rs | 332 +++++++++ types/src/v17/raw_transactions/into.rs | 418 ++++++++++- types/src/v17/raw_transactions/mod.rs | 420 +++++++++-- types/src/v17/wallet/error.rs | 73 -- types/src/v17/wallet/into.rs | 43 +- types/src/v17/wallet/mod.rs | 37 - types/src/v18/mod.rs | 84 +-- types/src/v18/raw_transactions/error.rs | 87 +++ types/src/v18/raw_transactions/into.rs | 120 ++++ types/src/v18/raw_transactions/mod.rs | 98 +++ types/src/v19/mod.rs | 59 +- types/src/v20/mod.rs | 88 +-- types/src/v21/mod.rs | 88 +-- types/src/v22/mod.rs | 89 +-- types/src/v23/mod.rs | 94 +-- types/src/v23/raw_transactions/error.rs | 257 +++++++ types/src/v23/raw_transactions/into.rs | 308 ++++++++ types/src/v23/raw_transactions/mod.rs | 123 ++++ types/src/v24/mod.rs | 99 +-- types/src/v24/raw_transactions/error.rs | 500 +++++++++++++ types/src/v24/raw_transactions/into.rs | 464 ++++++++++++ types/src/v24/raw_transactions/mod.rs | 256 +++++++ types/src/v25/mod.rs | 81 ++- types/src/v26/mod.rs | 90 ++- types/src/v26/raw_transactions/error.rs | 164 +++++ types/src/v26/raw_transactions/into.rs | 85 +++ types/src/v26/raw_transactions/mod.rs | 111 +++ types/src/v27/mod.rs | 91 +-- types/src/v28/mod.rs | 96 +-- verify/src/method/v17.rs | 32 +- verify/src/method/v18.rs | 39 +- verify/src/method/v19.rs | 2 +- verify/src/method/v20.rs | 2 +- verify/src/method/v21.rs | 2 +- verify/src/method/v22.rs | 2 +- verify/src/method/v23.rs | 2 +- verify/src/method/v24.rs | 2 +- verify/src/method/v25.rs | 2 +- verify/src/method/v26.rs | 2 +- verify/src/method/v27.rs | 2 +- verify/src/method/v28.rs | 2 +- 72 files changed, 6220 insertions(+), 846 deletions(-) create mode 100644 client/src/client_sync/v18/raw_transactions.rs create mode 100644 client/src/client_sync/v26/raw_transactions.rs create mode 100644 types/src/psbt/README.md create mode 100644 types/src/psbt/error.rs create mode 100644 types/src/psbt/mod.rs create mode 100644 types/src/v18/raw_transactions/error.rs create mode 100644 types/src/v18/raw_transactions/into.rs create mode 100644 types/src/v18/raw_transactions/mod.rs create mode 100644 types/src/v23/raw_transactions/error.rs create mode 100644 types/src/v23/raw_transactions/into.rs create mode 100644 types/src/v23/raw_transactions/mod.rs create mode 100644 types/src/v24/raw_transactions/error.rs create mode 100644 types/src/v24/raw_transactions/into.rs create mode 100644 types/src/v24/raw_transactions/mod.rs create mode 100644 types/src/v26/raw_transactions/error.rs create mode 100644 types/src/v26/raw_transactions/into.rs create mode 100644 types/src/v26/raw_transactions/mod.rs diff --git a/client/src/client_sync/mod.rs b/client/src/client_sync/mod.rs index 15d4b971..8bb37a85 100644 --- a/client/src/client_sync/mod.rs +++ b/client/src/client_sync/mod.rs @@ -16,11 +16,12 @@ pub mod v26; pub mod v27; pub mod v28; +use std::collections::HashMap; use std::fs::File; use std::io::{BufRead, BufReader}; use std::path::PathBuf; -use bitcoin::Txid; +use bitcoin::{Address, Amount, Txid}; use serde::{Deserialize, Serialize}; pub use crate::client_sync::error::Error; @@ -235,6 +236,23 @@ pub struct Input { pub sequence: Option, } +/// Output used as parameter to `create_raw_transaction`. +// Abuse `HashMap` so we can derive serialize to get the correct JSON object. +#[derive(Debug, Serialize)] +pub struct Output( + /// Map of address to value. Always only has a single item in it. + HashMap, +); + +impl Output { + /// Creates a single output that serializes as Core expects. + pub fn new(addr: Address, value: Amount) -> Self { + let mut map = HashMap::new(); + map.insert(addr.to_string(), value.to_btc()); + Output(map) + } +} + /// An element in the `inputs` argument of method `walletcreatefundedpsbt`. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct WalletCreateFundedPsbtInput { diff --git a/client/src/client_sync/v17/mod.rs b/client/src/client_sync/v17/mod.rs index e4f98fd8..a19f4e48 100644 --- a/client/src/client_sync/v17/mod.rs +++ b/client/src/client_sync/v17/mod.rs @@ -76,9 +76,21 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); +crate::impl_client_v17__testmempoolaccept!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/client/src/client_sync/v17/raw_transactions.rs b/client/src/client_sync/v17/raw_transactions.rs index cd32a221..2016f730 100644 --- a/client/src/client_sync/v17/raw_transactions.rs +++ b/client/src/client_sync/v17/raw_transactions.rs @@ -9,6 +9,67 @@ //! //! See or use the `define_jsonrpc_minreq_client!` macro to define a `Client`. +/// Implements Bitcoin Core JSON-RPC API method `combinepsbt` +#[macro_export] +macro_rules! impl_client_v17__combinepsbt { + () => { + impl Client { + pub fn combine_psbt(&self, txs: &[bitcoin::Psbt]) -> Result { + let txs = txs.iter().map(|psbt| format!("{}", psbt)).collect::>(); + self.call("combinepsbt", &[txs.into()]) + } + } + }; +} + +/// Implements Bitcoin Core JSON-RPC API method `combinerawtransaction` +#[macro_export] +macro_rules! impl_client_v17__combinerawtransaction { + () => { + impl Client { + pub fn combine_raw_transaction( + &self, + txs: &[bitcoin::Transaction], + ) -> Result { + let encoded = txs + .iter() + .map(|tx| bitcoin::consensus::encode::serialize_hex(tx)) + .collect::>(); + self.call("combinerawtransaction", &[into_json(encoded)?]) + } + } + }; +} + +/// Implements Bitcoin Core JSON-RPC API method `converttopsbt` +#[macro_export] +macro_rules! impl_client_v17__converttopsbt { + () => { + impl Client { + pub fn convert_to_psbt(&self, tx: &bitcoin::Transaction) -> Result { + let hex = bitcoin::consensus::encode::serialize_hex(tx); + self.call("converttopsbt", &[hex.into()]) + } + } + }; +} + +/// Implements Bitcoin Core JSON-RPC API method `createpsbt` +#[macro_export] +macro_rules! impl_client_v17__createpsbt { + () => { + impl Client { + pub fn create_psbt( + &self, + inputs: &[$crate::client_sync::Input], + outputs: &[$crate::client_sync::Output], + ) -> Result { + self.call("createpsbt", &[into_json(inputs)?, into_json(outputs)?]) + } + } + }; +} + /// Implements Bitcoin Core JSON-RPC API method `createrawtransaction` #[macro_export] macro_rules! impl_client_v17__createrawtransaction { @@ -17,7 +78,7 @@ macro_rules! impl_client_v17__createrawtransaction { pub fn create_raw_transaction( &self, inputs: &[$crate::client_sync::Input], - outputs: &std::collections::BTreeMap, // Map of address to amount. + outputs: &[$crate::client_sync::Output], ) -> Result { self.call("createrawtransaction", &[into_json(inputs)?, into_json(outputs)?]) } @@ -25,6 +86,60 @@ macro_rules! impl_client_v17__createrawtransaction { }; } +/// Implements Bitcoin Core JSON-RPC API method `decodepsbt` +#[macro_export] +macro_rules! impl_client_v17__decodepsbt { + () => { + impl Client { + pub fn decode_psbt(&self, psbt: &str) -> Result { + self.call("decodepsbt", &[psbt.into()]) + } + } + }; +} + +/// Implements Bitcoin Core JSON-RPC API method `finalizepsbt` +#[macro_export] +macro_rules! impl_client_v17__finalizepsbt { + () => { + impl Client { + pub fn finalize_psbt(&self, psbt: &bitcoin::Psbt) -> Result { + let psbt = format!("{}", psbt); + self.call("finalizepsbt", &[psbt.into()]) + } + } + }; +} + +/// Implements Bitcoin Core JSON-RPC API method `decoderawtransaction` +#[macro_export] +macro_rules! impl_client_v17__decoderawtransaction { + () => { + impl Client { + pub fn decode_raw_transaction( + &self, + tx: &bitcoin::Transaction, + ) -> Result { + let hex = bitcoin::consensus::encode::serialize_hex(tx); + self.call("decoderawtransaction", &[hex.into()]) + } + } + }; +} + +/// Implements Bitcoin Core JSON-RPC API method `decodescript` +#[macro_export] +macro_rules! impl_client_v17__decodescript { + () => { + impl Client { + // Arg is the hex encoded script we want to decode. + pub fn decode_script(&self, script: &str) -> Result { + self.call("decodescript", &[script.into()]) + } + } + }; +} + /// Implements Bitcoin Core JSON-RPC API method `fundrawtransaction` #[macro_export] macro_rules! impl_client_v17__fundrawtransaction { @@ -32,9 +147,29 @@ macro_rules! impl_client_v17__fundrawtransaction { impl Client { pub fn fund_raw_transaction( &self, - hex: &str, // Hex encoded transaction. + tx: &bitcoin::Transaction, ) -> Result { - self.call("fundrawtransaction", &[into_json(hex)?]) + let hex = bitcoin::consensus::encode::serialize_hex(tx); + self.call("fundrawtransaction", &[hex.into()]) + } + } + }; +} + +/// Implements Bitcoin Core JSON-RPC API method `getrawtransaction` +#[macro_export] +macro_rules! impl_client_v17__getrawtransaction { + () => { + impl Client { + pub fn get_raw_transaction(&self, txid: bitcoin::Txid) -> Result { + self.call("getrawtransaction", &[into_json(&txid)?, false.into()]) + } + + pub fn get_raw_transaction_verbose( + &self, + txid: Txid, + ) -> Result { + self.call("getrawtransaction", &[into_json(&txid)?, true.into()]) } } }; @@ -55,3 +190,56 @@ macro_rules! impl_client_v17__sendrawtransaction { } }; } + +/// Implements Bitcoin Core JSON-RPC API method `signrawtransaction` +#[macro_export] +macro_rules! impl_client_v17__signrawtransaction { + () => { + impl Client { + pub fn sign_raw_transaction( + &self, + tx: &bitcoin::Transaction, + ) -> Result { + let hex = bitcoin::consensus::encode::serialize_hex(tx); + self.call("signrawtransaction", &[hex.into()]) + } + } + }; +} + +/// Implements Bitcoin Core JSON-RPC API method `signrawtransactionwithkey` +#[macro_export] +macro_rules! impl_client_v17__signrawtransactionwithkey { + () => { + impl Client { + pub fn sign_raw_transaction_with_key( + &self, + tx: &bitcoin::Transaction, + keys: &[bitcoin::PrivateKey], + ) -> Result { + let hex = bitcoin::consensus::encode::serialize_hex(tx); + let keys = keys.iter().map(|k| format!("{}", k)).collect::>(); + self.call("signrawtransactionwithkey", &[hex.into(), into_json(keys)?]) + } + } + }; +} + +/// Implements Bitcoin Core JSON-RPC API method `testmempoolaccept` +#[macro_export] +macro_rules! impl_client_v17__testmempoolaccept { + () => { + impl Client { + pub fn test_mempool_accept( + &self, + txs: &[bitcoin::Transaction], + ) -> Result { + let encoded = txs + .iter() + .map(|tx| bitcoin::consensus::encode::serialize_hex(tx)) + .collect::>(); + self.call("testmempoolaccept", &[into_json(encoded)?]) + } + } + }; +} diff --git a/client/src/client_sync/v17/wallet.rs b/client/src/client_sync/v17/wallet.rs index ce8f6093..21100037 100644 --- a/client/src/client_sync/v17/wallet.rs +++ b/client/src/client_sync/v17/wallet.rs @@ -414,8 +414,9 @@ macro_rules! impl_client_v17__signrawtransactionwithwallet { // `hexstring`: The transaction hex string. pub fn sign_raw_transaction_with_wallet( &self, - hex: &str, - ) -> Result { + tx: &bitcoin::Transaction, + ) -> Result { + let hex = bitcoin::consensus::encode::serialize_hex(tx); self.call("signrawtransactionwithwallet", &[into_json(hex)?]) } } diff --git a/client/src/client_sync/v18/mod.rs b/client/src/client_sync/v18/mod.rs index 79c731bf..74f3563b 100644 --- a/client/src/client_sync/v18/mod.rs +++ b/client/src/client_sync/v18/mod.rs @@ -5,6 +5,7 @@ //! We ignore option arguments unless they effect the shape of the returned JSON data. pub mod control; +pub mod raw_transactions; use std::collections::BTreeMap; use std::path::Path; @@ -70,9 +71,24 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v18__analyzepsbt!(); +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); +crate::impl_client_v18__joinpsbts!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); +crate::impl_client_v17__testmempoolaccept!(); +crate::impl_client_v18__utxoupdatepsbt!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/client/src/client_sync/v18/raw_transactions.rs b/client/src/client_sync/v18/raw_transactions.rs new file mode 100644 index 00000000..0c2eaf9d --- /dev/null +++ b/client/src/client_sync/v18/raw_transactions.rs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Macros for implementing JSON-RPC methods on a client. +//! +//! Specifically this is methods found under the `== Rawtransactions ==` section of the +//! API docs of Bitcoin Core `v0.18`. +//! +//! All macros require `Client` to be in scope. +//! +//! See or use the `define_jsonrpc_minreq_client!` macro to define a `Client`. + +/// Implements Bitcoin Core JSON-RPC API method `analyzepsbt` +#[macro_export] +macro_rules! impl_client_v18__analyzepsbt { + () => { + impl Client { + pub fn analyze_psbt(&self, psbt: &bitcoin::Psbt) -> Result { + let psbt = format!("{}", psbt); + self.call("analyzepsbt", &[psbt.into()]) + } + } + }; +} + +/// Implements Bitcoin Core JSON-RPC API method `joinpsbts` +#[macro_export] +macro_rules! impl_client_v18__joinpsbts { + () => { + impl Client { + pub fn join_psbts(&self, psbts: &[bitcoin::Psbt]) -> Result { + let psbts = psbts.iter().map(|psbt| format!("{}", psbt)).collect::>(); + self.call("joinpsbts", &[psbts.into()]) + } + } + }; +} + +/// Implements Bitcoin Core JSON-RPC API method `uxtoupdatepsbt` +#[macro_export] +macro_rules! impl_client_v18__utxoupdatepsbt { + () => { + impl Client { + pub fn utxo_update_psbt(&self, psbt: &bitcoin::Psbt) -> Result { + let psbt = format!("{}", psbt); + self.call("uxtoupdatepsbt", &[psbt.into()]) + } + } + }; +} diff --git a/client/src/client_sync/v19/mod.rs b/client/src/client_sync/v19/mod.rs index 5c2ed9d9..d75638ba 100644 --- a/client/src/client_sync/v19/mod.rs +++ b/client/src/client_sync/v19/mod.rs @@ -71,9 +71,24 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v18__analyzepsbt!(); +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); +crate::impl_client_v18__joinpsbts!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); +crate::impl_client_v17__testmempoolaccept!(); +crate::impl_client_v18__utxoupdatepsbt!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/client/src/client_sync/v20.rs b/client/src/client_sync/v20.rs index 488ea744..633b54cb 100644 --- a/client/src/client_sync/v20.rs +++ b/client/src/client_sync/v20.rs @@ -68,9 +68,24 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v18__analyzepsbt!(); +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); +crate::impl_client_v18__joinpsbts!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); +crate::impl_client_v17__testmempoolaccept!(); +crate::impl_client_v18__utxoupdatepsbt!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/client/src/client_sync/v21/mod.rs b/client/src/client_sync/v21/mod.rs index 6c1bd42a..a894202e 100644 --- a/client/src/client_sync/v21/mod.rs +++ b/client/src/client_sync/v21/mod.rs @@ -70,9 +70,24 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v18__analyzepsbt!(); +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); +crate::impl_client_v18__joinpsbts!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); +crate::impl_client_v17__testmempoolaccept!(); +crate::impl_client_v18__utxoupdatepsbt!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/client/src/client_sync/v22/mod.rs b/client/src/client_sync/v22/mod.rs index 3ad79a4b..360287ea 100644 --- a/client/src/client_sync/v22/mod.rs +++ b/client/src/client_sync/v22/mod.rs @@ -71,9 +71,24 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v18__analyzepsbt!(); +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); +crate::impl_client_v18__joinpsbts!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); +crate::impl_client_v17__testmempoolaccept!(); +crate::impl_client_v18__utxoupdatepsbt!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/client/src/client_sync/v23/mod.rs b/client/src/client_sync/v23/mod.rs index f500d8c0..677727fa 100644 --- a/client/src/client_sync/v23/mod.rs +++ b/client/src/client_sync/v23/mod.rs @@ -71,9 +71,24 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v18__analyzepsbt!(); +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); +crate::impl_client_v18__joinpsbts!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); +crate::impl_client_v17__testmempoolaccept!(); +crate::impl_client_v18__utxoupdatepsbt!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/client/src/client_sync/v24.rs b/client/src/client_sync/v24.rs index 026583a0..ed4c2667 100644 --- a/client/src/client_sync/v24.rs +++ b/client/src/client_sync/v24.rs @@ -68,9 +68,24 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v18__analyzepsbt!(); +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); +crate::impl_client_v18__joinpsbts!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); +crate::impl_client_v17__testmempoolaccept!(); +crate::impl_client_v18__utxoupdatepsbt!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/client/src/client_sync/v25.rs b/client/src/client_sync/v25.rs index f9e3cd6d..10229ddc 100644 --- a/client/src/client_sync/v25.rs +++ b/client/src/client_sync/v25.rs @@ -68,9 +68,24 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v18__analyzepsbt!(); +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); +crate::impl_client_v18__joinpsbts!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); +crate::impl_client_v17__testmempoolaccept!(); +crate::impl_client_v18__utxoupdatepsbt!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/client/src/client_sync/v26/mod.rs b/client/src/client_sync/v26/mod.rs index b0c5e2c0..180d4792 100644 --- a/client/src/client_sync/v26/mod.rs +++ b/client/src/client_sync/v26/mod.rs @@ -6,6 +6,7 @@ pub mod blockchain; pub mod mining; +pub mod raw_transactions; use std::collections::BTreeMap; use std::path::Path; @@ -72,9 +73,25 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v18__analyzepsbt!(); +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); +crate::impl_client_v18__joinpsbts!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); +crate::impl_client_v26__submitpackage!(); +crate::impl_client_v17__testmempoolaccept!(); +crate::impl_client_v18__utxoupdatepsbt!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/client/src/client_sync/v26/raw_transactions.rs b/client/src/client_sync/v26/raw_transactions.rs new file mode 100644 index 00000000..74c19a99 --- /dev/null +++ b/client/src/client_sync/v26/raw_transactions.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Macros for implementing JSON-RPC methods on a client. +//! +//! Specifically this is methods found under the `== Rawtransactions ==` section of the +//! API docs of Bitcoin Core `v26`. +//! +//! All macros require `Client` to be in scope. +//! +//! See or use the `define_jsonrpc_minreq_client!` macro to define a `Client`. + +/// Implements Bitcoin Core JSON-RPC API method `submitpackage` +#[macro_export] +macro_rules! impl_client_v26__submitpackage { + () => { + impl Client { + pub fn submit_package( + &self, + package: &[bitcoin::Transaction], + ) -> Result { + let package_txs = package + .into_iter() + .map(|tx| bitcoin::consensus::encode::serialize_hex(tx)) + .collect::>(); + self.call("submitpackage", &[package_txs.into()]) + } + } + }; +} diff --git a/client/src/client_sync/v27.rs b/client/src/client_sync/v27.rs index ae20d8c9..676750fd 100644 --- a/client/src/client_sync/v27.rs +++ b/client/src/client_sync/v27.rs @@ -69,9 +69,25 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v18__analyzepsbt!(); +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); +crate::impl_client_v18__joinpsbts!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); +crate::impl_client_v26__submitpackage!(); +crate::impl_client_v17__testmempoolaccept!(); +crate::impl_client_v18__utxoupdatepsbt!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/client/src/client_sync/v28/mod.rs b/client/src/client_sync/v28/mod.rs index ce8ce37f..cc6d5add 100644 --- a/client/src/client_sync/v28/mod.rs +++ b/client/src/client_sync/v28/mod.rs @@ -71,10 +71,25 @@ crate::impl_client_v17__getnetworkinfo!(); crate::impl_client_v17__getpeerinfo!(); // == Rawtransactions == +crate::impl_client_v18__analyzepsbt!(); +crate::impl_client_v17__combinepsbt!(); +crate::impl_client_v17__combinerawtransaction!(); +crate::impl_client_v17__converttopsbt!(); +crate::impl_client_v17__createpsbt!(); crate::impl_client_v17__createrawtransaction!(); +crate::impl_client_v17__decodepsbt!(); +crate::impl_client_v17__decoderawtransaction!(); +crate::impl_client_v17__decodescript!(); +crate::impl_client_v17__finalizepsbt!(); crate::impl_client_v17__fundrawtransaction!(); +crate::impl_client_v17__getrawtransaction!(); +crate::impl_client_v18__joinpsbts!(); crate::impl_client_v17__sendrawtransaction!(); +crate::impl_client_v17__signrawtransaction!(); +crate::impl_client_v17__signrawtransactionwithkey!(); crate::impl_client_v28__submitpackage!(); +crate::impl_client_v17__testmempoolaccept!(); +crate::impl_client_v18__utxoupdatepsbt!(); // == Wallet == crate::impl_client_v17__addmultisigaddress!(); diff --git a/integration_test/tests/generating.rs b/integration_test/tests/generating.rs index 5e8d7ce6..febd1028 100644 --- a/integration_test/tests/generating.rs +++ b/integration_test/tests/generating.rs @@ -10,7 +10,10 @@ use node::mtype; #[test] // The `generate` method was deprecated in Core v18 and was removed in v19. -#[cfg(feature = "v17")] +#[cfg(all( + feature = "v17", + not(feature = "v18") // We just need one `not` so that --all-features builds. +))] fn generating__generate__modelled() { const NBLOCKS: usize = 10; let node = Node::with_wallet(Wallet::Default, &[]); diff --git a/integration_test/tests/network.rs b/integration_test/tests/network.rs index c0ba298b..909c96e2 100644 --- a/integration_test/tests/network.rs +++ b/integration_test/tests/network.rs @@ -21,7 +21,7 @@ fn network__get_net_totals() { } #[test] -fn network__get_network_info() { +fn network__get_network_info__modelled() { let node = Node::with_wallet(Wallet::None, &[]); let json: GetNetworkInfo = node.client.get_network_info().expect("getnetworkinfo"); let model: Result = json.into_model(); diff --git a/integration_test/tests/raw_transactions.rs b/integration_test/tests/raw_transactions.rs index 52a89876..04a5dbac 100644 --- a/integration_test/tests/raw_transactions.rs +++ b/integration_test/tests/raw_transactions.rs @@ -3,100 +3,272 @@ //! Tests for methods found under the `== Rawtransactions ==` section of the API docs. #![allow(non_snake_case)] // Test names intentionally use double underscore. +#![allow(unused_imports)] // Because of feature gated tests. -use std::collections::BTreeMap; - -use bitcoin::{Amount, Sequence}; -#[cfg(feature = "TODO")] -use bitcoin::{absolute, transaction, consensus, TxOut, Transaction}; +use bitcoin::hex::FromHex as _; +use bitcoin::opcodes::all::*; +use bitcoin::{absolute, transaction, consensus, script, Amount, TxOut, Transaction, ScriptBuf}; use integration_test::{Node, NodeExt as _, Wallet}; -use node::client::client_sync::Input; +use node::client::client_sync::{Input, Output}; +use node::vtype::*; // All the version specific types. +use node::mtype; #[test] -fn raw_transacitons__create_raw_transaction() { +#[cfg(not(feature = "v17"))] // analyzepsbt was added in v18. +fn raw_transactions__analyze_psbt__modelled() { let node = Node::with_wallet(Wallet::Default, &[]); node.fund_wallet(); - // Calls `createrawtransaction`. - _create_and_send(&node); -} -#[test] -#[cfg(feature = "TODO")] -fn raw_transactions__fund_raw_transaction() { - let node = Node::with_wallet(Wallet::Default, &[]); - node.fund_wallet(); - _create_fund_and_send(&node); + let psbt = create_a_psbt(&node); + let json: AnalyzePsbt = node.client.analyze_psbt(&psbt).expect("analyzepsbt"); + let model: Result = json.into_model(); + model.unwrap(); } #[test] -fn raw_transactions__send_raw_transaction() { +fn raw_transactions__combine_psbt__modelled() { let node = Node::with_wallet(Wallet::Default, &[]); node.fund_wallet(); - // Calls `sendrawtransaction`. - _create_and_send(&node); -} -fn _create_and_send(node: &Node) { let (_addr, txid) = node.create_mempool_transaction(); // A million sats. node.mine_a_block(); - // We don't know what vout the UTXO is in. let tx_out = node .client - .get_tx_out(txid, 0) + .get_tx_out(txid, 0) // Might be previous spend or might be change. .expect("gettxout") .into_model() .expect("GetTxOut into model") .tx_out; let spend_amount = Amount::from_sat(100_000); let fee = Amount::from_sat(1000); + // Calculate the change because we do not know the value of the UTXO. let change_amount = tx_out.value - spend_amount - fee; - let sequence = Sequence::MAX; // Ignore locktime. - let inputs = vec![Input { txid, vout: 0, sequence: Some(sequence) }]; + let inputs = vec![Input { txid, vout: 0, sequence: None }]; - let mut outputs = BTreeMap::new(); + let mut outputs = vec![]; // Just send back to ourself. let spend_address = node.client.new_address().expect("failed to create new address"); - outputs.insert(spend_address.to_string(), spend_amount.to_btc()); + outputs.push(Output::new(spend_address, spend_amount)); + + let change_address = node + .client + .get_raw_change_address() + .expect("getrawchangeaddress") + .into_model() + .expect("GetRawChangeAddress into model") + .0 + .assume_checked(); + outputs.push(Output::new(change_address, change_amount)); - let json = node.client.get_raw_change_address().expect("getrawchangeaddress"); - let change_address = json.0; - outputs.insert(change_address.to_string(), change_amount.to_btc()); + let json: CreatePsbt = node.client.create_psbt(&inputs, &outputs).expect("createpsbt"); + let res: Result = json.clone().into_model(); + let psbt = res.expect("CreatePsbt into model"); + let psbt = psbt.0; - let json = node.client.create_raw_transaction(&inputs, &outputs).expect("createrawtransaction"); + // Quick and dirty test, just combine the same PSBT with itself. + let psbts = vec![psbt.clone(), psbt.clone()]; - // This method is from the wallet section. - let json = node + let json: CombinePsbt = node.client.combine_psbt(&psbts).expect("combinepsbt"); + let model: Result = json.into_model(); + let combined = model.expect("CombinePsbt into model"); + // Just for giggles. + assert_eq!(combined.0, psbt) +} + +#[test] +fn raw_transactions__combine_raw_transaction__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); + node.fund_wallet(); + + let (_, txid) = node.create_mempool_transaction(); + let tx = node .client - .sign_raw_transaction_with_wallet(&json.0) - .expect("signrawtransactionwithwallet"); + .get_raw_transaction(txid) + .expect("getrawtransaction") + .transaction() + .expect("GetRawTransaction into model"); - // The proves we did everything correctly. - let model = json.clone().into_model().expect("SignRawTransactionWithWalet into model"); - let _ = node.client.send_raw_transaction(&model.raw_transaction).expect("createrawtransaction"); + // Quick and dirty test, just combine the same tx with itself. + let txs = vec![tx.clone(), tx.clone()]; + + let json: CombineRawTransaction = node.client.combine_raw_transaction(&txs).expect("combinerawtransaction"); + let model: Result = json.into_model(); + + let combined = model.expect("CombineRawTransaction into model"); + // Just for giggles. + assert_eq!(combined.0, tx) } -#[cfg(feature = "TODO")] -fn _create_fund_and_send(node: &Node) { - let (_addr, txid) = node.create_mempool_transaction(); // A million sats. - node.mine_a_block(); +#[test] +fn raw_transactions__convert_to_psbt__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); + node.fund_wallet(); - // We don't know what vout the UTXO is in. - let tx_out = node + let tx = create_a_raw_transaction(&node); + + let json: ConvertToPsbt = node.client.convert_to_psbt(&tx).expect("converttopsbt"); + let model: Result = json.into_model(); + let _ = model.expect("ConvertToPsbt into model"); +} + +#[test] +fn raw_transactions__create_psbt__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); + node.fund_wallet(); + let _ = create_a_psbt(&node); +} + +#[test] +fn raw_transactions__create_raw_transaction__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); + node.fund_wallet(); + create_sign_send(&node); +} + +// Notes on testing decoding of PBST. +// +// - `bip32_derivs` field in the input list of the decoded PSBT changes shape a bunch of times. +// - In v23 a bunch of additional fields are added. +// - In v24 taproot fields are added. +// +// All this should still be handled by `into_model` because `bitcoin::Psbt` has all optional fields. +#[test] +fn raw_transactions__decode_psbt__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); + node.fund_wallet(); + + let mut psbt = create_a_psbt(&node); + + // A bunch of new fields got added in v23. + // + // Add an arbitrary global xpub to see if it decodes. Before v23 this will end up in `unknown`, + // from v23 onwards in should be in its own field. + { + use std::collections::BTreeMap; + use bitcoin::bip32::{Fingerprint, DerivationPath, Xpub}; + + let mut map = BTreeMap::default(); + // Some arbitrary xpub I grabbed fom rust-bitcoin. + let xpub = "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"; + let xpub = xpub.parse::().expect("failed to parse xpub"); + let fp = Fingerprint::from([1u8, 2, 3, 42]); + let path = "m/84'/0'/0'/0/1".parse::().expect("failed to parse derivation path"); + map.insert(xpub, (fp, path)); + + psbt.xpub = map; + } + + let encoded = psbt.to_string(); + + let json: DecodePsbt = node.client.decode_psbt(&encoded).expect("decodepsbt"); + let res: Result = json.into_model(); + + #[allow(unused_variables)] + let decoded = res.expect("DecodePsbt into model"); + + // Before Core v23 global xpubs was not a known keypair. + #[cfg(feature = "v17")] + #[cfg(feature = "v22")] + assert_eq!(decoded.psbt.unknown.len(), 1); + + #[cfg(feature = "v23")] + assert_eq!(decoded.psbt.xpub.len(), 1); + + // TODO: Add a taproot field and test it with v24 +} + +#[test] +fn raw_transactions__decode_raw_transaction__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); + node.fund_wallet(); + + let (_, txid) = node.create_mempool_transaction(); + + let tx = node .client - .get_tx_out(txid, 0) - .expect("gettxout") - .into_model() - .expect("GetTxOut into model") - .tx_out; + .get_raw_transaction(txid) + .expect("getrawtransaction") + .transaction() + .expect("GetRawTransaction into model"); + let json = node.client.decode_raw_transaction(&tx).expect("decoderawtransaction"); + let model: Result = json.into_model(); + model.expect("DecodeRawTransaction into model"); +} + +#[test] +// FIXME: Seems the returned fields are different depending on the script. Needs more thorough testing. +fn raw_transactions__decode_script__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); + node.fund_wallet(); + + let p2pkh = arbitrary_p2pkh_script(); + let multi = arbitrary_multisig_script(); + + for script in &[p2pkh, multi] { + let hex = script.to_hex_string(); + + let json: DecodeScript = node.client.decode_script(&hex).expect("decodescript"); + let model: Result = json.into_model(); + let _ = model.expect("DecodeScript into model"); + } +} + +// Script builder code copied from rust-bitcoin script unit tests. +fn arbitrary_p2pkh_script() -> ScriptBuf { + let pubkey_hash = <[u8; 20]>::from_hex("16e1ae70ff0fa102905d4af297f6912bda6cce19").unwrap(); + + script::Builder::new() + .push_opcode(OP_DUP) + .push_opcode(OP_HASH160) + .push_slice(&pubkey_hash) + .push_opcode(OP_EQUALVERIFY) + .push_opcode(OP_CHECKSIG) + .into_script() +} + +fn arbitrary_multisig_script() -> ScriptBuf { + let pk1 = + <[u8; 33]>::from_hex("022afc20bf379bc96a2f4e9e63ffceb8652b2b6a097f63fbee6ecec2a49a48010e") + .unwrap(); + let pk2 = + <[u8; 33]>::from_hex("03a767c7221e9f15f870f1ad9311f5ab937d79fcaeee15bb2c722bca515581b4c0") + .unwrap(); + + script::Builder::new() + .push_opcode(OP_PUSHNUM_1) + .push_opcode(OP_PUSHBYTES_33) + .push_slice(&pk1) + .push_opcode(OP_PUSHBYTES_33) + .push_slice(&pk2) + .push_opcode(OP_PUSHNUM_2) + .push_opcode(OP_CHECKMULTISIG) + .into_script() +} + +#[test] +#[cfg(feature = "TODO")] +fn raw_transactions__finalize_psbt__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); + node.fund_wallet(); + + let (addr, _tx, txid, tx_out, vout) = create_utxo(&node); + + // Assumes tx_out has a million sats in it. let spend_amount = Amount::from_sat(100_000); let fee = Amount::from_sat(1000); let change_amount = tx_out.value - spend_amount - fee; + let inputs = vec![Input { txid, vout, sequence: None }]; + + let mut outputs = vec![]; + // Just send back to ourself. - let spend_address = node.client.new_address().expect("failed to get new address"); + let spend_address = node.client.new_address().expect("failed to create new address"); + outputs.push(Output::new(spend_address, spend_amount)); + let change_address = node .client .get_raw_change_address() @@ -105,35 +277,137 @@ fn _create_fund_and_send(node: &Node) { .expect("GetRawChangeAddress into model") .0 .assume_checked(); + outputs.push(Output::new(change_address, change_amount)); - let spend = TxOut { value: spend_amount, script_pubkey: spend_address.script_pubkey() }; - let change = TxOut { value: change_amount, script_pubkey: change_address.script_pubkey() }; + let json: CreatePsbt = node.client.create_psbt(&inputs, &outputs).expect("createpsbt"); + let res: Result = json.clone().into_model(); + let psbt = res.expect("CreatePsbt into model"); + let psbt = psbt.0; - let tx = Transaction { - version: transaction::Version::TWO, - lock_time: absolute::LockTime::ZERO, - input: vec![], - output: vec![spend, change], - }; - let _ = consensus::encode::serialize_hex(&tx); + let json: DumpPrivKey = node.client.dump_priv_key(&addr).expect("dumpprivkey"); + let model: mtype::DumpPrivKey = json.into_model().expect("DumpPrivKey"); + let key = model.0; + + let json: SignRawTransaction = node + .client + .sign_raw_transaction_with_key(&psbt.unsigned_tx, &[key]) + .expect("signrawtransactionwithkey"); + let res: Result = json.into_model(); + let model = res.expect("SignRawTransaction into model"); - // TODO: This errors with: RpcError { code: -22, message: "TX decode failed", data: None } - // let json = node.client.fund_raw_transaction(&tx).expect("fundrawtransaction"); - // let model = json.into_model().expect("FundRawTransaction into model"); - // let funded = consensus::encode::serialize_hex(&model.tx); + // FIXME: Core errors here with: code: -22, message: "TX decode failed" + let json: ConvertToPsbt = node.client.convert_to_psbt(&model.tx).expect("converttopsbt"); + let model: Result = json.into_model(); + let psbt = model.expect("ConvertToPsbt into model").0; - // // This method is from the wallet section. - // let json = node.client.sign_raw_transaction_with_wallet(&funded).expect("signrawtransactionwithwallet"); + let json: FinalizePsbt = node.client.finalize_psbt(&psbt).expect("finalizepsbt"); + let model: Result = json.into_model(); + let _ = model.expect("FinalizePsbt into model"); +} - // // The proves we did everything correctly. - // let model = json.clone().into_model().expect("SignRawTransactionWithWalet into model"); - // let _ = node.client.send_raw_transaction(&model.raw_transaction).expect("createrawtransaction"); +#[test] +fn raw_transactions__fund_raw_transaction__modelled() { + let node = Node::with_wallet(Wallet::Default, &[]); + node.fund_wallet(); + create_fund_sign_send(&node); } #[test] -#[cfg(feature = "v28")] -fn raw_transactions__submitpackage() { +fn raw_transactions__send_raw_transaction__modelled() { let node = Node::with_wallet(Wallet::Default, &[]); + node.fund_wallet(); + create_sign_send(&node); // Calls `sendrawtransaction`. +} + +#[test] +fn raw_transactions__get_raw_transaction__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); + node.fund_wallet(); + + // Get raw transaction using a mined transaction and verbose = false. + let (_, tx) = node.create_mined_transaction(); + let json: GetRawTransaction = + node.client.get_raw_transaction(tx.compute_txid()).expect("getrawtransaction"); + let model: Result = json.into_model(); + model.expect("GetRawTransaction into model"); + + // Get raw transaction using a mined transaction and verbose = true. + let (_, tx) = node.create_mined_transaction(); + let json = node + .client + .get_raw_transaction_verbose(tx.compute_txid()) + .expect("getrawtransaction verbose"); + let model: Result = + json.into_model(); + model.expect("GetRawTransactionVerbose into model"); + + // Get raw transaction using an un-mined transaction. + let (_, txid) = node.create_mempool_transaction(); + let _ = node + .client + .get_raw_transaction_verbose(txid) + .expect("getrawtransaction verbose") + .into_model() + .expect("GetRawTransactionVerbose into model"); + +} + +#[test] +fn raw_transactions__sign_raw_transaction__modelled() { + let node = Node::with_wallet(Wallet::Default, &[]); + node.fund_wallet(); + create_sign_send(&node); +} + +// TODO: Work out how to test things without using dumpprivkey. +#[test] +#[cfg(any( + feature = "v17", + feature = "v18", + feature = "v19", + feature = "v20", + feature = "v21", + feature = "v22", +))] // In v23 dumpprivkey no longer works. +fn raw_transactions__sign_raw_transaction_with_key__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); + node.fund_wallet(); + create_sign_with_key_send(&node) +} + +// FIXME: Doesn't work for v26 for some reason. +#[test] +#[cfg(all(feature = "v27", not(feature = "v28")))] +fn raw_transactions__submit_package__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); + + // Submitting the empty package should simply fail. + assert!(node.client.submit_package(&[]).is_err()); + + node.fund_wallet(); + + let (_, tx_0) = node.create_mined_transaction(); + let (_, tx_1) = node.create_mined_transaction(); + + // The call for submitting this package should succeed, but yield an 'already known' + // error for all transactions. + let res = node + .client + .submit_package(&[tx_0, tx_1]) + .expect("failed to submit package") + .into_model() + .expect("failed to submit package"); + for (_, tx_result) in &res.tx_results { + assert!(tx_result.error.is_some()); + } + assert!(res.replaced_transactions.is_empty()); +} + +// In Core v28 submitpackage has additional optional features. +#[test] +#[cfg(all(not(feature = "v27"), feature = "v28"))] +fn raw_transactions__submit_package__modelled() { + let node = Node::with_wallet(Wallet::Default, &["-txindex"]); // Submitting the empty package should simply fail. assert!(node.client.submit_package(&[], None, None).is_err()); @@ -156,3 +430,269 @@ fn raw_transactions__submitpackage() { } assert!(res.replaced_transactions.is_empty()); } + +#[test] +#[cfg(feature = "TODO")] +fn raw_transactions__test_mempool_accept__modelled() {} + +#[test] +#[cfg(not(feature = "v17"))] // utxoupdatepsbt was added in v18. +fn raw_transactions__utxo_update_psbt() {} + +// Manipulates raw transactions. +// +// Calls the following RPC methods: +// - create_raw_transaction +// - sign_raw_transaction_with_wallet +// - send_raw_transaction +fn create_sign_send(node: &Node) { + let (_addr, _tx, txid, tx_out, vout) = create_utxo(node); + + // Assumes tx_out has a million sats in it. + let spend_amount = Amount::from_sat(100_000); + let fee = Amount::from_sat(1000); + let change_amount = tx_out.value - spend_amount - fee; + + let inputs = vec![Input { txid, vout, sequence: None }]; + + let mut outputs = vec![]; + + // Just send back to ourself. + let spend_address = node.client.new_address().expect("failed to create new address"); + outputs.push(Output::new(spend_address, spend_amount)); + + let change_address = node + .client + .get_raw_change_address() + .expect("getrawchangeaddress") + .into_model() + .expect("GetRawChangeAddress into model") + .0 + .assume_checked(); + outputs.push(Output::new(change_address, change_amount)); + + let json: CreateRawTransaction = + node.client.create_raw_transaction(&inputs, &outputs).expect("createrawtransaction"); + let res: Result = json.clone().into_model(); + let _ = res.expect("CreateRawTransaction into model"); + let tx = json.transaction().unwrap(); + + // wallet.rs expects this call to exist, if you change it then you'll need to update the test + // `wallet__sign_raw_transaction_with_wallet__modelled`. + let json: SignRawTransaction = + node.client.sign_raw_transaction_with_wallet(&tx).expect("signrawtransactionwithwallet"); + + let res: Result = json.into_model(); + let model = res.expect("SignRawTransactionWithWallet into model"); + + // The proves we did everything correctly. + let json: SendRawTransaction = + node.client.send_raw_transaction(&model.tx).expect("sendrawtransaction"); + let res: Result = json.into_model(); + let _ = res.expect("SendRawTransaction into model"); +} + +// Manipulates raw transactions. +// +// Calls the following RPC methods: +// - create_raw_transaction +// - sign_raw_transaction_with_key (sign_raw_transaction was deprecated in v17). +// - send_raw_transaction +// +// TODO: Work out how to get a private key without using `dumpprivkey`. +#[cfg(any( + feature = "v17", + feature = "v18", + feature = "v19", + feature = "v20", + feature = "v21", + feature = "v22", +))] // In v23 dumpprivkey no longer works. +fn create_sign_with_key_send(node: &Node) { + let (addr, _tx, txid, tx_out, vout) = create_utxo(node); + + // Assumes tx_out has a million sats in it. + let spend_amount = Amount::from_sat(100_000); + let fee = Amount::from_sat(1000); + let change_amount = tx_out.value - spend_amount - fee; + + let inputs = vec![Input { txid, vout, sequence: None }]; + + let mut outputs = vec![]; + + // Just send back to ourself. + let spend_address = node.client.new_address().expect("failed to create new address"); + outputs.push(Output::new(spend_address, spend_amount)); + + let change_address = node + .client + .get_raw_change_address() + .expect("getrawchangeaddress") + .into_model() + .expect("GetRawChangeAddress into model") + .0 + .assume_checked(); + outputs.push(Output::new(change_address, change_amount)); + + let json: CreateRawTransaction = + node.client.create_raw_transaction(&inputs, &outputs).expect("createrawtransaction"); + let res: Result = json.clone().into_model(); + let _ = res.expect("CreateRawTransaction into model"); + let tx = json.transaction().unwrap(); + + let json: DumpPrivKey = node.client.dump_priv_key(&addr).expect("dumpprivkey"); + let model: mtype::DumpPrivKey = json.into_model().expect("DumpPrivKey"); + let key = model.0; + + let json: SignRawTransaction = + node.client.sign_raw_transaction_with_key(&tx, &[key]).expect("signrawtransactionwithkey"); + let res: Result = json.into_model(); + let model = res.expect("SignRawTransaction into model"); + + // The proves we did everything correctly. + let json: SendRawTransaction = + node.client.send_raw_transaction(&model.tx).expect("sendrawtransaction"); + let res: Result = json.into_model(); + let _ = res.expect("SendRawTransaction into model"); +} + +// Manipulates raw transactions. +// +// Calls the following RPC methods: +// - fund_raw_transaction +// - sign_raw_transaction_with_wallet (sign_raw_transaction was deprecated in v17). +// - send_raw_transaction +fn create_fund_sign_send(node: &Node) { + let (_addr, _tx, txid, _tx_out, vout) = create_utxo(node); + + // We need to add an input so that transaction is consensus encoded to hex correctly (because of + // different encoding for segwit and non-segwit transactions). + let inputs = vec![Input { txid, vout, sequence: None }]; + let mut outputs = vec![]; + + let spend_amount = Amount::from_sat(50_00_000_000); + // Just send back to ourself. + let spend_address = node.client.new_address().expect("failed to create new address"); + outputs.push(Output::new(spend_address, spend_amount)); + + let json: CreateRawTransaction = + node.client.create_raw_transaction(&inputs, &outputs).expect("createrawtransaction"); + let res: Result = json.clone().into_model(); + let _ = res.expect("CreateRawTransaction into model"); + let tx = json.transaction().unwrap(); + + let json: FundRawTransaction = + node.client.fund_raw_transaction(&tx).expect("fundrawtransaction"); + let res: Result = json.clone().into_model(); + let _ = res.expect("FundRawTransaction into model"); + let funded = json.transaction().unwrap(); + + // This method is from the wallet section. + let json = node.client.sign_raw_transaction_with_wallet(&funded).expect("signrawtransactionwithwallet"); + + // The proves we did everything correctly. + let model = json.into_model().expect("SignRawTransactionWithWallet into model"); + let _ = node.client.send_raw_transaction(&model.tx).expect("createrawtransaction"); +} + +// Creates a transaction using client to do RPC call `create_raw_transaction`. +fn create_a_raw_transaction(node: &Node) -> Transaction { + let (_addr, _tx, txid, tx_out, vout) = create_utxo(&node); + + // Assumes tx_out has a million sats in it. + let spend_amount = Amount::from_sat(100_000); + let fee = Amount::from_sat(1000); + let change_amount = tx_out.value - spend_amount - fee; + + let inputs = vec![Input { txid, vout, sequence: None }]; + + let mut outputs = vec![]; + + // Just send back to ourself. + let spend_address = node.client.new_address().expect("failed to create new address"); + outputs.push(Output::new(spend_address, spend_amount)); + + let change_address = node + .client + .get_raw_change_address() + .expect("getrawchangeaddress") + .into_model() + .expect("GetRawChangeAddress into model") + .0 + .assume_checked(); + outputs.push(Output::new(change_address, change_amount)); + + let json: CreateRawTransaction = + node.client.create_raw_transaction(&inputs, &outputs).expect("createrawtransaction"); + let res: Result = json.clone().into_model(); + let _ = res.expect("CreateRawTransaction into model"); + json.transaction().unwrap() +} + +// Sends a transaction, mines a block then grabs a million sat UTXO from the mined transaction. +fn create_utxo( + node: &Node, +) -> (bitcoin::Address, bitcoin::Transaction, bitcoin::Txid, bitcoin::TxOut, u64) { + // TODO: We should probably pass this into `create_mined_transaction`. + const MILLION_SATS: bitcoin::Amount = bitcoin::Amount::from_sat(1000000); + + let (addr, tx) = node.create_mined_transaction(); // A million sat transaction. + let txid = tx.compute_txid(); + + // We don't know which output is the spend and which is the change + // so we check for value of MILLION_SATS. + let tx_out = node + .client + .get_tx_out(txid, 0) + .expect("gettxout") + .into_model() + .expect("GetTxOut into model") + .tx_out; + + let (tx_out, vout) = if tx_out.value == MILLION_SATS { + (tx_out, 0) + } else { + let out = node + .client + .get_tx_out(txid, 1) + .expect("gettxout") + .into_model() + .expect("GetTxOut into model") + .tx_out; + (out, 1) + }; + (addr, tx, txid, tx_out, vout) +} + +// Creates a PSBT using client to do RPC call `create_psbt`. +fn create_a_psbt(node: &Node) -> bitcoin::Psbt { + let (_addr, _tx, txid, tx_out, vout) = create_utxo(node); + + // Assumes tx_out has a million sats in it. + let spend_amount = Amount::from_sat(100_000); + let fee = Amount::from_sat(1000); + let change_amount = tx_out.value - spend_amount - fee; + + let inputs = vec![Input { txid, vout, sequence: None }]; + + let mut outputs = vec![]; + + // Just send back to ourself. + let spend_address = node.client.new_address().expect("failed to create new address"); + outputs.push(Output::new(spend_address, spend_amount)); + + let change_address = node + .client + .get_raw_change_address() + .expect("getrawchangeaddress") + .into_model() + .expect("GetRawChangeAddress into model") + .0 + .assume_checked(); + outputs.push(Output::new(change_address, change_amount)); + + let json: CreatePsbt = node.client.create_psbt(&inputs, &outputs).expect("createpsbt"); + let res: Result = json.clone().into_model(); + let psbt = res.expect("CreatePsbt into model"); + psbt.0 +} diff --git a/integration_test/tests/wallet.rs b/integration_test/tests/wallet.rs index 59182219..61b2f53e 100644 --- a/integration_test/tests/wallet.rs +++ b/integration_test/tests/wallet.rs @@ -27,7 +27,7 @@ fn wallet__add_multisig_address__modelled() { .client .add_multisig_address_with_addresses(nrequired, vec![add1, add2]) .expect("addmultisigaddress"); - let model: Result = json.into_model(); + let model: Result = json.into_model(); model.unwrap(); } @@ -246,6 +246,10 @@ fn wallet__load_wallet__modelled() { create_load_unload_wallet(); } +// This is tested in raw_transactions.rs `create_sign_send()`. +#[test] +fn wallet__sign_raw_transaction_with_wallet__modelled() {} + #[test] fn wallet__unload_wallet() { create_load_unload_wallet(); diff --git a/types/src/lib.rs b/types/src/lib.rs index f080b00f..ba54a48f 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -13,6 +13,9 @@ extern crate alloc; // TODO: Consider updating https://en.bitcoin.it/wiki/API_reference_%28JSON-RPC%29 when this is complete. +mod error; +mod psbt; + // JSON types, for each specific version of `bitcoind`. pub mod v17; pub mod v18; @@ -27,14 +30,15 @@ pub mod v26; pub mod v27; pub mod v28; -mod error; // JSON types that model _all_ `bitcoind` versions. pub mod model; use core::fmt; use bitcoin::amount::ParseAmountError; -use bitcoin::{Amount, FeeRate}; +use bitcoin::hex::{self, FromHex as _}; +use bitcoin::{Amount, FeeRate, ScriptBuf, Witness}; +use serde::{Deserialize, Serialize}; /// Converts an `i64` numeric type to a `u32`. /// @@ -91,3 +95,115 @@ fn btc_per_kb(btc_per_kb: f64) -> Result, ParseAmountError> { Ok(rate) } + +// TODO: Remove this function if a new `Witness` constructor gets added. +// https://github.com/rust-bitcoin/rust-bitcoin/issues/4350 +fn witness_from_hex_slice>(witness: &[T]) -> Result { + let bytes: Vec> = + witness.iter().map(|hex| Vec::from_hex(hex.as_ref())).collect::>()?; + Ok(Witness::from_slice(&bytes)) +} + +/// Gets the compact size encoded value from `slice` and moves slice past the encoding. +/// +/// Caller to guarantee that the encoding is well formed. Well formed is defined as: +/// +/// * Being at least long enough. +/// * Containing a minimal encoding. +/// +/// # Panics +/// +/// * Panics in release mode if the `slice` does not contain a valid minimal compact size encoding. +/// * Panics in debug mode if the encoding is not minimal (referred to as "non-canonical" in Core). +// This is copied from the `bitcoin-internals::compact_size` module. +pub fn compact_size_decode(slice: &mut &[u8]) -> u64 { + if slice.is_empty() { + panic!("tried to decode an empty slice"); + } + + match slice[0] { + 0xFF => { + const SIZE: usize = 9; + if slice.len() < SIZE { + panic!("slice too short, expected at least 9 bytes"); + }; + + let mut bytes = [0_u8; SIZE - 1]; + bytes.copy_from_slice(&slice[1..SIZE]); + + let v = u64::from_le_bytes(bytes); + debug_assert!(v > u32::MAX.into(), "non-minimal encoding of a u64"); + *slice = &slice[SIZE..]; + v + } + 0xFE => { + const SIZE: usize = 5; + if slice.len() < SIZE { + panic!("slice too short, expected at least 5 bytes"); + }; + + let mut bytes = [0_u8; SIZE - 1]; + bytes.copy_from_slice(&slice[1..SIZE]); + + let v = u32::from_le_bytes(bytes); + debug_assert!(v > u16::MAX.into(), "non-minimal encoding of a u32"); + *slice = &slice[SIZE..]; + u64::from(v) + } + 0xFD => { + const SIZE: usize = 3; + if slice.len() < SIZE { + panic!("slice too short, expected at least 3 bytes"); + }; + + let mut bytes = [0_u8; SIZE - 1]; + bytes.copy_from_slice(&slice[1..SIZE]); + + let v = u16::from_le_bytes(bytes); + debug_assert!(v >= 0xFD, "non-minimal encoding of a u16"); + *slice = &slice[SIZE..]; + u64::from(v) + } + n => { + *slice = &slice[1..]; + u64::from(n) + } + } +} + +/// Data returned by Core for a script pubkey. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct ScriptPubkey { + /// Script assembly. + pub asm: String, + /// Script hex. + pub hex: String, + #[serde(rename = "reqSigs")] + pub req_sigs: Option, + /// The type, eg pubkeyhash. + #[serde(rename = "type")] + pub type_: String, + /// Array of bitcoin address. + pub addresses: Option>, +} + +impl ScriptPubkey { + fn script_buf(&self) -> Result { + ScriptBuf::from_hex(&self.hex) + } +} + +/// Data returned by Core for a script signature. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct ScriptSig { + /// Assembly representation of the script. + pub asm: String, + /// Hex representation of the script. + pub hex: String, +} + +impl ScriptSig { + pub fn script_buf(&self) -> Result { + ScriptBuf::from_hex(&self.hex) + } +} diff --git a/types/src/model/mod.rs b/types/src/model/mod.rs index 0a00202f..a0efa138 100644 --- a/types/src/model/mod.rs +++ b/types/src/model/mod.rs @@ -36,8 +36,12 @@ pub use self::{ }, network::{GetNetworkInfo, GetNetworkInfoAddress, GetNetworkInfoNetwork}, raw_transactions::{ - CreateRawTransaction, FundRawTransaction, SendRawTransaction, SubmitPackage, - SubmitPackageTxResult, SubmitPackageTxResultFees, + AnalyzePsbt, AnalyzePsbtInput, AnalyzePsbtInputMissing, CombinePsbt, CombineRawTransaction, + ConvertToPsbt, CreatePsbt, CreateRawTransaction, DecodePsbt, DecodeRawTransaction, + DecodeScript, DescriptorProcessPsbt, FinalizePsbt, FundRawTransaction, GetRawTransaction, + GetRawTransactionVerbose, JoinPsbts, MempoolAcceptance, SendRawTransaction, SignFail, + SignRawTransaction, SubmitPackage, SubmitPackageTxResult, SubmitPackageTxResultFees, + TestMempoolAccept, UtxoUpdatePsbt, }, wallet::{ AddMultisigAddress, AddressInformation, AddressLabel, AddressPurpose, Bip125Replaceable, @@ -49,7 +53,6 @@ pub use self::{ ListReceivedByAddress, ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockTransaction, ListTransactions, ListTransactionsItem, ListUnspentItem, ListWallets, LoadWallet, RescanBlockchain, ScriptType, SendMany, SendToAddress, - SignErrorData, SignMessage, SignRawTransactionWithWallet, TransactionCategory, - UnloadWallet, WalletCreateFundedPsbt, WalletProcessPsbt, + SignMessage, TransactionCategory, UnloadWallet, WalletCreateFundedPsbt, WalletProcessPsbt, }, }; diff --git a/types/src/model/raw_transactions.rs b/types/src/model/raw_transactions.rs index 405c62f8..c5f44fbd 100644 --- a/types/src/model/raw_transactions.rs +++ b/types/src/model/raw_transactions.rs @@ -7,13 +7,124 @@ use alloc::collections::BTreeMap; -use bitcoin::{Amount, FeeRate, Transaction, Txid, Wtxid}; +use bitcoin::address::{Address, NetworkUnchecked}; +use bitcoin::hashes::{hash160, sha256}; +use bitcoin::{Amount, BlockHash, FeeRate, Psbt, ScriptBuf, Sequence, Transaction, Txid, Wtxid}; use serde::{Deserialize, Serialize}; +/// Models the result of JSON-RPC method `analyzepsbt`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct AnalyzePsbt { + /// Array of input objects. + pub inputs: Vec, + /// Estimated vsize of the final signed transaction. + pub estimated_vsize: Option, + /// Estimated feerate of the final signed transaction in BTC/kB. + /// + /// Shown only if all UTXO slots in the PSBT have been filled. + pub estimated_fee_rate: Option, + /// The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled. + pub fee: Option, + /// Role of the next person that this psbt needs to go to. + pub next: String, +} + +/// Represents an input in a PSBT operation. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct AnalyzePsbtInput { + /// Whether a UTXO is provided. + pub has_utxo: bool, + /// Whether the input is finalized. + pub is_final: bool, + /// Things that are missing that are required to complete this input. + pub missing: Option, + /// Role of the next person that this input needs to go to. + pub next: Option, +} + +/// Represents missing elements required to complete an input. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct AnalyzePsbtInputMissing { + /// Public key IDs of public keys whose BIP 32 derivation paths are missing. + pub pubkeys: Vec, + /// Public key IDs of public keys whose signatures are missing. + pub signatures: Vec, + /// Hash160 of the redeem script that is missing. + pub redeem_script: Option, + /// SHA256 of the witness script that is missing. + pub witness_script: Option, +} + +/// Models the result of JSON-RPC method `combinepsbt`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct CombinePsbt(pub Psbt); + +/// Models the result of JSON-RPC method `combinerawtransaction`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct CombineRawTransaction(pub Transaction); + +/// Models the result of JSON-RPC method `converttopsbt`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct ConvertToPsbt(pub Psbt); + +/// Models the result of JSON-RPC method `createpsbt`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct CreatePsbt(pub Psbt); + /// Models the result of JSON-RPC method `createrawtransaction`. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct CreateRawTransaction(pub Transaction); +/// Models the result of JSON-RPC method `decodepsbt`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct DecodePsbt { + /// The decoded PSBT. + pub psbt: Psbt, + /// The transaction fee paid if all UTXOs slots in the PSBT have been filled. + pub fee: Option, +} + +/// Models the result of JSON-RPC method `decoderawtransaction`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct DecodeRawTransaction(pub Transaction); + +/// Models the result of JSON-RPC method `decodescript`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct DecodeScript { + /// The `scriptPubkey`. + pub script_pubkey: Option, + /// The output type. + pub type_: Option, + /// The required signatures. + pub required_signatures: Option, + /// List of bitcoin addresses. + pub addresses: Vec>, + /// Address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH). + pub p2sh: Option>, +} + +/// Models the result of JSON-RPC method `descriptorprocesspsbt`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct DescriptorProcessPsbt { + /// The decoded PSBT. + pub psbt: Psbt, + /// If the transaction has a complete set of signatures. + pub complete: bool, + /// The transaction if complete. + pub tx: Option, +} + +/// Models the result of JSON-RPC method `finalizepsbt`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct FinalizePsbt { + /// The partially signed transaction if not extracted. + pub psbt: Psbt, + /// The transaction if extracted. + pub tx: Option, + /// If the transaction has a complete set of signatures. + pub complete: bool, +} + /// Models the result of JSON-RPC method `fundrawtransaction`. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct FundRawTransaction { @@ -25,10 +136,62 @@ pub struct FundRawTransaction { pub change_position: i64, } +/// Models the result of JSON-RPC method `getrawtransaction` with verbose set to `false`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct GetRawTransaction(pub Transaction); + +/// Models the result of JSON-RPC method `getrawtransaction` with verbose set to `true`. +/// Result of JSON-RPC method `getrawtransaction` +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct GetRawTransactionVerbose { + /// Whether specified block is in the active chain or not (only present with explicit "blockhash" argument). + pub in_active_chain: Option, + /// The transaction (encapsulates the other data returned by original RPC call). + pub transaction: Transaction, + /// The block hash (`None` for mempool transactions). + pub block_hash: Option, + /// The confirmations (`None` for mempool transactions). + pub confirmations: Option, + /// The transaction time in seconds since epoch (Jan 1 1970 GMT). + pub transaction_time: Option, + /// The block time in seconds since epoch (Jan 1 1970 GMT). + pub block_time: Option, +} + +/// Models the result of JSON-RPC method `joinpsbts`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct JoinPsbts(pub Psbt); + /// Models the result of JSON-RPC method `sendrawtransaction`. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct SendRawTransaction(pub Txid); +/// Models the result of JSON-RPC method `signrawtransaction`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct SignRawTransaction { + /// The raw transaction with signature(s). + pub tx: Transaction, + /// If the transaction has a complete set of signatures. + pub complete: bool, + /// Script verification errors (if there are any). + pub errors: Vec, +} + +/// Represents a script verification error. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct SignFail { + /// The referenced, previous transaction. + pub txid: Txid, + /// The index of the output to spent and used as input. + pub vout: u64, + /// The signature script. + pub script_sig: ScriptBuf, + /// Script sequence number. + pub sequence: Sequence, + /// Verification or signing error related to the input. + pub error: String, +} + /// Models the result of JSON-RPC method `submitpackage`. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct SubmitPackage { @@ -71,3 +234,25 @@ pub struct SubmitPackageTxResultFees { /// whose fees and vsizes are included in effective-feerate. pub effective_includes: Vec, } + +/// Models the result of JSON-RPC method `testmempoolaccept`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct TestMempoolAccept { + /// Test results for each raw transaction in the input array. + pub results: Vec, +} + +/// Represents a single mempool acceptance test result. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct MempoolAcceptance { + /// The transaction ID. + pub txid: Txid, + /// If the mempool allows this transaction to be inserted. + pub allowed: bool, + /// Rejection string (only present when 'allowed' is false). + pub reject_reason: Option, +} + +/// Models the result of JSON-RPC method `utxoupdatepsbt;`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct UtxoUpdatePsbt(pub Psbt); diff --git a/types/src/model/wallet.rs b/types/src/model/wallet.rs index 6e73f66f..84702615 100644 --- a/types/src/model/wallet.rs +++ b/types/src/model/wallet.rs @@ -11,7 +11,7 @@ use bitcoin::address::NetworkUnchecked; use bitcoin::hashes::hash160; use bitcoin::{ bip32, ecdsa, Address, Amount, BlockHash, FeeRate, PrivateKey, Psbt, PublicKey, ScriptBuf, - Sequence, SignedAmount, Transaction, Txid, WitnessProgram, WitnessVersion, + SignedAmount, Transaction, Txid, WitnessProgram, WitnessVersion, }; use serde::{Deserialize, Serialize}; @@ -631,31 +631,6 @@ pub struct SendToAddress { #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct SignMessage(pub ecdsa::Signature); -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct SignRawTransactionWithWallet { - /// The raw transaction with signature(s). - pub raw_transaction: Transaction, - /// If the transaction has a complete set of signatures. - pub complete: bool, - /// Script verification errors (if there are any). - pub errors: Vec, // 'Data' suffix to differentiate this from a normal error type. -} - -/// Returned as part of `signrawtransactionwithwallet`. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct SignErrorData { - /// The txid of the previous transaction. - pub txid: Txid, - /// The index of the output to spent and used as input. - pub vout: u32, - /// The signature script. - pub script_sig: ScriptBuf, - /// Script sequence number. - pub sequence: Sequence, - /// Verification or signing error related to the input. - pub error: String, -} - /// Models the result of JSON-RPC method `unloadwallet`. /// /// Core version v0.21 onwards. diff --git a/types/src/psbt/README.md b/types/src/psbt/README.md new file mode 100644 index 00000000..0a40a5b7 --- /dev/null +++ b/types/src/psbt/README.md @@ -0,0 +1,9 @@ +# PSBT + +A bunch of types and conversion code for supporting PSBTs (and +possibly raw transactions). This stuff could have been in one of the +`raw_transaction` modules but reaching into `v17` from, for example, +`v23::raw_transaction` doesn't seem right. + +Note that because PSBT was designed to be backwards compatible this +stuff should work with all versions. diff --git a/types/src/psbt/error.rs b/types/src/psbt/error.rs new file mode 100644 index 00000000..6603afc8 --- /dev/null +++ b/types/src/psbt/error.rs @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: CC0-1.0 + +use core::fmt; + +use bitcoin::amount::ParseAmountError; +use bitcoin::{amount, bip32, ecdsa, hex, key}; + +use crate::error::write_err; + +/// Error when converting a `RawTransaction` type into the model type. +#[derive(Debug)] +pub enum RawTransactionError { + /// Conversion of one of the transaction inputs failed. + Inputs(RawTransactionInputError), + /// Conversion of one of the transaction outputs failed. + Outputs(RawTransactionOutputError), +} + +impl fmt::Display for RawTransactionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use RawTransactionError as E; + + match *self { + E::Inputs(ref e) => + write_err!(f, "conversion of one of the transaction inputs failed"; e), + E::Outputs(ref e) => + write_err!(f, "conversion of one of the transaction outputs failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for RawTransactionError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use RawTransactionError as E; + + match *self { + E::Inputs(ref e) => Some(e), + E::Outputs(ref e) => Some(e), + } + } +} + +/// Error when converting a `RawTransactionInput` type into a `TxIn`. +#[derive(Debug)] +pub enum RawTransactionInputError { + /// Conversion of the input `txid` field failed. + Txid(hex::HexToArrayError), + /// Conversion of the input `script_sig` field failed. + ScriptSig(hex::HexToBytesError), + /// Conversion of one of the `witness` hex strings failed. + Witness(hex::HexToBytesError), +} + +impl fmt::Display for RawTransactionInputError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use RawTransactionInputError as E; + + match *self { + E::Txid(ref e) => write_err!(f, "conversion of the input `txid` field failed"; e), + E::ScriptSig(ref e) => + write_err!(f, "conversion of the input `script_sig` field failed"; e), + E::Witness(ref e) => + write_err!(f, "conversion of one of the `witness` hex strings failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for RawTransactionInputError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use RawTransactionInputError as E; + + match *self { + E::Txid(ref e) => Some(e), + E::ScriptSig(ref e) => Some(e), + E::Witness(ref e) => Some(e), + } + } +} + +/// Error when converting a `RawTransactionOutput` type into a `TxIn`. +#[derive(Debug)] +pub enum RawTransactionOutputError { + /// Conversion of the output `value` field failed. + Value(amount::ParseAmountError), + /// Conversion of the output `script_pubkey` field failed. + ScriptPubkey(hex::HexToBytesError), +} + +impl fmt::Display for RawTransactionOutputError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use RawTransactionOutputError as E; + + match *self { + E::Value(ref e) => write_err!(f, "conversion of the output `value` field failed"; e), + E::ScriptPubkey(ref e) => + write_err!(f, "conversion of the output `script_pubkey` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for RawTransactionOutputError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use RawTransactionOutputError as E; + + match *self { + E::Value(ref e) => Some(e), + E::ScriptPubkey(ref e) => Some(e), + } + } +} + +/// Error when converting a `WitnessUtxo` type into a `TxOut`. +#[derive(Debug)] +pub enum WitnessUtxoError { + /// Conversion of the `amount` field failed. + Amount(ParseAmountError), + /// Conversion of the `script_pubkey` field failed. + ScriptPubkey(hex::HexToBytesError), +} + +impl fmt::Display for WitnessUtxoError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use WitnessUtxoError as E; + + match *self { + E::Amount(ref e) => write_err!(f, "conversion of the `amount` field failed"; e), + E::ScriptPubkey(ref e) => + write_err!(f, "conversion of the `script_pubkey` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for WitnessUtxoError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use WitnessUtxoError as E; + + match *self { + E::Amount(ref e) => Some(e), + E::ScriptPubkey(ref e) => Some(e), + } + } +} + +/// Error when converting one of the partial sigs key-value pairs. +#[derive(Debug)] +pub enum PartialSignatureError { + /// Error parsing public key. + PublicKey(key::ParsePublicKeyError), + /// Error parsing signature. + Signature(ecdsa::Error), +} + +impl fmt::Display for PartialSignatureError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use PartialSignatureError as E; + + match *self { + E::PublicKey(ref e) => + write_err!(f, "partial sigs key-value pair parse pubkey failed"; e), + E::Signature(ref e) => write_err!(f, "partial sigs key-value pair parse sig failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for PartialSignatureError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use PartialSignatureError as E; + + match *self { + E::PublicKey(ref e) => Some(e), + E::Signature(ref e) => Some(e), + } + } +} + +/// Error when converting BIP-32 derivation information. +#[derive(Debug)] +pub enum Bip32DerivError { + /// Conversion of the pubkey failed. + Pubkey(key::ParsePublicKeyError), + /// Conversion of the `master_fingerprint` field failed. + MasterFingerprint(hex::HexToArrayError), + /// Conversion of the `path` field failed. + Path(bip32::Error), +} + +impl fmt::Display for Bip32DerivError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Bip32DerivError as E; + + match *self { + E::Pubkey(ref e) => write_err!(f, "conversion of the pubkey failed"; e), + E::MasterFingerprint(ref e) => + write_err!(f, "conversion of the `master_fingerprint` field failed"; e), + E::Path(ref e) => write_err!(f, "conversion of the `path` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Bip32DerivError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use Bip32DerivError as E; + + match *self { + E::Pubkey(ref e) => Some(e), + E::MasterFingerprint(ref e) => Some(e), + E::Path(ref e) => Some(e), + } + } +} diff --git a/types/src/psbt/mod.rs b/types/src/psbt/mod.rs new file mode 100644 index 00000000..280b53db --- /dev/null +++ b/types/src/psbt/mod.rs @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: CC0-1.0 + +mod error; + +use std::collections::{BTreeMap, HashMap}; + +use bitcoin::hex::{self, FromHex as _}; +use bitcoin::{ + absolute, bip32, ecdsa, psbt, secp256k1, transaction, Amount, OutPoint, PublicKey, ScriptBuf, + Sequence, Transaction, TxIn, TxOut, Txid, Witness, +}; +use serde::{Deserialize, Serialize}; + +pub use self::error::{ + Bip32DerivError, PartialSignatureError, RawTransactionError, RawTransactionInputError, + RawTransactionOutputError, WitnessUtxoError, +}; +use crate::{ScriptPubkey, ScriptSig}; + +/// Represents a bitcoin transaction. +/// +/// Returned as part of `decoderawtransaction` and `decodepsbt`. +// This JSON data can be encapsulated by a `bitcoin::Transaction`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct RawTransaction { + /// The transaction id. + pub txid: String, + /// The transaction hash (differs from txid for witness transactions). + pub hash: String, + /// The transaction size in bytes. + pub size: u64, + /// The virtual transaction size (differs from size for witness transactions). + pub vsize: u64, + /// The transaction's weight (between vsize*4 - 3 and vsize*4). + pub weight: u64, + /// The version number. + pub version: i32, + /// The lock time. + #[serde(rename = "locktime")] + pub lock_time: u32, + /// Array of transaction inputs. + #[serde(rename = "vin")] + pub inputs: Vec, + /// Array of transaction outputs. + #[serde(rename = "vout")] + pub outputs: Vec, +} + +impl RawTransaction { + /// Converts this raw transaction data into a bitcoin transaction. + pub fn to_transaction(&self) -> Result { + use RawTransactionError as E; + + let version = transaction::Version::non_standard(self.version); + let lock_time = absolute::LockTime::from_consensus(self.lock_time); + let input = self + .inputs + .iter() + .map(|input| input.to_input()) + .collect::>() + .map_err(E::Inputs)?; + let output = self + .outputs + .iter() + .map(|output| output.to_output()) + .collect::>() + .map_err(E::Outputs)?; + + Ok(Transaction { version, lock_time, input, output }) + } +} + +/// Represents a transaction input. +// This JSON data can be encapsulated by a `bitcoin::TxIn`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct RawTransactionInput { + /// The transaction id. + pub txid: String, + /// The output number. + pub vout: u32, + /// The script. + #[serde(rename = "scriptSig")] + pub script_sig: ScriptSig, + /// Hex-encoded witness data (if any). + #[serde(rename = "txinwitness")] + pub txin_witness: Option>, + /// The script sequence number. + pub sequence: u32, +} + +impl RawTransactionInput { + /// Converts this raw transaction input data into a bitcoin [`TxIn`]. + pub fn to_input(&self) -> Result { + use RawTransactionInputError as E; + + let txid = self.txid.parse::().map_err(E::Txid)?; + let script_sig = self.script_sig.script_buf().map_err(E::ScriptSig)?; + + let witness = match &self.txin_witness { + Some(v) => crate::witness_from_hex_slice(v).map_err(E::Witness)?, + None => Witness::new(), + }; + + Ok(TxIn { + previous_output: OutPoint { txid, vout: self.vout }, + script_sig, + sequence: Sequence::from_consensus(self.sequence), + witness, + }) + } +} + +/// Represents a transaction output. +// This JSON data can be encapsulated by a `bitcoin::TxOut` + index. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct RawTransactionOutput { + /// The value in BTC. + pub value: f64, + /// Index number. + #[serde(rename = "n")] + pub index: u64, + /// The script pubkey. + #[serde(rename = "scriptPubKey")] + pub script_pubkey: ScriptPubkey, +} + +impl RawTransactionOutput { + /// Converts this raw UTXO data into a bitcoin `TxOut`. + pub fn to_output(&self) -> Result { + use RawTransactionOutputError as E; + + let value = Amount::from_btc(self.value).map_err(E::Value)?; + let script_pubkey = self.script_pubkey.script_buf().map_err(E::ScriptPubkey)?; + + Ok(TxOut { value, script_pubkey }) + } +} + +/// Transaction output for witness UTXOs. +// This JSON data can be encapsulated by a `bitcoin::TxOut`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct WitnessUtxo { + /// The value in BTC. + pub amount: f64, + /// The scriptPubKey. + #[serde(rename = "scriptPubKey")] + pub script_pubkey: ScriptPubkey, +} + +impl WitnessUtxo { + /// Converts this raw UTXO data into a bitcoin `TxOut`. + pub fn to_tx_out(&self) -> Result { + use WitnessUtxoError as E; + + let value = Amount::from_btc(self.amount).map_err(E::Amount)?; + let script_pubkey = self.script_pubkey.script_buf().map_err(E::ScriptPubkey)?; + + Ok(TxOut { value, script_pubkey }) + } +} + +/// A script returned as part of a PSBT input or output. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct PsbtScript { + /// The asm. + pub asm: String, + /// The hex. + pub hex: String, + /// The type, eg 'pubkeyhash'. + #[serde(rename = "type")] + pub script_type: String, +} + +impl PsbtScript { + /// Parses the hex into a `ScriptBuf`. + pub fn script_buf(&self) -> Result { + ScriptBuf::from_hex(&self.hex) + } +} + +/// BIP-32 derivation information. +// WARNING: The bip32_derivs in PSBT input list seems to change shape +// a bunch of times over the versions. By v23 it looks like this. +// +// This JSON data can be encapsulated as a map item in bitcoin::psbt::{Input, Output} +// bip32_derivation: BTreeMap, +// KeySource = (Fingerprint, DerivationPath); +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct Bip32Deriv { + /// The public key this path corresponds to. + pub pubkey: String, + /// The fingerprint of the master key. + pub master_fingerprint: String, + /// The path. + pub path: String, +} + +/// The key source data for a BIP-32 derivation. +// In v17 the BIP-32 derivation for inputs is a map of pubkey to this type. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct InputKeySource { + /// The fingerprint of the master key. + pub master_fingerprint: String, + /// The path. + pub path: String, +} + +/// Final script data. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct FinalScript { + /// The asm. + pub asm: String, + /// The hex. + pub hex: String, +} + +/// Converts a map of unknown key-value pairs. +pub fn into_unknown( + hash_map: HashMap, +) -> Result>, hex::HexToBytesError> { + let mut map = BTreeMap::default(); + for (k, v) in hash_map.iter() { + // FIXME: This is best guess, I (Tobin) don't actually know what + // is in the hex string returned by Core. + let key = Vec::from_hex(k)?; + let value = Vec::from_hex(v)?; + + // rust-bitcoin separates out the key type. + let mut p = key.as_slice(); + let _ = crate::compact_size_decode(&mut p); // Moves p past the keylen integer. + let type_value = crate::compact_size_decode(&mut p); + + // In the next release of rust-bitcoin this is changed to a u64. + // Yes this looses data - c'est la vie. + let type_value: u8 = type_value as u8; + + let key = psbt::raw::Key { type_value, key }; + map.insert(key, value); + } + Ok(map) +} + +/// Converts a map of partial signature key-value pairs. +pub fn into_partial_signatures( + hash_map: HashMap, +) -> Result, PartialSignatureError> { + use PartialSignatureError as E; + + let mut map = BTreeMap::default(); + for (k, v) in hash_map.iter() { + let pubkey = k.parse::().map_err(E::PublicKey)?; + let signature = v.parse::().map_err(E::Signature)?; + map.insert(pubkey, signature); + } + Ok(map) +} + +/// Converts a hash map of BIP-32 derivation data into a map suitable for use with `psbt::Psbt`. +pub fn map_into_bip32_derivation( + hash_map: HashMap, +) -> Result, Bip32DerivError> { + use bip32::{DerivationPath, Fingerprint}; + use Bip32DerivError as E; + + let mut map = BTreeMap::default(); + for (k, v) in hash_map.iter() { + let pubkey = k.parse::().map_err(E::Pubkey)?; + let fingerprint = + Fingerprint::from_hex(&v.master_fingerprint).map_err(E::MasterFingerprint)?; + let path = v.path.parse::().map_err(E::Path)?; + + map.insert(pubkey.inner, (fingerprint, path)); + } + Ok(map) +} + +/// Converts a vector map of BIP-32 derivation data into a map suitable for use with `psbt::Psbt`. +pub fn vec_into_bip32_derivation( + v: Vec, +) -> Result, Bip32DerivError> { + use bip32::{DerivationPath, Fingerprint}; + use Bip32DerivError as E; + + let mut map = BTreeMap::default(); + for deriv in v.iter() { + let pubkey = deriv.pubkey.parse::().map_err(E::Pubkey)?; + let fingerprint = + Fingerprint::from_hex(&deriv.master_fingerprint).map_err(E::MasterFingerprint)?; + let path = deriv.path.parse::().map_err(E::Path)?; + + map.insert(pubkey.inner, (fingerprint, path)); + } + Ok(map) +} diff --git a/types/src/v17/blockchain/into.rs b/types/src/v17/blockchain/into.rs index aa8a9c90..f26004c1 100644 --- a/types/src/v17/blockchain/into.rs +++ b/types/src/v17/blockchain/into.rs @@ -493,13 +493,14 @@ impl GetTxOut { script_pubkey: ScriptBuf::from_hex(&self.script_pubkey.hex).map_err(E::ScriptPubkey)?, }; - let addresses = self - .script_pubkey - .addresses - .into_iter() - .map(|address| address.parse::>()) - .collect::, _>>() - .map_err(E::Addresses)?; + let addresses = match self.script_pubkey.addresses { + Some(addresses) => addresses + .into_iter() + .map(|address| address.parse::>()) + .collect::, _>>() + .map_err(E::Addresses)?, + None => vec![], + }; Ok(model::GetTxOut { best_block, diff --git a/types/src/v17/blockchain/mod.rs b/types/src/v17/blockchain/mod.rs index 81056d10..c854ceb1 100644 --- a/types/src/v17/blockchain/mod.rs +++ b/types/src/v17/blockchain/mod.rs @@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize}; // TODO: Remove wildcard, use explicit types. pub use self::error::*; -use crate::model; +use crate::{model, ScriptPubkey}; /// Result of JSON-RPC method `getbestblockhash`. /// @@ -608,22 +608,6 @@ pub struct GetTxOut { pub coinbase: bool, } -/// A script pubkey. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct ScriptPubkey { - /// Script assembly. - pub asm: String, - /// Script hex. - pub hex: String, - #[serde(rename = "reqSigs")] - pub req_sigs: i64, - /// The type, eg pubkeyhash. - #[serde(rename = "type")] - pub type_: String, - /// Array of bitcoin address. - pub addresses: Vec, -} - /// Result of JSON-RPC method `gettxoutsetinfo`. /// /// > gettxoutsetinfo diff --git a/types/src/v17/mod.rs b/types/src/v17/mod.rs index 02c7ceef..38d6d5f5 100644 --- a/types/src/v17/mod.rs +++ b/types/src/v17/mod.rs @@ -105,21 +105,21 @@ //! //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | finalizepsbt | todo | -//! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | finalizepsbt | done (untested) | +//! | fundrawtransaction | done | +//! | getrawtransaction | done | //! | sendrawtransaction | done | -//! | signrawtransaction | todo | -//! | signrawtransactionwithkey | todo | -//! | testmempoolaccept | todo | +//! | signrawtransaction | done | +//! | signrawtransactionwithkey | done | +//! | testmempoolaccept | done (untested) | //! //! //! @@ -192,7 +192,7 @@ //! | sethdseed | omitted | //! | settxfee | omitted | //! | signmessage | done (untested) | -//! | signrawtransactionwithwallet | done (untested) | +//! | signrawtransactionwithwallet | done | //! | unloadwallet | done | //! | walletcreatefundedpsbt | done (untested) | //! | walletlock | omitted | @@ -217,7 +217,7 @@ mod control; mod generating; mod mining; mod network; -mod raw_transactions; +pub(crate) mod raw_transactions; mod util; mod wallet; mod zmq; @@ -234,8 +234,7 @@ pub use self::{ GetMempoolDescendantsVerbose, GetMempoolEntry, GetMempoolInfo, GetMempoolInfoError, GetRawMempool, GetRawMempoolVerbose, GetTxOut, GetTxOutError, GetTxOutSetInfo, GetTxOutSetInfoError, MapMempoolEntryError, MempoolEntry, MempoolEntryError, - MempoolEntryFees, MempoolEntryFeesError, ScriptPubkey, Softfork, SoftforkReject, - VerifyTxOutProof, + MempoolEntryFees, MempoolEntryFeesError, Softfork, SoftforkReject, VerifyTxOutProof, }, control::{GetMemoryInfoStats, Locked, Logging}, generating::{Generate, GenerateToAddress}, @@ -249,7 +248,13 @@ pub use self::{ PeerInfo, UploadTarget, }, raw_transactions::{ - CreateRawTransaction, FundRawTransaction, FundRawTransactionError, SendRawTransaction, + CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, CreateRawTransaction, + DecodePsbt, DecodePsbtError, DecodeRawTransaction, DecodeScript, DecodeScriptError, + FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError, + GetRawTransaction, GetRawTransactionVerbose, GetRawTransactionVerboseError, + MempoolAcceptance, PsbtInput, PsbtInputError, PsbtOutput, PsbtOutputError, + SendRawTransaction, SignFail, SignFailError, SignRawTransaction, SignRawTransactionError, + TestMempoolAccept, }, wallet::{ AddMultisigAddress, AddMultisigAddressError, AddressInformation, BumpFee, BumpFeeError, @@ -264,9 +269,14 @@ pub use self::{ ListSinceBlockTransaction, ListSinceBlockTransactionError, ListTransactions, ListTransactionsItem, ListTransactionsItemError, ListUnspent, ListUnspentItem, ListUnspentItemError, ListWallets, LoadWallet, RescanBlockchain, SendMany, SendToAddress, - SignErrorData, SignErrorDataError, SignMessage, SignRawTransactionWithWallet, - SignRawTransactionWithWalletError, TransactionCategory, WalletCreateFundedPsbt, - WalletCreateFundedPsbtError, WalletProcessPsbt, + SignMessage, TransactionCategory, WalletCreateFundedPsbt, WalletCreateFundedPsbtError, + WalletProcessPsbt, }, zmq::GetZmqNotifications, }; +#[doc(inline)] +pub use crate::psbt::{ + Bip32Deriv, Bip32DerivError, FinalScript, InputKeySource, PartialSignatureError, PsbtScript, + RawTransaction, RawTransactionError, RawTransactionInput, RawTransactionInputError, + RawTransactionOutput, RawTransactionOutputError, WitnessUtxo, WitnessUtxoError, +}; diff --git a/types/src/v17/raw_transactions/error.rs b/types/src/v17/raw_transactions/error.rs index 32175a32..5bb469a9 100644 --- a/types/src/v17/raw_transactions/error.rs +++ b/types/src/v17/raw_transactions/error.rs @@ -4,8 +4,238 @@ use core::fmt; use bitcoin::amount::ParseAmountError; use bitcoin::consensus::encode; +use bitcoin::psbt::PsbtParseError; +use bitcoin::{address, hex, sighash}; use crate::error::write_err; +use crate::psbt::{ + Bip32DerivError, PartialSignatureError, RawTransactionError, RawTransactionInputError, + RawTransactionOutputError, WitnessUtxoError, +}; + +/// Error when converting a `DecodePsbt` type into the model type. +#[derive(Debug)] +pub enum DecodePsbtError { + /// Conversion of the `tx` field failed. + Tx(RawTransactionError), + /// Conversion of one the map items in the `unknown` field failed. + Unknown(hex::HexToBytesError), + /// Conversion of one of the PSBT inputs failed. + Inputs(PsbtInputError), + /// Conversion of one of the PSBT outputs failed. + Outputs(PsbtOutputError), +} + +impl fmt::Display for DecodePsbtError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use DecodePsbtError as E; + + match *self { + E::Tx(ref e) => write_err!(f, "conversion of raw transaction data field failed"; e), + E::Unknown(ref e) => + write_err!(f, "conversion of one the map items in the `unknown` field failed"; e), + E::Inputs(ref e) => write_err!(f, "conversion of one of the PSBT inputs failed"; e), + E::Outputs(ref e) => write_err!(f, "conversion of one of the PSBT outputs failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DecodePsbtError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use DecodePsbtError as E; + + match *self { + E::Tx(ref e) => Some(e), + E::Unknown(ref e) => Some(e), + E::Inputs(ref e) => Some(e), + E::Outputs(ref e) => Some(e), + } + } +} + +/// Error when converting one of the `DecodePsbt` inputs failed. +#[derive(Debug)] +pub enum PsbtInputError { + /// Conversion of the `non_witness_utxo` field failed. + NonWitnessUtxo(RawTransactionError), + /// Conversion of the `witness_utxo` field failed. + WitnessUtxo(WitnessUtxoError), + /// Conversion of the `partial_signatures` field failed. + PartialSignatures(PartialSignatureError), + /// Conversion of the `sighash` field failed. + Sighash(sighash::SighashTypeParseError), + /// Conversion of the `redeem_script` field failed. + RedeemScript(hex::HexToBytesError), + /// Conversion of the `witness_script` field failed. + WitnessScript(hex::HexToBytesError), + /// Conversion of the `bip32_derivs` field failed. + Bip32Derivs(Bip32DerivError), + /// Conversion of the `final_script_sig` field failed. + FinalScriptSig(hex::HexToBytesError), + /// Conversion of the `final_script_witness` field failed. + FinalScriptWitness(hex::HexToBytesError), + /// Conversion of the `unknown` field failed. + Unknown(hex::HexToBytesError), +} + +impl fmt::Display for PsbtInputError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use PsbtInputError as E; + + match *self { + E::NonWitnessUtxo(ref e) => + write_err!(f, "conversion of the `non_witness_utxo` field failed"; e), + E::WitnessUtxo(ref e) => + write_err!(f, "conversion of the `witness_utxo` field failed"; e), + E::PartialSignatures(ref e) => + write_err!(f, "conversion of the `partial_signatures` field failed"; e), + E::Sighash(ref e) => write_err!(f, "conversion of the `sighash` field failed"; e), + E::RedeemScript(ref e) => + write_err!(f, "conversion of the `redeem_script` field failed"; e), + E::WitnessScript(ref e) => + write_err!(f, "conversion of the `witness_script` field failed"; e), + E::Bip32Derivs(ref e) => + write_err!(f, "conversion of the `bip32_derivs` field failed"; e), + E::FinalScriptSig(ref e) => + write_err!(f, "conversion of the `final_script_sig` field failed"; e), + E::FinalScriptWitness(ref e) => + write_err!(f, "conversion of the `final_script_witness` field failed"; e), + E::Unknown(ref e) => write_err!(f, "conversion of the `unknown` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for PsbtInputError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use PsbtInputError as E; + + match *self { + E::NonWitnessUtxo(ref e) => Some(e), + E::WitnessUtxo(ref e) => Some(e), + E::PartialSignatures(ref e) => Some(e), + E::Sighash(ref e) => Some(e), + E::RedeemScript(ref e) => Some(e), + E::WitnessScript(ref e) => Some(e), + E::Bip32Derivs(ref e) => Some(e), + E::FinalScriptSig(ref e) => Some(e), + E::FinalScriptWitness(ref e) => Some(e), + E::Unknown(ref e) => Some(e), + } + } +} + +/// Error when converting one of the `DecodePsbt` outputs failed. +#[derive(Debug)] +pub enum PsbtOutputError { + /// Conversion of the `redeem_script` field failed. + RedeemScript(hex::HexToBytesError), + /// Conversion of the `witness_script` field failed. + WitnessScript(hex::HexToBytesError), + /// Conversion of the `bip32_derivs` field failed. + Bip32Derivs(Bip32DerivError), + /// Conversion of the `unknown` field failed. + Unknown(hex::HexToBytesError), +} + +impl fmt::Display for PsbtOutputError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use PsbtOutputError as E; + + match *self { + E::RedeemScript(ref e) => + write_err!(f, "conversion of the `redeem_script` field failed"; e), + E::WitnessScript(ref e) => + write_err!(f, "conversion of the `witness_script` field failed"; e), + E::Bip32Derivs(ref e) => + write_err!(f, "conversion of the `bip32_derivs` field failed"; e), + E::Unknown(ref e) => write_err!(f, "conversion of the `unknown` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for PsbtOutputError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use PsbtOutputError as E; + + match *self { + E::RedeemScript(ref e) => Some(e), + E::WitnessScript(ref e) => Some(e), + E::Bip32Derivs(ref e) => Some(e), + E::Unknown(ref e) => Some(e), + } + } +} + +/// Error when converting a `DecodeScript` type into the model type. +#[derive(Debug)] +pub enum DecodeScriptError { + /// Conversion of the transaction `hex` field failed. + Hex(hex::HexToBytesError), + /// Conversion of the transaction `addresses` field failed. + Addresses(address::ParseError), + /// Conversion of the transaction `p2sh` field failed. + P2sh(address::ParseError), +} + +impl fmt::Display for DecodeScriptError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use DecodeScriptError as E; + + match *self { + E::Hex(ref e) => write_err!(f, "conversion of the `hex` field failed"; e), + E::Addresses(ref e) => write_err!(f, "conversion of the `addresses` field failed"; e), + E::P2sh(ref e) => write_err!(f, "conversion of the `p2sh` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DecodeScriptError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use DecodeScriptError as E; + + match *self { + E::Hex(ref e) => Some(e), + E::Addresses(ref e) => Some(e), + E::P2sh(ref e) => Some(e), + } + } +} + +/// Error when converting a `FinalizePsbt` type into the model type. +#[derive(Debug)] +pub enum FinalizePsbtError { + /// Conversion of the transaction `psbt` field failed. + Psbt(PsbtParseError), + /// Conversion of the transaction `hex` field failed. + Hex(encode::FromHexError), +} + +impl fmt::Display for FinalizePsbtError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use FinalizePsbtError as E; + + match *self { + E::Psbt(ref e) => write_err!(f, "conversion of the `psbt` field failed"; e), + E::Hex(ref e) => write_err!(f, "conversion of the `hex` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for FinalizePsbtError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use FinalizePsbtError as E; + + match *self { + E::Psbt(ref e) => Some(e), + E::Hex(ref e) => Some(e), + } + } +} /// Error when converting a `FundRawTransaction` type into the model type. #[derive(Debug)] @@ -38,3 +268,105 @@ impl std::error::Error for FundRawTransactionError { } } } + +/// Error when converting a `GetRawTransactionVerbose` type into the model type. +#[derive(Debug)] +pub enum GetRawTransactionVerboseError { + /// Conversion of one of the transaction inputs failed. + Inputs(RawTransactionInputError), + /// Conversion of one of the transaction outputs failed. + Outputs(RawTransactionOutputError), + /// Conversion of the `block_hash` field failed. + BlockHash(hex::HexToArrayError), +} + +impl fmt::Display for GetRawTransactionVerboseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use GetRawTransactionVerboseError as E; + + match *self { + E::Inputs(ref e) => + write_err!(f, "conversion of one of the transaction inputs failed"; e), + E::Outputs(ref e) => + write_err!(f, "conversion of one of the transaction outputs failed"; e), + E::BlockHash(ref e) => write_err!(f, "conversion of the `block_hash` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for GetRawTransactionVerboseError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use GetRawTransactionVerboseError as E; + + match *self { + E::Inputs(ref e) => Some(e), + E::Outputs(ref e) => Some(e), + E::BlockHash(ref e) => Some(e), + } + } +} + +/// Error when converting a `SignRawTransaction` type into the model type. +#[derive(Debug)] +pub enum SignRawTransactionError { + /// Conversion of the transaction `hex` field failed. + Hex(encode::FromHexError), + /// Conversion of the transaction `errors` field failed. + Errors(SignFailError), +} + +impl fmt::Display for SignRawTransactionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use SignRawTransactionError as E; + + match *self { + E::Hex(ref e) => write_err!(f, "conversion of the `hex` field failed"; e), + E::Errors(ref e) => write_err!(f, "conversion of the `errors` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SignRawTransactionError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use SignRawTransactionError as E; + + match *self { + E::Hex(ref e) => Some(e), + E::Errors(ref e) => Some(e), + } + } +} + +/// Error when converting a `SignFailError` type into the model type. +#[derive(Debug)] +pub enum SignFailError { + /// Conversion of the transaction `txid` field failed. + Txid(hex::HexToArrayError), + /// Conversion of the transaction `script_sig` field failed. + ScriptSig(hex::HexToBytesError), +} + +impl fmt::Display for SignFailError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use SignFailError as E; + + match *self { + E::Txid(ref e) => write_err!(f, "conversion of the `txid` field failed"; e), + E::ScriptSig(ref e) => write_err!(f, "conversion of the `script_sig` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SignFailError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use SignFailError as E; + + match *self { + E::Txid(ref e) => Some(e), + E::ScriptSig(ref e) => Some(e), + } + } +} diff --git a/types/src/v17/raw_transactions/into.rs b/types/src/v17/raw_transactions/into.rs index cfefd8af..08a7c2e0 100644 --- a/types/src/v17/raw_transactions/into.rs +++ b/types/src/v17/raw_transactions/into.rs @@ -1,11 +1,81 @@ // SPDX-License-Identifier: CC0-1.0 -use bitcoin::{consensus, hex, Amount, Transaction, Txid}; +use std::collections::BTreeMap; + +use bitcoin::psbt::{self, Psbt, PsbtParseError, PsbtSighashType}; +use bitcoin::{ + absolute, consensus, hex, transaction, Address, Amount, BlockHash, ScriptBuf, Sequence, + Transaction, Txid, +}; use super::{ - CreateRawTransaction, FundRawTransaction, FundRawTransactionError, SendRawTransaction, + CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, CreateRawTransaction, + DecodePsbt, DecodePsbtError, DecodeRawTransaction, DecodeScript, DecodeScriptError, + FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError, + GetRawTransaction, GetRawTransactionVerbose, GetRawTransactionVerboseError, MempoolAcceptance, + PsbtInput, PsbtInputError, PsbtOutput, PsbtOutputError, SendRawTransaction, SignFail, + SignFailError, SignRawTransaction, SignRawTransactionError, TestMempoolAccept, }; use crate::model; +use crate::psbt::RawTransactionError; + +impl CombinePsbt { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + let psbt = self.0.parse::()?; + Ok(model::CombinePsbt(psbt)) + } + + /// Converts json straight to a `bitcoin::Psbt`. + pub fn psbt(self) -> Result { + let model = self.into_model()?; + Ok(model.0) + } +} + +impl CombineRawTransaction { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model( + self, + ) -> Result { + let tx: Transaction = consensus::encode::deserialize_hex(&self.0)?; + Ok(model::CombineRawTransaction(tx)) + } + + /// Converts json straight to a `bitcoin::Transaction`. + pub fn transaction(self) -> Result { + let model = self.into_model()?; + Ok(model.0) + } +} + +impl ConvertToPsbt { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + let psbt = self.0.parse::()?; + Ok(model::ConvertToPsbt(psbt)) + } + + /// Converts json straight to a `bitcoin::Psbt`. + pub fn psbt(self) -> Result { + let model = self.into_model()?; + Ok(model.0) + } +} + +impl CreatePsbt { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + let psbt = self.0.parse::()?; + Ok(model::CreatePsbt(psbt)) + } + + /// Converts json straight to a `bitcoin::Psbt`. + pub fn psbt(self) -> Result { + let model = self.into_model()?; + Ok(model.0) + } +} impl CreateRawTransaction { /// Converts version specific type to a version nonspecific, more strongly typed type. @@ -23,6 +93,239 @@ impl CreateRawTransaction { } } +impl DecodePsbt { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + use DecodePsbtError as E; + + let unsigned_tx = self.tx.to_transaction().map_err(E::Tx)?; + let unknown = match self.unknown { + Some(map) => crate::psbt::into_unknown(map).map_err(E::Unknown)?, + None => BTreeMap::default(), + }; + + let inputs = self + .inputs + .into_iter() + .map(|input| input.into_input()) + .collect::>() + .map_err(E::Inputs)?; + let outputs = self + .outputs + .into_iter() + .map(|output| output.into_output()) + .collect::>() + .map_err(E::Outputs)?; + + // These fields do not appear until Core v23. + let version = 0; + let xpub = BTreeMap::default(); + let proprietary = BTreeMap::default(); + + let psbt = + bitcoin::Psbt { unsigned_tx, version, xpub, proprietary, unknown, inputs, outputs }; + let fee = self.fee.map(Amount::from_sat); + + Ok(model::DecodePsbt { psbt, fee }) + } +} + +impl PsbtInput { + /// Converts this PSBT data into a PSBT input. + pub fn into_input(self) -> Result { + use PsbtInputError as E; + + let non_witness_utxo = self + .non_witness_utxo + .map(|raw| raw.to_transaction()) + .transpose() + .map_err(E::NonWitnessUtxo)?; + let witness_utxo = + self.witness_utxo.map(|utxo| utxo.to_tx_out()).transpose().map_err(E::WitnessUtxo)?; + let partial_sigs = match self.partial_signatures { + Some(map) => crate::psbt::into_partial_signatures(map).map_err(E::PartialSignatures)?, + None => BTreeMap::default(), + }; + let sighash_type = self + .sighash + .map(|partial| partial.parse::()) + .transpose() + .map_err(E::Sighash)?; + let redeem_script = self + .redeem_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::RedeemScript)?; + let witness_script = self + .witness_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::WitnessScript)?; + let bip32_derivation = match self.bip32_derivs { + Some(derivs) => + crate::psbt::map_into_bip32_derivation(derivs).map_err(E::Bip32Derivs)?, + None => BTreeMap::default(), + }; + let final_script_sig = self + .final_script_sig + .map(|script| script.script_buf()) + .transpose() + .map_err(E::FinalScriptSig)?; + let final_script_witness = self + .final_script_witness + .map(|v| crate::witness_from_hex_slice(&v)) + .transpose() + .map_err(E::FinalScriptWitness)?; + let unknown = match self.unknown { + Some(map) => crate::psbt::into_unknown(map).map_err(E::Unknown)?, + None => BTreeMap::default(), + }; + + // These fields do not appear until Core v23. + let ripemd160_preimages = BTreeMap::default(); + let sha256_preimages = BTreeMap::default(); + let hash160_preimages = BTreeMap::default(); + let hash256_preimages = BTreeMap::default(); + let proprietary = BTreeMap::default(); + + // These fields do not appear until Core v24. + let tap_key_sig = None; + let tap_script_sigs = BTreeMap::default(); + let tap_scripts = BTreeMap::default(); + let tap_key_origins = BTreeMap::default(); + let tap_internal_key = None; + let tap_merkle_root = None; + + Ok(psbt::Input { + non_witness_utxo, + witness_utxo, + partial_sigs, + sighash_type, + redeem_script, + witness_script, + bip32_derivation, + final_script_sig, + final_script_witness, + ripemd160_preimages, + sha256_preimages, + hash160_preimages, + hash256_preimages, + tap_key_sig, + tap_script_sigs, + tap_scripts, + tap_key_origins, + tap_internal_key, + tap_merkle_root, + proprietary, + unknown, + }) + } +} + +impl PsbtOutput { + /// Converts this PSBT data into a PSBT output. + pub fn into_output(self) -> Result { + use PsbtOutputError as E; + + let redeem_script = self + .redeem_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::RedeemScript)?; + let witness_script = self + .witness_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::WitnessScript)?; + let bip32_derivation = match self.bip32_derivs { + Some(derivs) => + crate::psbt::vec_into_bip32_derivation(derivs).map_err(E::Bip32Derivs)?, + None => BTreeMap::default(), + }; + let unknown = match self.unknown { + Some(map) => crate::psbt::into_unknown(map).map_err(E::Unknown)?, + None => BTreeMap::default(), + }; + + // This field does not appear until Core v23. + let proprietary = BTreeMap::default(); + + // These fields do not appear until Core v24. + let tap_internal_key = None; + let tap_tree = None; + let tap_key_origins = BTreeMap::default(); + + Ok(psbt::Output { + redeem_script, + witness_script, + bip32_derivation, + tap_internal_key, + tap_tree, + tap_key_origins, + proprietary, + unknown, + }) + } +} + +impl DecodeRawTransaction { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + let raw_tx = self.0.to_transaction()?; + Ok(model::DecodeRawTransaction(raw_tx)) + } + + /// Converts json straight to a `bitcoin::Transaction`. + pub fn transaction(self) -> Result { + let model = self.into_model()?; + Ok(model.0) + } +} + +impl DecodeScript { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + use DecodeScriptError as E; + + let script_pubkey = match self.hex { + Some(hex) => Some(ScriptBuf::from_hex(&hex).map_err(E::Hex)?), + None => None, + }; + let addresses = match self.addresses { + Some(addresses) => addresses + .iter() + .map(|s| s.parse::>()) + .collect::>() + .map_err(E::Addresses)?, + None => vec![], + }; + let p2sh = self.p2sh.map(|s| s.parse::>()).transpose().map_err(E::P2sh)?; + + Ok(model::DecodeScript { + script_pubkey, + type_: self.type_, + required_signatures: self.required_signatures, + addresses, + p2sh, + }) + } +} + +impl FinalizePsbt { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + use FinalizePsbtError as E; + + let psbt = self.psbt.parse::().map_err(E::Psbt)?; + let tx = match self.hex { + Some(hex) => Some(consensus::encode::deserialize_hex(&hex).map_err(E::Hex)?), + None => None, + }; + + Ok(model::FinalizePsbt { psbt, complete: self.complete, tx }) + } +} + impl FundRawTransaction { /// Converts version specific type to a version nonspecific, more strongly typed type. pub fn into_model(self) -> Result { @@ -34,13 +337,65 @@ impl FundRawTransaction { Ok(model::FundRawTransaction { tx, fee, change_position: self.change_position }) } - /// Converts json straight to a `bitcoin::Txid`. - pub fn tx(self) -> Result { + /// Converts json straight to a `bitcoin::Transaction`. + pub fn transaction(self) -> Result { let model = self.into_model()?; Ok(model.tx) } } +impl GetRawTransaction { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + let tx: Transaction = consensus::encode::deserialize_hex(&self.0)?; + Ok(model::GetRawTransaction(tx)) + } + + /// Converts json straight to a `bitcoin::Transaction`. + pub fn transaction(self) -> Result { + let model = self.into_model()?; + Ok(model.0) + } +} + +impl GetRawTransactionVerbose { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model( + self, + ) -> Result { + use GetRawTransactionVerboseError as E; + + let version = transaction::Version::non_standard(self.version); + let lock_time = absolute::LockTime::from_consensus(self.lock_time); + + let input = self + .inputs + .into_iter() + .map(|input| input.to_input()) + .collect::>() + .map_err(E::Inputs)?; + let output = self + .outputs + .into_iter() + .map(|output| output.to_output()) + .collect::>() + .map_err(E::Outputs)?; + + let transaction = Transaction { version, lock_time, input, output }; + let block_hash = + self.block_hash.map(|s| s.parse::()).transpose().map_err(E::BlockHash)?; + + Ok(model::GetRawTransactionVerbose { + in_active_chain: self.in_active_chain, + transaction, + block_hash, + confirmations: self.confirmations, + transaction_time: self.transaction_time, + block_time: self.block_time, + }) + } +} + impl SendRawTransaction { /// Converts version specific type to a version nonspecific, more strongly typed type. pub fn into_model(self) -> Result { @@ -54,3 +409,58 @@ impl SendRawTransaction { Ok(model.0) } } + +impl SignRawTransaction { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + use SignRawTransactionError as E; + + let tx: Transaction = consensus::encode::deserialize_hex(&self.hex).map_err(E::Hex)?; + + let errors = match self.errors { + Some(v) => v + .into_iter() + .map(|f| f.into_model()) + .collect::>() + .map_err(E::Errors)?, + None => vec![], + }; + + Ok(model::SignRawTransaction { tx, complete: self.complete, errors }) + } +} + +impl SignFail { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + use SignFailError as E; + + let txid = self.txid.parse::().map_err(E::Txid)?; + let script_sig = ScriptBuf::from_hex(&self.script_sig).map_err(E::ScriptSig)?; + let sequence = Sequence::from_consensus(self.sequence); + + Ok(model::SignFail { txid, vout: self.vout, script_sig, sequence, error: self.error }) + } +} + +impl TestMempoolAccept { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + let results = self.results.into_iter().map(|r| r.into_model()).collect::>()?; + + Ok(model::TestMempoolAccept { results }) + } +} + +impl MempoolAcceptance { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + let txid = self.txid.parse::()?; + + Ok(model::MempoolAcceptance { + txid, + allowed: self.allowed, + reject_reason: self.reject_reason, + }) + } +} diff --git a/types/src/v17/raw_transactions/mod.rs b/types/src/v17/raw_transactions/mod.rs index e242e4fc..de4cdfc2 100644 --- a/types/src/v17/raw_transactions/mod.rs +++ b/types/src/v17/raw_transactions/mod.rs @@ -7,10 +7,94 @@ mod error; mod into; +use std::collections::HashMap; + use serde::{Deserialize, Serialize}; -// TODO: Remove wildcard, use explicit types. -pub use self::error::*; +use crate::ScriptSig; + +#[rustfmt::skip] // Keep public re-exports separate. +pub use self::error::{ + DecodePsbtError, DecodeScriptError, FundRawTransactionError, GetRawTransactionVerboseError, + PsbtInputError, PsbtOutputError, SignFailError, SignRawTransactionError, FinalizePsbtError, +}; +// Re-export types that appear in the public API of this module. +pub use crate::psbt::{ + Bip32Deriv, InputKeySource, PsbtScript, RawTransaction, RawTransactionInput, + RawTransactionOutput, WitnessUtxo, +}; + +/// Result of JSON-RPC method `combinepsbt`. +/// +/// > combinepsbt ["psbt",...] +/// > +/// > Combine multiple partially signed Bitcoin transactions into one transaction. +/// > Implements the Combiner role. +/// > +/// > Arguments: +/// > 1. "txs" (string) A json array of base64 strings of partially signed transactions +/// > [ +/// > "psbt" (string) A base64 string of a PSBT +/// > ,... +/// > ] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct CombinePsbt( + /// The base64-encoded partially signed transaction. + pub String, +); + +/// Result of JSON-RPC method `combinerawtransaction`. +/// +/// > combinerawtransaction ["hexstring",...] +/// > +/// > Combine multiple partially signed transactions into one transaction. +/// > The combined transaction may be another partially signed transaction or a +/// > fully signed transaction. +/// > Arguments: +/// > 1. "txs" (string) A json array of hex strings of partially signed transactions +/// > [ +/// > "hexstring" (string) A transaction hash +/// > ,... +/// > ] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct CombineRawTransaction( + /// The hex-encoded raw transaction with signature(s). + pub String, +); + +/// Result of JSON-RPC method `converttopsbt`. +/// +/// > converttopsbt "hexstring" ( permitsigdata iswitness ) +/// > +/// > Converts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction +/// > createpsbt and walletcreatefundedpsbt should be used for new applications. +/// > +/// > Arguments: +/// > 1. "hexstring" (string, required) The hex string of a raw transaction +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct ConvertToPsbt( + /// The resulting raw transaction (base64-encoded string). + pub String, +); + +/// Result of JSON-RPC method `createpsbt`. +/// +/// > createpsbt [{"txid":"id","vout":n},...] [{"address":amount},{"data":"hex"},...] ( locktime ) ( replaceable ) +/// > +/// > Creates a transaction in the Partially Signed Transaction format. +/// > Implements the Creator role. +/// > +/// > Arguments: +/// > 1. "inputs" (array, required) A json array of json objects +/// > [ +/// > { +/// > "txid":"id", (string, required) The transaction id +/// > "vout":n, (numeric, required) The output number +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct CreatePsbt( + /// The resulting raw transaction (base64-encoded string). + pub String, +); /// Result of JSON-RPC method `createrawtransaction`. /// @@ -28,30 +112,155 @@ pub use self::error::*; /// > { /// > "txid":"id", (string, required) The transaction id /// > "vout":n, (numeric, required) The output number -/// > "sequence":n (numeric, optional) The sequence number -/// > } -/// > ,... -/// > ] -/// > 2. "outputs" (array, required) a json array with outputs (key-value pairs) -/// > [ -/// > { -/// > "address": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in BTC -/// > }, -/// > { -/// > "data": "hex" (obj, optional) A key-value pair. The key must be "data", the value is hex encoded data -/// > } -/// > ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also -/// > accepted as second parameter. -/// > ] -/// > 3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs -/// > 4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable. -/// > Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct CreateRawTransaction( - /// The hex encoded transaction. + /// hex string of the transaction. pub String, ); +/// Result of JSON-RPC method `decodepsbt`. +/// +/// > decodepsbt "psbt" +/// > +/// > Return a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction. +/// > +/// > Arguments: +/// > 1. "psbt" (string, required) The PSBT base64 string +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct DecodePsbt { + /// The decoded network-serialized unsigned transaction. + pub tx: RawTransaction, + /// The unknown global fields. + pub unknown: Option>, + /// Array of transaction inputs. + pub inputs: Vec, + /// Array of transaction outputs. + pub outputs: Vec, + /// The transaction fee paid if all UTXOs slots in the PSBT have been filled. + pub fee: Option, +} + +/// An input in a partially signed Bitcoin transaction. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct PsbtInput { + /// Decoded network transaction for non-witness UTXOs. + pub non_witness_utxo: Option, + /// Transaction output for witness UTXOs. + pub witness_utxo: Option, + /// The public key and signature that corresponds to it. + pub partial_signatures: Option>, + /// The sighash type to be used. + pub sighash: Option, + /// The redeem script. + pub redeem_script: Option, + /// The witness script. + pub witness_script: Option, + /// The public key with the derivation path as the value. + pub bip32_derivs: Option>, + /// The final scriptsig. + #[serde(rename = "final_scriptsig")] + pub final_script_sig: Option, + /// Hex-encoded witness data (if any). + #[serde(rename = "final_scriptwitness")] + pub final_script_witness: Option>, + // `s/global/input`: this is a bug in the Core v17 docs. + /// The unknown global fields. + pub unknown: Option>, +} + +/// An output in a partially signed Bitcoin transaction. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct PsbtOutput { + /// The redeem script. + pub redeem_script: Option, + /// The witness script. + pub witness_script: Option, + /// The public key with the derivation path as the value. + pub bip32_derivs: Option>, + /// The unknown global fields. + pub unknown: Option>, +} + +/// Result of JSON-RPC method `decoderawtransaction`. +/// +/// > decoderawtransaction "hexstring" ( iswitness ) +/// > +/// > Return a JSON object representing the serialized, hex-encoded transaction. +/// > +/// > Arguments: +/// > 1. "hexstring" (string, required) The transaction hex string +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct DecodeRawTransaction(pub RawTransaction); + +/// Result of JSON-RPC method `decodescript`. +/// +/// > decodescript "hexstring" +/// > +/// > Decode a hex-encoded script. +/// > +/// > Arguments: +/// > 1. "hexstring" (string) the hex encoded script +// The docs on Core v17 appear to be way off what is actually returned. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct DecodeScript { + /// Script public key. + pub asm: String, + /// Hex encoded public key. + pub hex: Option, + /// The output type. + #[serde(rename = "type")] + pub type_: Option, + /// The required signatures. + #[serde(rename = "reqSigs")] + pub required_signatures: Option, + /// List of bitcoin addresses. + pub addresses: Option>, + /// Address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH). + pub p2sh: Option, + /// Segwit data (see `DecodeScriptSegwit` for explanation). + pub segwit: Option, +} + +/// Seemingly undocumented data returned in the `segwit` field of `DecodeScript`. +// This seems to be the same as `DecodeScript` except the `p2sh` field is caled `p2sh-segwit`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct DecodeScriptSegwit { + /// Script public key. + pub asm: String, + /// Hex encoded public key. + pub hex: String, + /// The output type. + #[serde(rename = "type")] + pub type_: String, + /// The required signatures. + #[serde(rename = "reqSigs")] + pub required_signatures: Option, + /// List of bitcoin addresses. + pub addresses: Option>, + /// Address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH). + pub p2sh_segtwit: Option, +} + +/// Result of JSON-RPC method `finalizepsbt`. +/// +/// > finalizepsbt "psbt" ( extract ) +/// > Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a +/// > network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be +/// > created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete. +/// > Implements the Finalizer and Extractor roles. +/// > +/// > Arguments: +/// > 1. "psbt" (string) A base64 string of a PSBT +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct FinalizePsbt { + /// The base64-encoded partially signed transaction if not extracted. + pub psbt: String, + /// The hex-encoded network transaction if extracted. + pub hex: Option, + /// If the transaction has a complete set of signatures. + pub complete: bool, +} + /// Result of JSON-RPC method `fundrawtransaction`. /// /// > fundrawtransaction "hexstring" ( options iswitness ) @@ -69,31 +278,6 @@ pub struct CreateRawTransaction( /// > /// > Arguments: /// > 1. "hexstring" (string, required) The hex string of the raw transaction -/// > 2. options (object, optional) -/// > { -/// > "changeAddress" (string, optional, default pool address) The bitcoin address to receive the change -/// > "changePosition" (numeric, optional, default random) The index of the change output -/// > "change_type" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are "legacy", "p2sh-segwit", and "bech32". Default is set by -changetype. -/// > "includeWatching" (boolean, optional, default false) Also select inputs which are watch only -/// > "lockUnspents" (boolean, optional, default false) Lock selected unspent outputs -/// > "feeRate" (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in BTC/kB -/// > "subtractFeeFromOutputs" (array, optional) A json array of integers. -/// > The fee will be equally deducted from the amount of each specified output. -/// > The outputs are specified by their zero-based index, before any change output is added. -/// > Those recipients will receive less bitcoins than you enter in their corresponding amount field. -/// > If no outputs are specified here, the sender pays the fee. -/// > [vout_index,...] -/// > "replaceable" (boolean, optional) Marks this transaction as BIP125 replaceable. -/// > Allows this transaction to be replaced by a transaction with higher fees -/// > "conf_target" (numeric, optional) Confirmation target (in blocks) -/// > "estimate_mode" (string, optional, default=UNSET) The fee estimate mode, must be one of: -/// > "UNSET" -/// > "ECONOMICAL" -/// > "CONSERVATIVE" -/// > } -/// > for backward compatibility: passing in a true instead of an object will result in {"includeWatching":true} -/// > 3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction -/// > If iswitness is not present, heuristic tests will be used in decoding #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct FundRawTransaction { /// The resulting raw transaction (hex-encoded string). @@ -105,6 +289,74 @@ pub struct FundRawTransaction { pub change_position: i64, } +/// Result of JSON-RPC method `getrawtransaction` with verbose set to `false`. +/// +/// > getrawtransaction "txid" ( verbose "blockhash" ) +/// > +/// > NOTE: By default this function only works for mempool transactions. If the -txindex option is +/// > enabled, it also works for blockchain transactions. If the block which contains the transaction +/// > is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is +/// > provided, only that block will be searched and if the transaction is in the mempool or other +/// > blocks, or if this node does not have the given block available, the transaction will not be found. +/// > DEPRECATED: for now, it also works for transactions with unspent outputs. +/// > +/// > Return the raw transaction data. +/// > +/// > If verbose is 'true', returns an Object with information about 'txid'. +/// > If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'. +/// > +/// > Arguments: +/// > 1. "txid" (string, required) The transaction id +/// > 2. verbose (bool, optional, default=false) If false, return a string, otherwise return a json object +/// > 3. "blockhash" (string, optional) The block in which to look for the transaction +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct GetRawTransaction( + /// The serialized, hex-encoded data for 'txid'. + pub String, +); + +/// Result of JSON-RPC method `getrawtransaction` with verbose set to `true`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct GetRawTransactionVerbose { + /// Whether specified block is in the active chain or not (only present with explicit "blockhash" argument). + pub in_active_chain: Option, + /// The serialized, hex-encoded data for 'txid'. + pub hex: String, + /// The transaction id (same as provided). + pub txid: String, + /// The transaction hash (differs from txid for witness transactions). + pub hash: String, + /// The serialized transaction size. + pub size: u64, + /// The virtual transaction size (differs from size for witness transactions). + pub vsize: u64, + /// The transaction's weight (between vsize*4-3 and vsize*4). + pub weight: u64, + /// The version. + pub version: i32, + /// The lock time. + #[serde(rename = "locktime")] + pub lock_time: u32, + /// Array of transaction inputs. + #[serde(rename = "vin")] + pub inputs: Vec, + /// Array of transaction outputs. + #[serde(rename = "vout")] + pub outputs: Vec, + // The following fields are all `None` if the transaction is in the mempool. + /// The block hash. + #[serde(rename = "blockhash")] + pub block_hash: Option, + /// The confirmations. + pub confirmations: Option, + /// The transaction time in seconds since epoch (Jan 1 1970 GMT). + #[serde(rename = "time")] + pub transaction_time: Option, + /// The block time in seconds since epoch (Jan 1 1970 GMT). + #[serde(rename = "blocktime")] + pub block_time: Option, +} + /// Result of JSON-RPC method `sendrawtransaction`. /// /// > sendrawtransaction "hexstring" ( allowhighfees ) @@ -115,8 +367,82 @@ pub struct FundRawTransaction { /// > /// > Arguments: /// > 1. hexstring (string, required) The hex string of the raw transaction +/// > 2. allowhighfees (boolean, optional, default=false) Allow high fees #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct SendRawTransaction( - /// The hex encoded txid. + /// The transaction hash in hex. pub String, ); + +/// Result of JSON-RPC method `signrawtransactionwithkey` (and deprecated `signrawtransaction`). +/// +/// > signrawtransaction "hexstring" ( [{"txid":"id","vout":n,"scriptPubKey":"hex","redeemScript":"hex"},...] ["privatekey1",...] sighashtype ) +/// > +/// > DEPRECATED. Sign inputs for raw transaction (serialized, hex-encoded). +/// > The second optional argument (may be null) is an array of previous transaction outputs that +/// > this transaction depends on but may not yet be in the block chain. +/// > The third optional argument (may be null) is an array of base58-encoded private +/// > keys that, if given, will be the only keys used to sign the transaction. +/// > +/// > +/// > Arguments: +/// > 1. "hexstring" (string, required) The transaction hex string +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct SignRawTransaction { + /// The hex-encoded raw transaction with signature(s). + pub hex: String, + /// If the transaction has a complete set of signatures. + pub complete: bool, + /// Script verification errors (if there are any). + pub errors: Option>, +} + +/// Represents a script verification error. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct SignFail { + /// The hash of the referenced, previous transaction. + pub txid: String, + /// The index of the output to spent and used as input. + pub vout: u64, + /// The hex-encoded signature script. + #[serde(rename = "scriptSig")] + pub script_sig: String, + /// Script sequence number. + pub sequence: u32, + /// Verification or signing error related to the input. + pub error: String, +} + +/// Result of JSON-RPC method `testmempoolaccept`. +/// +/// > testmempoolaccept ["rawtxs"] ( allowhighfees ) +/// > +/// > Returns if raw transaction (serialized, hex-encoded) would be accepted by mempool. +/// > +/// > This checks if the transaction violates the consensus or policy rules. +/// > +/// > See sendrawtransaction call. +/// > +/// > Arguments: +/// > 1. ["rawtxs"] (array, required) An array of hex strings of raw transactions. +/// > Length must be one for now. +/// > 2. allowhighfees (boolean, optional, default=false) Allow high fees +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct TestMempoolAccept { + /// Array of test results for each raw transaction in the input array. + /// + /// Length is exactly one for now. + pub results: Vec, +} + +/// Represents a single mempool acceptance test result. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct MempoolAcceptance { + /// The transaction hash in hex. + pub txid: String, + /// If the mempool allows this tx to be inserted. + pub allowed: bool, + /// Rejection string (only present when 'allowed' is false). + #[serde(rename = "reject-reason")] + pub reject_reason: Option, +} diff --git a/types/src/v17/wallet/error.rs b/types/src/v17/wallet/error.rs index d6d55f0d..77b15e2a 100644 --- a/types/src/v17/wallet/error.rs +++ b/types/src/v17/wallet/error.rs @@ -686,79 +686,6 @@ impl From for ListUnspentItemError { fn from(e: NumericError) -> Self { Self::Numeric(e) } } -/// Error when converting a `SignRawTransactionWithWalletError` type into the model type. -#[derive(Debug)] -pub enum SignRawTransactionWithWalletError { - /// Conversion of the transaction `hex` field failed. - Tx(encode::FromHexError), - /// Conversion of one of the items in the `errors` field failed. - Errors(SignErrorDataError), -} - -impl fmt::Display for SignRawTransactionWithWalletError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use SignRawTransactionWithWalletError as E; - - match *self { - E::Tx(ref e) => write_err!(f, "conversion of the `hex` field failed"; e), - E::Errors(ref e) => - write_err!(f, "conversion of one of the items in the `errors` field failed"; e), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for SignRawTransactionWithWalletError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use SignRawTransactionWithWalletError as E; - - match *self { - E::Tx(ref e) => Some(e), - E::Errors(ref e) => Some(e), - } - } -} - -/// Error when converting a `SignErrorData` type into the model type. -#[derive(Debug)] -pub enum SignErrorDataError { - /// Conversion of numeric type to expected type failed. - Numeric(NumericError), - /// Conversion of the `txid` field failed. - Txid(hex::HexToArrayError), - /// Conversion of the `script_sig` field failed. - ScriptSig(hex::HexToBytesError), -} - -impl fmt::Display for SignErrorDataError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use SignErrorDataError as E; - - match *self { - E::Numeric(ref e) => write_err!(f, "numeric"; e), - E::Txid(ref e) => write_err!(f, "conversion of the `txid` field failed"; e), - E::ScriptSig(ref e) => write_err!(f, "conversion of the `script_sig` field failed"; e), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for SignErrorDataError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use SignErrorDataError as E; - - match *self { - E::Numeric(ref e) => Some(e), - E::Txid(ref e) => Some(e), - E::ScriptSig(ref e) => Some(e), - } - } -} - -impl From for SignErrorDataError { - fn from(e: NumericError) -> Self { Self::Numeric(e) } -} - /// Error when converting a `WalletCreateFundedPsbt` type into the model type. #[derive(Debug)] pub enum WalletCreateFundedPsbtError { diff --git a/types/src/v17/wallet/into.rs b/types/src/v17/wallet/into.rs index fc67f1a2..a7f9d221 100644 --- a/types/src/v17/wallet/into.rs +++ b/types/src/v17/wallet/into.rs @@ -8,8 +8,8 @@ use bitcoin::hex::FromHex; use bitcoin::key::{self, PrivateKey, PublicKey}; use bitcoin::psbt::PsbtParseError; use bitcoin::{ - address, bip32, ecdsa, Address, Amount, BlockHash, Psbt, ScriptBuf, Sequence, SignedAmount, - Transaction, Txid, WitnessProgram, WitnessVersion, + address, bip32, ecdsa, Address, Amount, BlockHash, Psbt, ScriptBuf, SignedAmount, Transaction, + Txid, WitnessProgram, WitnessVersion, }; // TODO: Use explicit imports? @@ -706,45 +706,6 @@ impl SignMessage { } } -impl SignRawTransactionWithWallet { - /// Converts version specific type to a version nonspecific, more strongly typed type. - pub fn into_model( - self, - ) -> Result { - use SignRawTransactionWithWalletError as E; - - let tx = encode::deserialize_hex::(&self.hex).map_err(E::Tx)?; - let errors = match self.errors { - None => Ok(vec![]), - Some(errors) => errors - .into_iter() - .map(|e| e.into_model()) - .collect::, _>>() - .map_err(E::Errors), - }; - - Ok(model::SignRawTransactionWithWallet { - raw_transaction: tx, - complete: self.complete, - errors: errors?, - }) - } -} - -impl SignErrorData { - /// Converts version specific type to a version nonspecific, more strongly typed type. - pub fn into_model(self) -> Result { - use SignErrorDataError as E; - - let txid = self.txid.parse::().map_err(E::Txid)?; - let vout = crate::to_u32(self.vout, "vout")?; - let script_sig = ScriptBuf::from_hex(&self.script_sig).map_err(E::ScriptSig)?; - let sequence = Sequence(self.sequence); - - Ok(model::SignErrorData { txid, vout, script_sig, sequence, error: self.error }) - } -} - impl WalletCreateFundedPsbt { /// Converts version specific type to a version nonspecific, more strongly typed type. pub fn into_model(self) -> Result { diff --git a/types/src/v17/wallet/mod.rs b/types/src/v17/wallet/mod.rs index e035b1b4..cd70fefd 100644 --- a/types/src/v17/wallet/mod.rs +++ b/types/src/v17/wallet/mod.rs @@ -897,43 +897,6 @@ pub struct SignMessage( pub String, ); -/// Result of the JSON-RPC method `signrawtransactionwithwallet`. -/// -/// > signrawtransactionwithwallet "hexstring" ( [{"txid":"id","vout":n,"scriptPubKey":"hex","redeemScript":"hex"},...] sighashtype ) -/// > -/// > Sign inputs for raw transaction (serialized, hex-encoded). -/// > The second optional argument (may be null) is an array of previous transaction outputs that -/// > this transaction depends on but may not yet be in the block chain. -/// > -/// > -/// > Arguments: -/// > 1. "hexstring" (string, required) The transaction hex string -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct SignRawTransactionWithWallet { - /// The hex-encoded raw transaction with signature(s). - pub hex: String, - /// If the transaction has a complete set of signatures. - pub complete: bool, - /// Script verification errors (if there are any). - pub errors: Option>, // 'Data' suffix to differentiate this from a normal error type. -} - -/// Returned as part of `signrawtransactionwithwallet`. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct SignErrorData { - /// The hash of the referenced, previous transaction. - pub txid: String, - /// The index of the output to spent and used as input. - pub vout: i64, - /// The hex-encoded signature script. - #[serde(rename = "ScriptSig")] - pub script_sig: String, - /// Script sequence number. - pub sequence: u32, - /// Verification or signing error related to the input. - pub error: String, -} - /// Result of the JSON-RPC method `walletcreatefundedpsbt`. /// /// > walletcreatefundedpsbt [{"txid":"id","vout":n},...] [{"address":amount},{"data":"hex"},...] ( locktime ) ( replaceable ) ( options bip32derivs ) diff --git a/types/src/v18/mod.rs b/types/src/v18/mod.rs index b3f2b31c..18b56b0a 100644 --- a/types/src/v18/mod.rs +++ b/types/src/v18/mod.rs @@ -113,23 +113,23 @@ //! //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| -//! | analyzepsbt | todo | -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | analyzepsbt | done | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | finalizepsbt | todo | -//! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | -//! | joinpsbts | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | finalizepsbt | done (untested) | +//! | fundrawtransaction | done | +//! | getrawtransaction | done | +//! | joinpsbts | done (untested) | //! | sendrawtransaction | done | -//! | signrawtransactionwithkey | todo | -//! | testmempoolaccept | todo | -//! | utxoupdatepsbt | todo | +//! | signrawtransactionwithkey | done | +//! | testmempoolaccept | done (untested) | +//! | utxoupdatepsbt | done (untested) | //! //! //! @@ -217,24 +217,26 @@ //! | getzmqnotifications | done (untested) | //! //! -//! -//! -//! **Items marked omitted were omitted because:** -//! -//! - Method does not return anything. -//! - Method returns a simple type (e.g. bool or integer). -//! - Method is deprecated. // JSON-RPC types by API section. mod control; +mod raw_transactions; #[doc(inline)] -pub use self::control::{ActiveCommand, GetRpcInfo}; +pub use self::{ + control::{ActiveCommand, GetRpcInfo}, + raw_transactions::{ + AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, JoinPsbts, UtxoUpdatePsbt, + }, +}; #[doc(inline)] pub use crate::v17::{ AddMultisigAddress, AddMultisigAddressError, AddedNode, AddedNodeAddress, AddressInformation, - Banned, Bip9Softfork, Bip9SoftforkStatus, BumpFee, BumpFeeError, ChainTips, ChainTipsError, - ChainTipsStatus, CreateRawTransaction, CreateWallet, DumpPrivKey, DumpWallet, + Banned, Bip32Deriv, Bip9Softfork, Bip9SoftforkStatus, BumpFee, BumpFeeError, ChainTips, + ChainTipsError, ChainTipsStatus, CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, + CreateRawTransaction, CreateWallet, DecodePsbt, DecodePsbtError, DecodeRawTransaction, + DecodeScript, DecodeScriptError, DumpPrivKey, DumpWallet, FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, GetAddressInfoEmbedded, GetAddressInfoEmbeddedError, GetAddressInfoError, GetAddressInfoLabel, GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, @@ -246,19 +248,21 @@ pub use crate::v17::{ GetMempoolDescendants, GetMempoolDescendantsVerbose, GetMempoolEntry, GetMempoolInfo, GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, GetNetworkInfoAddress, GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, GetPeerInfo, GetRawChangeAddress, - GetRawMempool, GetRawMempoolVerbose, GetReceivedByAddress, GetTransaction, - GetTransactionDetail, GetTransactionDetailError, GetTransactionError, GetTxOut, GetTxOutError, - GetTxOutSetInfo, GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, - GetWalletInfoError, GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsError, - ListAddressGroupingsItem, ListBanned, ListLabels, ListLockUnspent, ListLockUnspentItem, - ListLockUnspentItemError, ListReceivedByAddress, ListReceivedByAddressError, - ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockError, ListSinceBlockTransaction, - ListSinceBlockTransactionError, ListTransactions, ListTransactionsItem, - ListTransactionsItemError, ListUnspent, ListUnspentItem, ListUnspentItemError, ListWallets, - LoadWallet, Locked, Logging, MapMempoolEntryError, MempoolEntry, MempoolEntryError, - MempoolEntryFees, MempoolEntryFeesError, PeerInfo, RescanBlockchain, ScriptPubkey, SendMany, - SendRawTransaction, SendToAddress, SignErrorData, SignErrorDataError, SignMessage, - SignRawTransactionWithWallet, SignRawTransactionWithWalletError, Softfork, SoftforkReject, - TransactionCategory, UploadTarget, VerifyTxOutProof, WalletCreateFundedPsbt, - WalletCreateFundedPsbtError, WalletProcessPsbt, + GetRawMempool, GetRawMempoolVerbose, GetRawTransaction, GetRawTransactionVerbose, + GetRawTransactionVerboseError, GetReceivedByAddress, GetTransaction, GetTransactionDetail, + GetTransactionDetailError, GetTransactionError, GetTxOut, GetTxOutError, GetTxOutSetInfo, + GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, GetWalletInfoError, + GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsError, ListAddressGroupingsItem, + ListBanned, ListLabels, ListLockUnspent, ListLockUnspentItem, ListLockUnspentItemError, + ListReceivedByAddress, ListReceivedByAddressError, ListReceivedByAddressItem, ListSinceBlock, + ListSinceBlockError, ListSinceBlockTransaction, ListSinceBlockTransactionError, + ListTransactions, ListTransactionsItem, ListTransactionsItemError, ListUnspent, + ListUnspentItem, ListUnspentItemError, ListWallets, LoadWallet, Locked, Logging, + MapMempoolEntryError, MempoolAcceptance, MempoolEntry, MempoolEntryError, MempoolEntryFees, + MempoolEntryFeesError, PeerInfo, PsbtInput, PsbtOutput, PsbtScript, RawTransaction, + RawTransactionError, RawTransactionInput, RawTransactionOutput, RescanBlockchain, SendMany, + SendRawTransaction, SendToAddress, SignFail, SignFailError, SignMessage, SignRawTransaction, + SignRawTransactionError, Softfork, SoftforkReject, TestMempoolAccept, TransactionCategory, + UploadTarget, VerifyTxOutProof, WalletCreateFundedPsbt, WalletCreateFundedPsbtError, + WalletProcessPsbt, WitnessUtxo, }; diff --git a/types/src/v18/raw_transactions/error.rs b/types/src/v18/raw_transactions/error.rs new file mode 100644 index 00000000..740614a7 --- /dev/null +++ b/types/src/v18/raw_transactions/error.rs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: CC0-1.0 + +use core::fmt; + +use bitcoin::amount::ParseAmountError; +use bitcoin::hex; + +use crate::error::write_err; + +/// Error when converting a missing input during `analyzepsbt`. +#[derive(Debug)] +pub enum AnalyzePsbtError { + /// Conversion of the `inputs` field failed. + Inputs(AnalyzePsbtInputMissingError), + /// Conversion of the `estimated_fee_rate` field failed. + EstimatedFeeRate(ParseAmountError), + /// Conversion of the `fee` field failed. + Fee(ParseAmountError), +} + +impl fmt::Display for AnalyzePsbtError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use AnalyzePsbtError as E; + + match *self { + E::Inputs(ref e) => write_err!(f, "conversion of one of the `inputs` failed"; e), + E::EstimatedFeeRate(ref e) => + write_err!(f, "conversion of the `estimated_fee_rate` field failed"; e), + E::Fee(ref e) => write_err!(f, "conversion of the `fee` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for AnalyzePsbtError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use AnalyzePsbtError as E; + + match *self { + E::Inputs(ref e) => Some(e), + E::EstimatedFeeRate(ref e) => Some(e), + E::Fee(ref e) => Some(e), + } + } +} + +/// Error when converting a missing input during `analyzepsbt`. +#[derive(Debug)] +pub enum AnalyzePsbtInputMissingError { + /// Conversion of the `pubkeys` field failed. + Pubkeys(hex::HexToArrayError), + /// Conversion of the `signatures` field failed. + Signatures(hex::HexToArrayError), + /// Conversion of the `RedeemScript` field failed. + RedeemScript(hex::HexToArrayError), + /// Conversion of the `witness_script` field failed. + WitnessScript(hex::HexToArrayError), +} + +impl fmt::Display for AnalyzePsbtInputMissingError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use AnalyzePsbtInputMissingError as E; + + match *self { + E::Pubkeys(ref e) => write_err!(f, "conversion of the `pubkeys` field failed"; e), + E::Signatures(ref e) => write_err!(f, "conversion of the `signatures` field failed"; e), + E::RedeemScript(ref e) => + write_err!(f, "conversion of the `redeem_script` field failed"; e), + E::WitnessScript(ref e) => + write_err!(f, "conversion of the `witness_script` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for AnalyzePsbtInputMissingError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use AnalyzePsbtInputMissingError as E; + + match *self { + E::Pubkeys(ref e) => Some(e), + E::Signatures(ref e) => Some(e), + E::RedeemScript(ref e) => Some(e), + E::WitnessScript(ref e) => Some(e), + } + } +} diff --git a/types/src/v18/raw_transactions/into.rs b/types/src/v18/raw_transactions/into.rs new file mode 100644 index 00000000..8acd0c2d --- /dev/null +++ b/types/src/v18/raw_transactions/into.rs @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: CC0-1.0 + +use bitcoin::hashes::{hash160, sha256}; +use bitcoin::psbt::{Psbt, PsbtParseError}; +use bitcoin::Amount; + +use super::{ + AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, JoinPsbts, UtxoUpdatePsbt, +}; +use crate::model; + +impl AnalyzePsbt { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + use AnalyzePsbtError as E; + + let inputs = self + .inputs + .into_iter() + .map(|input| input.into_model()) + .collect::>() + .map_err(E::Inputs)?; + let estimated_fee_rate = self + .estimated_fee_rate + .map(crate::btc_per_kb) + .transpose() + .map_err(E::EstimatedFeeRate)? + .flatten(); + let fee = self.fee.map(Amount::from_btc).transpose().map_err(E::Fee)?; + + Ok(model::AnalyzePsbt { + inputs, + estimated_vsize: self.estimated_vsize, + estimated_fee_rate, + fee, + next: self.next, + }) + } +} + +impl AnalyzePsbtInput { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + let missing = self.missing.map(|m| m.into_model()).transpose()?; + + Ok(model::AnalyzePsbtInput { + has_utxo: self.has_utxo, + is_final: self.is_final, + missing, + next: self.next, + }) + } +} + +impl AnalyzePsbtInputMissing { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model( + self, + ) -> Result { + use AnalyzePsbtInputMissingError as E; + + let pubkeys = match self.pubkeys { + Some(v) => v + .iter() + .map(|s| s.parse::()) + .collect::>() + .map_err(E::Pubkeys)?, + None => vec![], + }; + let signatures = match self.signatures { + Some(v) => v + .iter() + .map(|s| s.parse::()) + .collect::>() + .map_err(E::Signatures)?, + None => vec![], + }; + let redeem_script = self + .redeem_script + .map(|s| s.parse::()) + .transpose() + .map_err(E::RedeemScript)?; + let witness_script = self + .witness_script + .map(|s| s.parse::()) + .transpose() + .map_err(E::WitnessScript)?; + + Ok(model::AnalyzePsbtInputMissing { pubkeys, signatures, redeem_script, witness_script }) + } +} + +impl JoinPsbts { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + let psbt = self.0.parse::()?; + Ok(model::JoinPsbts(psbt)) + } + + /// Converts json straight to a `bitcoin::Psbt`. + pub fn psbt(self) -> Result { + let model = self.into_model()?; + Ok(model.0) + } +} + +impl UtxoUpdatePsbt { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + let psbt = self.0.parse::()?; + Ok(model::UtxoUpdatePsbt(psbt)) + } + + /// Converts json straight to a `bitcoin::Psbt`. + pub fn psbt(self) -> Result { + let model = self.into_model()?; + Ok(model.0) + } +} diff --git a/types/src/v18/raw_transactions/mod.rs b/types/src/v18/raw_transactions/mod.rs new file mode 100644 index 00000000..07529716 --- /dev/null +++ b/types/src/v18/raw_transactions/mod.rs @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! The JSON-RPC API for Bitcoin Core `v0.18` - raw transactions. +//! +//! Types for methods found under the `== Rawtransactions ==` section of the API docs. + +mod error; +mod into; + +use serde::{Deserialize, Serialize}; + +pub use self::error::{AnalyzePsbtError, AnalyzePsbtInputMissingError}; + +/// Result of JSON-RPC method `analyzepsbt`. +/// +/// analyzepsbt "psbt" +/// +/// Analyzes and provides information about the current status of a PSBT and its inputs +/// +/// Arguments: +/// 1. psbt (string, required) A base64 string of a PSBT +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct AnalyzePsbt { + /// Array of input objects. + pub inputs: Vec, + /// Estimated vsize of the final signed transaction. + pub estimated_vsize: Option, + /// Estimated feerate of the final signed transaction in BTC/kB. + /// + /// Shown only if all UTXO slots in the PSBT have been filled. + #[serde(rename = "estimated_feerate")] + pub estimated_fee_rate: Option, + /// The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled. + pub fee: Option, + /// Role of the next person that this psbt needs to go to. + pub next: String, +} + +/// Represents an input in a PSBT operation. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct AnalyzePsbtInput { + /// Whether a UTXO is provided. + pub has_utxo: bool, + /// Whether the input is finalized. + pub is_final: bool, + /// Things that are missing that are required to complete this input. + pub missing: Option, + /// Role of the next person that this input needs to go to. + pub next: Option, +} + +/// Represents missing elements required to complete an input. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct AnalyzePsbtInputMissing { + /// Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing. + pub pubkeys: Option>, + /// Public key ID, hash160 of the public key, of a public key whose signature is missing. + pub signatures: Option>, + /// Hash160 of the redeemScript that is missing. + #[serde(rename = "redeemscript")] + pub redeem_script: Option, + /// SHA256 of the witnessScript that is missing. + #[serde(rename = "witnessscript")] + pub witness_script: Option, +} + +/// Result of JSON-RPC method `joinpsbts`. +/// +/// > joinpsbts ["psbt",...] +/// > +/// > Joins multiple distinct PSBTs with different inputs and outputs into one PSBT with inputs and outputs from all of the PSBTs +/// > No input in any of the PSBTs can be in more than one of the PSBTs. +/// > +/// > Arguments: +/// > 1. txs (json array, required) A json array of base64 strings of partially signed transactions +/// > [ +/// > "psbt", (string, required) A base64 string of a PSBT +/// > ... +/// > ] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct JoinPsbts( + /// The base64-encoded partially signed transaction. + pub String, +); + +/// Result of JSON-RPC method `utxoupdatepsbt`. +/// +/// > utxoupdatepsbt "psbt" +/// > +/// > Updates a PSBT with witness UTXOs retrieved from the UTXO set or the mempool. +/// > +/// > Arguments: +/// > 1. psbt (string, required) A base64 string of a PSBT +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct UtxoUpdatePsbt( + /// The base64-encoded partially signed transaction with inputs updated. + pub String, +); diff --git a/types/src/v19/mod.rs b/types/src/v19/mod.rs index f462a836..3a4ffc29 100644 --- a/types/src/v19/mod.rs +++ b/types/src/v19/mod.rs @@ -108,23 +108,23 @@ //! //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| -//! | analyzepsbt | todo | -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | analyzepsbt | done | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | finalizepsbt | todo | -//! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | -//! | joinpsbts | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | finalizepsbt | done (untested) | +//! | fundrawtransaction | done | +//! | getrawtransaction | done | +//! | joinpsbts | done (untested) | //! | sendrawtransaction | done | -//! | signrawtransactionwithkey | todo | -//! | testmempoolaccept | todo | -//! | utxoupdatepsbt | todo | +//! | signrawtransactionwithkey | done | +//! | testmempoolaccept | done (untested) | +//! | utxoupdatepsbt | done (untested) | //! //! //! @@ -214,13 +214,6 @@ //! | getzmqnotifications | done (untested) | //! //! -//! -//! -//! **Items marked omitted were omitted because:** -//! -//! - Method does not return anything. -//! - Method returns a simple type (e.g. bool or integer). -//! - Method is deprecated. // JSON-RPC types by API section. mod blockchain; @@ -240,8 +233,10 @@ pub use self::{ #[doc(inline)] pub use crate::v17::{ AddMultisigAddress, AddMultisigAddressError, AddedNode, AddedNodeAddress, AddressInformation, - Banned, BumpFee, BumpFeeError, ChainTips, ChainTipsError, ChainTipsStatus, - CreateRawTransaction, CreateWallet, DumpPrivKey, DumpWallet, FundRawTransaction, + Banned, BumpFee, BumpFeeError, ChainTips, ChainTipsError, ChainTipsStatus, CombinePsbt, + CombineRawTransaction, ConvertToPsbt, CreatePsbt, CreateRawTransaction, CreateWallet, + DecodePsbt, DecodePsbtError, DecodeRawTransaction, DecodeScript, DecodeScriptError, + DumpPrivKey, DumpWallet, FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, GetAddressInfoEmbedded, GetAddressInfoEmbeddedError, GetAddressInfoError, GetAddressInfoLabel, GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, GetBlockHash, GetBlockHeader, @@ -251,7 +246,8 @@ pub use crate::v17::{ GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, GetMempoolInfo, GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, GetNetworkInfoAddress, GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, GetPeerInfo, GetRawChangeAddress, GetRawMempool, - GetRawMempoolVerbose, GetReceivedByAddress, GetTransaction, GetTransactionDetail, + GetRawMempoolVerbose, GetRawTransaction, GetRawTransactionVerbose, + GetRawTransactionVerboseError, GetReceivedByAddress, GetTransaction, GetTransactionDetail, GetTransactionDetailError, GetTransactionError, GetTxOut, GetTxOutError, GetTxOutSetInfo, GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, GetWalletInfoError, GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsError, ListAddressGroupingsItem, @@ -260,10 +256,13 @@ pub use crate::v17::{ ListSinceBlockError, ListSinceBlockTransaction, ListSinceBlockTransactionError, ListTransactions, ListTransactionsItem, ListTransactionsItemError, ListUnspent, ListUnspentItem, ListUnspentItemError, ListWallets, LoadWallet, Locked, Logging, PeerInfo, - RescanBlockchain, ScriptPubkey, SendMany, SendRawTransaction, SendToAddress, SignErrorData, - SignErrorDataError, SignMessage, SignRawTransactionWithWallet, - SignRawTransactionWithWalletError, SoftforkReject, TransactionCategory, UploadTarget, - VerifyTxOutProof, WalletCreateFundedPsbt, WalletCreateFundedPsbtError, WalletProcessPsbt, + RawTransactionError, RawTransactionInput, RawTransactionOutput, RescanBlockchain, SendMany, + SendRawTransaction, SendToAddress, SignMessage, SignRawTransaction, SignRawTransactionError, + SoftforkReject, TestMempoolAccept, TransactionCategory, UploadTarget, VerifyTxOutProof, + WalletCreateFundedPsbt, WalletCreateFundedPsbtError, WalletProcessPsbt, WitnessUtxo, }; #[doc(inline)] -pub use crate::v18::{ActiveCommand, GetRpcInfo}; +pub use crate::v18::{ + ActiveCommand, AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, GetRpcInfo, JoinPsbts, UtxoUpdatePsbt, +}; diff --git a/types/src/v20/mod.rs b/types/src/v20/mod.rs index 293399b6..6c2fb577 100644 --- a/types/src/v20/mod.rs +++ b/types/src/v20/mod.rs @@ -109,23 +109,23 @@ //! //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| -//! | analyzepsbt | todo | -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | analyzepsbt | done | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | finalizepsbt | todo | -//! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | -//! | joinpsbts | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | finalizepsbt | done (untested) | +//! | fundrawtransaction | done | +//! | getrawtransaction | done | +//! | joinpsbts | done (untested) | //! | sendrawtransaction | done | -//! | signrawtransactionwithkey | todo | -//! | testmempoolaccept | todo | -//! | utxoupdatepsbt | todo | +//! | signrawtransactionwithkey | done | +//! | testmempoolaccept | done (untested) | +//! | utxoupdatepsbt | done (untested) | //! //! //! @@ -215,13 +215,6 @@ //! | getzmqnotifications | done (untested) | //! //! -//! -//! -//! **Items marked omitted were omitted because:** -//! -//! - Method does not return anything. -//! - Method returns a simple type (e.g. bool or integer). -//! - Method is deprecated. // JSON-RPC types by API section. mod control; @@ -233,29 +226,38 @@ pub use crate::{ v17::{ AddMultisigAddress, AddMultisigAddressError, AddedNode, AddedNodeAddress, AddressInformation, Banned, BumpFee, BumpFeeError, ChainTips, ChainTipsError, - ChainTipsStatus, CreateRawTransaction, CreateWallet, DumpPrivKey, DumpWallet, + ChainTipsStatus, CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, + CreateRawTransaction, CreateWallet, DecodePsbt, DecodePsbtError, DecodeRawTransaction, + DecodeScript, DecodeScriptError, DumpPrivKey, DumpWallet, FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, - GetAddressInfo, GetAddressInfoEmbedded, GetAddressInfoError, GetAddressInfoLabel, - GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, GetBlockHash, - GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, GetBlockHeaderVerboseError, - GetBlockStats, GetBlockStatsError, GetBlockTemplate, GetBlockTemplateError, - GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseZero, GetChainTips, - GetChainTxStats, GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, GetMempoolInfo, - GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, GetNetworkInfoAddress, - GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, GetPeerInfo, - GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetReceivedByAddress, - GetTransaction, GetTransactionDetail, GetTransactionError, GetTxOut, GetTxOutError, - GetTxOutSetInfo, GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, - GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsItem, ListBanned, - ListLabels, ListLockUnspent, ListLockUnspentItem, ListReceivedByAddress, - ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockTransaction, ListTransactions, - ListTransactionsItem, ListUnspent, ListUnspentItem, ListWallets, LoadWallet, Locked, - PeerInfo, RescanBlockchain, ScriptPubkey, SendMany, SendRawTransaction, SendToAddress, - SignErrorData, SignMessage, SignRawTransactionWithWallet, SoftforkReject, - TransactionCategory, UploadTarget, VerifyTxOutProof, WalletCreateFundedPsbt, - WalletProcessPsbt, + GetAddressInfo, GetAddressInfoEmbedded, GetAddressInfoEmbeddedError, GetAddressInfoError, + GetAddressInfoLabel, GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, + GetBlockHash, GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, + GetBlockHeaderVerboseError, GetBlockStats, GetBlockStatsError, GetBlockTemplate, + GetBlockTemplateError, GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseZero, + GetChainTips, GetChainTxStats, GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, + GetMempoolInfo, GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, + GetNetworkInfoAddress, GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, + GetPeerInfo, GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetRawTransaction, + GetRawTransactionVerbose, GetRawTransactionVerboseError, GetReceivedByAddress, + GetTransaction, GetTransactionDetail, GetTransactionDetailError, GetTransactionError, + GetTxOut, GetTxOutError, GetTxOutSetInfo, GetTxOutSetInfoError, GetUnconfirmedBalance, + GetWalletInfo, GetWalletInfoError, GetZmqNotifications, ListAddressGroupings, + ListAddressGroupingsError, ListAddressGroupingsItem, ListBanned, ListLabels, + ListLockUnspent, ListLockUnspentItem, ListLockUnspentItemError, ListReceivedByAddress, + ListReceivedByAddressError, ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockError, + ListSinceBlockTransaction, ListSinceBlockTransactionError, ListTransactions, + ListTransactionsItem, ListTransactionsItemError, ListUnspent, ListUnspentItem, + ListUnspentItemError, ListWallets, LoadWallet, Locked, PeerInfo, RawTransactionError, + RawTransactionInput, RawTransactionOutput, RescanBlockchain, SendMany, SendRawTransaction, + SendToAddress, SignMessage, SignRawTransaction, SignRawTransactionError, SoftforkReject, + TestMempoolAccept, TransactionCategory, UploadTarget, VerifyTxOutProof, + WalletCreateFundedPsbt, WalletCreateFundedPsbtError, WalletProcessPsbt, WitnessUtxo, + }, + v18::{ + ActiveCommand, AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, GetRpcInfo, JoinPsbts, UtxoUpdatePsbt, }, - v18::{ActiveCommand, GetRpcInfo}, v19::{ Bip9SoftforkInfo, Bip9SoftforkStatistics, Bip9SoftforkStatus, GetBalances, GetBalancesMine, GetBalancesWatchOnly, GetBlockFilter, GetBlockFilterError, GetBlockchainInfo, diff --git a/types/src/v21/mod.rs b/types/src/v21/mod.rs index 8f6adb01..c6a642b1 100644 --- a/types/src/v21/mod.rs +++ b/types/src/v21/mod.rs @@ -110,23 +110,23 @@ //! //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| -//! | analyzepsbt | todo | -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | analyzepsbt | done | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | finalizepsbt | todo | -//! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | -//! | joinpsbts | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | finalizepsbt | done (untested) | +//! | fundrawtransaction | done | +//! | getrawtransaction | done | +//! | joinpsbts | done (untested) | //! | sendrawtransaction | done | -//! | signrawtransactionwithkey | todo | -//! | testmempoolaccept | todo | -//! | utxoupdatepsbt | todo | +//! | signrawtransactionwithkey | done | +//! | testmempoolaccept | done (untested) | +//! | utxoupdatepsbt | done (untested) | //! //! //! @@ -221,13 +221,6 @@ //! | getzmqnotifications | done (untested) | //! //! -//! -//! -//! **Items marked omitted were omitted because:** -//! -//! - Method does not return anything. -//! - Method returns a simple type (e.g. bool or integer). -//! - Method is deprecated. // JSON-RPC types by API section. mod wallet; @@ -239,29 +232,38 @@ pub use crate::{ v17::{ AddMultisigAddress, AddMultisigAddressError, AddedNode, AddedNodeAddress, AddressInformation, Banned, BumpFee, BumpFeeError, ChainTips, ChainTipsError, - ChainTipsStatus, CreateRawTransaction, CreateWallet, DumpPrivKey, DumpWallet, + ChainTipsStatus, CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, + CreateRawTransaction, CreateWallet, DecodePsbt, DecodePsbtError, DecodeRawTransaction, + DecodeScript, DecodeScriptError, DumpPrivKey, DumpWallet, FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, - GetAddressInfo, GetAddressInfoEmbedded, GetAddressInfoError, GetAddressInfoLabel, - GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, GetBlockHash, - GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, GetBlockHeaderVerboseError, - GetBlockStats, GetBlockStatsError, GetBlockTemplate, GetBlockTemplateError, - GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseZero, GetChainTips, - GetChainTxStats, GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, GetMempoolInfo, - GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, GetNetworkInfoAddress, - GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, GetPeerInfo, - GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetReceivedByAddress, - GetTransaction, GetTransactionDetail, GetTransactionError, GetTxOut, GetTxOutError, - GetTxOutSetInfo, GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, - GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsItem, ListBanned, - ListLabels, ListLockUnspent, ListLockUnspentItem, ListReceivedByAddress, - ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockTransaction, ListTransactions, - ListTransactionsItem, ListUnspent, ListUnspentItem, ListWallets, LoadWallet, Locked, - PeerInfo, RescanBlockchain, ScriptPubkey, SendMany, SendRawTransaction, SendToAddress, - SignErrorData, SignMessage, SignRawTransactionWithWallet, SoftforkReject, - TransactionCategory, UploadTarget, VerifyTxOutProof, WalletCreateFundedPsbt, - WalletProcessPsbt, + GetAddressInfo, GetAddressInfoEmbedded, GetAddressInfoEmbeddedError, GetAddressInfoError, + GetAddressInfoLabel, GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, + GetBlockHash, GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, + GetBlockHeaderVerboseError, GetBlockStats, GetBlockStatsError, GetBlockTemplate, + GetBlockTemplateError, GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseZero, + GetChainTips, GetChainTxStats, GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, + GetMempoolInfo, GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, + GetNetworkInfoAddress, GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, + GetPeerInfo, GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetRawTransaction, + GetRawTransactionVerbose, GetRawTransactionVerboseError, GetReceivedByAddress, + GetTransaction, GetTransactionDetail, GetTransactionDetailError, GetTransactionError, + GetTxOut, GetTxOutError, GetTxOutSetInfo, GetTxOutSetInfoError, GetUnconfirmedBalance, + GetWalletInfo, GetWalletInfoError, GetZmqNotifications, ListAddressGroupings, + ListAddressGroupingsError, ListAddressGroupingsItem, ListBanned, ListLabels, + ListLockUnspent, ListLockUnspentItem, ListLockUnspentItemError, ListReceivedByAddress, + ListReceivedByAddressError, ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockError, + ListSinceBlockTransaction, ListSinceBlockTransactionError, ListTransactions, + ListTransactionsItem, ListTransactionsItemError, ListUnspent, ListUnspentItem, + ListUnspentItemError, ListWallets, LoadWallet, Locked, PeerInfo, RawTransactionError, + RawTransactionInput, RawTransactionOutput, RescanBlockchain, SendMany, SendRawTransaction, + SendToAddress, SignMessage, SignRawTransaction, SignRawTransactionError, SoftforkReject, + TestMempoolAccept, TransactionCategory, UploadTarget, VerifyTxOutProof, + WalletCreateFundedPsbt, WalletCreateFundedPsbtError, WalletProcessPsbt, WitnessUtxo, + }, + v18::{ + ActiveCommand, AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, GetRpcInfo, JoinPsbts, UtxoUpdatePsbt, }, - v18::{ActiveCommand, GetRpcInfo}, v19::{ Bip9SoftforkInfo, Bip9SoftforkStatistics, Bip9SoftforkStatus, GetBalances, GetBalancesMine, GetBalancesWatchOnly, GetBlockFilter, GetBlockFilterError, GetBlockchainInfo, diff --git a/types/src/v22/mod.rs b/types/src/v22/mod.rs index 28f572af..5b3201aa 100644 --- a/types/src/v22/mod.rs +++ b/types/src/v22/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: CC0-1.0 -//! # JSON-RPC types for Bitcoin Core `v0.22` +//! # JSON-RPC types for Bitcoin Core `v22` //! //! These structs are shaped for the JSON data returned by the JSON-RPC API. They use stdlib types //! (or custom types) and where necessary implement an `into_model` function to convert the type to @@ -110,23 +110,23 @@ //! //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| -//! | analyzepsbt | todo | -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | analyzepsbt | done | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | finalizepsbt | todo | -//! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | -//! | joinpsbts | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | finalizepsbt | done (untested) | +//! | fundrawtransaction | done | +//! | getrawtransaction | done | +//! | joinpsbts | done (untested) | //! | sendrawtransaction | done | -//! | signrawtransactionwithkey | todo | -//! | testmempoolaccept | todo | -//! | utxoupdatepsbt | todo | +//! | signrawtransactionwithkey | done | +//! | testmempoolaccept | done (untested) | +//! | utxoupdatepsbt | done (untested) | //! //! //! @@ -232,13 +232,6 @@ //! | getzmqnotifications | done (untested) | //! //! -//! -//! -//! **Items marked omitted were omitted because:** -//! -//! - Method does not return anything. -//! - Method returns a simple type (e.g. bool or integer). -//! - Method is deprecated. // JSON-RPC types by API section. mod blockchain; @@ -254,28 +247,38 @@ pub use crate::{ v17::{ AddMultisigAddress, AddMultisigAddressError, AddedNode, AddedNodeAddress, AddressInformation, Banned, BumpFee, BumpFeeError, ChainTips, ChainTipsError, - ChainTipsStatus, CreateRawTransaction, CreateWallet, DumpPrivKey, DumpWallet, + ChainTipsStatus, CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, + CreateRawTransaction, CreateWallet, DecodePsbt, DecodePsbtError, DecodeRawTransaction, + DecodeScript, DecodeScriptError, DumpPrivKey, DumpWallet, FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, - GetAddressInfo, GetAddressInfoEmbedded, GetAddressInfoError, GetAddressInfoLabel, - GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, GetBlockHash, - GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, GetBlockHeaderVerboseError, - GetBlockStats, GetBlockStatsError, GetBlockTemplate, GetBlockTemplateError, - GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseZero, GetChainTips, - GetChainTxStats, GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, GetMempoolInfo, - GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, GetNetworkInfoAddress, - GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, GetPeerInfo, - GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetReceivedByAddress, - GetTransaction, GetTransactionDetail, GetTransactionError, GetTxOutSetInfo, - GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, GetZmqNotifications, - ListAddressGroupings, ListAddressGroupingsItem, ListBanned, ListLabels, ListLockUnspent, - ListLockUnspentItem, ListReceivedByAddress, ListReceivedByAddressItem, ListSinceBlock, - ListSinceBlockTransaction, ListTransactions, ListTransactionsItem, ListUnspent, - ListUnspentItem, ListWallets, LoadWallet, Locked, PeerInfo, RescanBlockchain, SendMany, - SendRawTransaction, SendToAddress, SignErrorData, SignMessage, - SignRawTransactionWithWallet, SoftforkReject, TransactionCategory, UploadTarget, - VerifyTxOutProof, WalletCreateFundedPsbt, WalletProcessPsbt, + GetAddressInfo, GetAddressInfoEmbedded, GetAddressInfoEmbeddedError, GetAddressInfoError, + GetAddressInfoLabel, GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, + GetBlockHash, GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, + GetBlockHeaderVerboseError, GetBlockStats, GetBlockStatsError, GetBlockTemplate, + GetBlockTemplateError, GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseZero, + GetChainTips, GetChainTxStats, GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, + GetMempoolInfo, GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, + GetNetworkInfoAddress, GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, + GetPeerInfo, GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetRawTransaction, + GetRawTransactionVerbose, GetRawTransactionVerboseError, GetReceivedByAddress, + GetTransaction, GetTransactionDetail, GetTransactionDetailError, GetTransactionError, + GetTxOutSetInfo, GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, + GetWalletInfoError, GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsError, + ListAddressGroupingsItem, ListBanned, ListLabels, ListLockUnspent, ListLockUnspentItem, + ListLockUnspentItemError, ListReceivedByAddress, ListReceivedByAddressError, + ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockError, ListSinceBlockTransaction, + ListSinceBlockTransactionError, ListTransactions, ListTransactionsItem, + ListTransactionsItemError, ListUnspent, ListUnspentItem, ListUnspentItemError, ListWallets, + LoadWallet, Locked, PeerInfo, RawTransactionError, RawTransactionInput, + RawTransactionOutput, RescanBlockchain, SendMany, SendRawTransaction, SendToAddress, + SignMessage, SignRawTransaction, SignRawTransactionError, SoftforkReject, + TestMempoolAccept, TransactionCategory, UploadTarget, VerifyTxOutProof, + WalletCreateFundedPsbt, WalletCreateFundedPsbtError, WalletProcessPsbt, WitnessUtxo, + }, + v18::{ + ActiveCommand, AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, GetRpcInfo, JoinPsbts, UtxoUpdatePsbt, }, - v18::{ActiveCommand, GetRpcInfo}, v19::{ Bip9SoftforkInfo, Bip9SoftforkStatistics, Bip9SoftforkStatus, GetBalances, GetBalancesMine, GetBalancesWatchOnly, GetBlockFilter, GetBlockFilterError, GetBlockchainInfo, diff --git a/types/src/v23/mod.rs b/types/src/v23/mod.rs index b0bc177c..01f52a22 100644 --- a/types/src/v23/mod.rs +++ b/types/src/v23/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: CC0-1.0 -//! # JSON-RPC types for Bitcoin Core `v0.23` +//! # JSON-RPC types for Bitcoin Core `v23` //! //! These structs are shaped for the JSON data returned by the JSON-RPC API. They use stdlib types //! (or custom types) and where necessary implement an `into_model` function to convert the type to @@ -102,22 +102,22 @@ //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| //! | analyzepsbt | todo | -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | finalizepsbt | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | finalizepsbt | done (untested) | //! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | +//! | getrawtransaction | done | //! | joinpsbts | todo | //! | sendrawtransaction | done | -//! | signrawtransactionwithkey | todo | -//! | testmempoolaccept | todo | -//! | utxoupdatepsbt | todo | +//! | signrawtransactionwithkey | done | +//! | testmempoolaccept | done (untested) | +//! | utxoupdatepsbt | done (untested) | //! //! //! @@ -205,7 +205,7 @@ //! | settxfee | omitted | //! | setwalletflag | todo | //! | signmessage | done (untested) | -//! | signrawtransactionwithwallet | done (untested) | +//! | signrawtransactionwithwallet | done | //! | unloadwallet | done | //! | upgradewallet | todo | //! | walletcreatefundedpsbt | done (untested) | @@ -225,41 +225,51 @@ //! | getzmqnotifications | done (untested) | //! //! -//! -//! -//! **Items marked omitted were omitted because:** -//! -//! - Method does not return anything. -//! - Method returns a simple type (e.g. bool or integer). -//! - Method is deprecated. + +// JSON-RPC types by API section. +mod raw_transactions; +#[doc(inline)] +pub use self::raw_transactions::{ + DecodePsbt, DecodePsbtError, GlobalXpub, Proprietary, PsbtInput, PsbtOutput, +}; #[doc(inline)] pub use crate::{ v17::{ AddMultisigAddress, AddMultisigAddressError, AddedNode, AddedNodeAddress, AddressInformation, Banned, BumpFee, BumpFeeError, ChainTips, ChainTipsError, - ChainTipsStatus, CreateRawTransaction, CreateWallet, DumpPrivKey, DumpWallet, - FundRawTransaction, FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, - GetAddressInfo, GetAddressInfoEmbedded, GetAddressInfoError, GetAddressInfoLabel, - GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, GetBlockHash, - GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, GetBlockHeaderVerboseError, - GetBlockStats, GetBlockStatsError, GetBlockTemplate, GetBlockTemplateError, - GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseZero, GetChainTips, - GetChainTxStats, GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, GetMempoolInfo, - GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, GetNetworkInfoAddress, - GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, GetPeerInfo, - GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetReceivedByAddress, - GetTransaction, GetTransactionDetail, GetTransactionError, GetTxOutSetInfo, - GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, GetZmqNotifications, - ListAddressGroupings, ListAddressGroupingsItem, ListBanned, ListLabels, ListLockUnspent, - ListLockUnspentItem, ListReceivedByAddress, ListReceivedByAddressItem, ListSinceBlock, - ListSinceBlockTransaction, ListTransactions, ListTransactionsItem, ListUnspent, - ListUnspentItem, ListWallets, LoadWallet, Locked, PeerInfo, RescanBlockchain, SendMany, - SendRawTransaction, SendToAddress, SignErrorData, SignMessage, - SignRawTransactionWithWallet, SoftforkReject, TransactionCategory, UploadTarget, - VerifyTxOutProof, WalletCreateFundedPsbt, WalletProcessPsbt, + ChainTipsStatus, CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, + CreateRawTransaction, CreateWallet, DecodeRawTransaction, DecodeScript, DecodeScriptError, + DumpPrivKey, DumpWallet, FinalizePsbt, FinalizePsbtError, FundRawTransaction, + FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, + GetAddressInfoEmbedded, GetAddressInfoEmbeddedError, GetAddressInfoError, + GetAddressInfoLabel, GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, + GetBlockHash, GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, + GetBlockHeaderVerboseError, GetBlockStats, GetBlockStatsError, GetBlockTemplate, + GetBlockTemplateError, GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseZero, + GetChainTips, GetChainTxStats, GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, + GetMempoolInfo, GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, + GetNetworkInfoAddress, GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, + GetPeerInfo, GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetRawTransaction, + GetRawTransactionVerbose, GetRawTransactionVerboseError, GetReceivedByAddress, + GetTransaction, GetTransactionDetail, GetTransactionDetailError, GetTransactionError, + GetTxOutSetInfo, GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, + GetWalletInfoError, GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsError, + ListAddressGroupingsItem, ListBanned, ListLabels, ListLockUnspent, ListLockUnspentItem, + ListLockUnspentItemError, ListReceivedByAddress, ListReceivedByAddressError, + ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockError, ListSinceBlockTransaction, + ListSinceBlockTransactionError, ListTransactions, ListTransactionsItem, + ListTransactionsItemError, ListUnspent, ListUnspentItem, ListUnspentItemError, ListWallets, + LoadWallet, Locked, PeerInfo, RawTransactionError, RawTransactionInput, + RawTransactionOutput, RescanBlockchain, SendMany, SendRawTransaction, SendToAddress, + SignMessage, SignRawTransaction, SignRawTransactionError, SoftforkReject, + TestMempoolAccept, TransactionCategory, UploadTarget, VerifyTxOutProof, + WalletCreateFundedPsbt, WalletCreateFundedPsbtError, WalletProcessPsbt, WitnessUtxo, + }, + v18::{ + ActiveCommand, AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, GetRpcInfo, JoinPsbts, UtxoUpdatePsbt, }, - v18::{ActiveCommand, GetRpcInfo}, v19::{ Bip9SoftforkInfo, Bip9SoftforkStatistics, Bip9SoftforkStatus, GetBalances, GetBalancesMine, GetBalancesWatchOnly, GetBlockFilter, GetBlockFilterError, GetBlockchainInfo, diff --git a/types/src/v23/raw_transactions/error.rs b/types/src/v23/raw_transactions/error.rs new file mode 100644 index 00000000..1d353948 --- /dev/null +++ b/types/src/v23/raw_transactions/error.rs @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: CC0-1.0 + +use core::fmt; + +use bitcoin::{bip32, hex, sighash}; + +use crate::error::write_err; +use crate::v17::{Bip32DerivError, PartialSignatureError, RawTransactionError, WitnessUtxoError}; + +/// Error when converting a `DecodePsbt` type into the model type. +#[derive(Debug)] +pub enum DecodePsbtError { + /// Conversion of the `tx` field failed. + Tx(RawTransactionError), + /// Conversion of the `global_xpubs` field failed. + GlobalXpubs(GlobalXpubError), + /// Conversion of the `proprietary` field failed. + Proprietary(hex::HexToBytesError), + /// Conversion of one the map items in the `unknown` field failed. + Unknown(hex::HexToBytesError), + /// Conversion of one of the PSBT inputs failed. + Inputs(PsbtInputError), + /// Conversion of one of the PSBT outputs failed. + Outputs(PsbtOutputError), +} + +impl fmt::Display for DecodePsbtError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use DecodePsbtError as E; + + match *self { + E::Tx(ref e) => write_err!(f, "conversion of raw transaction data field failed"; e), + E::GlobalXpubs(ref e) => + write_err!(f, "conversion of one the map items in the `global_xbubs` field failed"; e), + E::Proprietary(ref e) => + write_err!(f, "conversion of one the map items in the `proprietray` field failed"; e), + E::Unknown(ref e) => + write_err!(f, "conversion of one the map items in the `unknown` field failed"; e), + E::Inputs(ref e) => write_err!(f, "conversion of one of the PSBT inputs failed"; e), + E::Outputs(ref e) => write_err!(f, "conversion of one of the PSBT outputs failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DecodePsbtError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use DecodePsbtError as E; + + match *self { + E::Tx(ref e) => Some(e), + E::GlobalXpubs(ref e) => Some(e), + E::Proprietary(ref e) => Some(e), + E::Unknown(ref e) => Some(e), + E::Inputs(ref e) => Some(e), + E::Outputs(ref e) => Some(e), + } + } +} + +/// Error when converting one of the global xpubs failed. +#[derive(Debug)] +pub enum GlobalXpubError { + /// Conversion of the `xpub` field failed. + Xpub(bip32::Error), + /// Conversion of the `master_fingerprint` field failed. + MasterFingerprint(hex::HexToArrayError), + /// Conversion of the `path` field failed. + Path(bip32::Error), +} + +impl fmt::Display for GlobalXpubError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use GlobalXpubError as E; + + match *self { + E::Xpub(ref e) => write_err!(f, "conversion of the xpub failed"; e), + E::MasterFingerprint(ref e) => + write_err!(f, "conversion of the `master_fingerprint` field failed"; e), + E::Path(ref e) => write_err!(f, "conversion of the `path` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for GlobalXpubError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use GlobalXpubError as E; + + match *self { + E::Xpub(ref e) => Some(e), + E::MasterFingerprint(ref e) => Some(e), + E::Path(ref e) => Some(e), + } + } +} + +/// Error when converting one of the `DecodePsbt` inputs failed. +#[derive(Debug)] +pub enum PsbtInputError { + /// Conversion of the `non_witness_utxo` field failed. + NonWitnessUtxo(RawTransactionError), + /// Conversion of the `witness_utxo` field failed. + WitnessUtxo(WitnessUtxoError), + /// Conversion of the `partial_signatures` field failed. + PartialSignatures(PartialSignatureError), + /// Conversion of the `sighash` field failed. + Sighash(sighash::SighashTypeParseError), + /// Conversion of the `redeem_script` field failed. + RedeemScript(hex::HexToBytesError), + /// Conversion of the `witness_script` field failed. + WitnessScript(hex::HexToBytesError), + /// Conversion of the `bip32_derivs` field failed. + Bip32Derivs(Bip32DerivError), + /// Conversion of the `final_script_sig` field failed. + FinalScriptSig(hex::HexToBytesError), + /// Conversion of the `final_script_witness` field failed. + FinalScriptWitness(hex::HexToBytesError), + /// Conversion of the `ripemd160` hash failed. + Ripemd160(hex::HexToArrayError), + /// Conversion of the `ripemd160` preimage failed. + Ripemd160Preimage(hex::HexToBytesError), + /// Conversion of the `sha256` hash failed. + Sha256(hex::HexToArrayError), + /// Conversion of the `sha256` preimage failed. + Sha256Preimage(hex::HexToBytesError), + /// Conversion of the `hash160` hash failed. + Hash160(hex::HexToArrayError), + /// Conversion of the `hash160` preimage failed. + Hash160Preimage(hex::HexToBytesError), + /// Conversion of the `hash256` hash failed. + Hash256(hex::HexToArrayError), + /// Conversion of the `hash256` preimage failed. + Hash256Preimage(hex::HexToBytesError), + /// Conversion of the `proprietary` field failed. + Proprietary(hex::HexToBytesError), + /// Conversion of the `unknown` field failed. + Unknown(hex::HexToBytesError), +} + +impl fmt::Display for PsbtInputError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use PsbtInputError as E; + + match *self { + E::NonWitnessUtxo(ref e) => + write_err!(f, "conversion of the `non_witness_utxo` field failed"; e), + E::WitnessUtxo(ref e) => + write_err!(f, "conversion of the `witness_utxo` field failed"; e), + E::PartialSignatures(ref e) => + write_err!(f, "conversion of the `partial_signatures` field failed"; e), + E::Sighash(ref e) => write_err!(f, "conversion of the `sighash` field failed"; e), + E::RedeemScript(ref e) => + write_err!(f, "conversion of the `redeem_script` field failed"; e), + E::WitnessScript(ref e) => + write_err!(f, "conversion of the `witness_script` field failed"; e), + E::Bip32Derivs(ref e) => + write_err!(f, "conversion of the `bip32_derivs` field failed"; e), + E::FinalScriptSig(ref e) => + write_err!(f, "conversion of the `final_script_sig` field failed"; e), + E::FinalScriptWitness(ref e) => + write_err!(f, "conversion of the `final_script_witness` field failed"; e), + E::Ripemd160(ref e) => write_err!(f, "conversion of the `ripemd160` hash failed"; e), + E::Ripemd160Preimage(ref e) => + write_err!(f, "conversion of the `ripemd160` preimage failed"; e), + E::Sha256(ref e) => write_err!(f, "conversion of the `sha256` hash failed"; e), + E::Sha256Preimage(ref e) => + write_err!(f, "conversion of the `sha256` preimage failed"; e), + E::Hash160(ref e) => write_err!(f, "conversion of the `hash160` hash failed"; e), + E::Hash160Preimage(ref e) => + write_err!(f, "conversion of the `hash160` preimage failed"; e), + E::Hash256(ref e) => write_err!(f, "conversion of the `hash256` hash failed"; e), + E::Hash256Preimage(ref e) => + write_err!(f, "conversion of the `hash256` preimage failed"; e), + E::Proprietary(ref e) => + write_err!(f, "conversion of one the map items in the `proprietray` field failed"; e), + E::Unknown(ref e) => write_err!(f, "conversion of the `unknown` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for PsbtInputError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use PsbtInputError as E; + + match *self { + E::NonWitnessUtxo(ref e) => Some(e), + E::WitnessUtxo(ref e) => Some(e), + E::PartialSignatures(ref e) => Some(e), + E::Sighash(ref e) => Some(e), + E::RedeemScript(ref e) => Some(e), + E::WitnessScript(ref e) => Some(e), + E::Bip32Derivs(ref e) => Some(e), + E::FinalScriptSig(ref e) => Some(e), + E::FinalScriptWitness(ref e) => Some(e), + E::Ripemd160(ref e) => Some(e), + E::Ripemd160Preimage(ref e) => Some(e), + E::Sha256(ref e) => Some(e), + E::Sha256Preimage(ref e) => Some(e), + E::Hash160(ref e) => Some(e), + E::Hash160Preimage(ref e) => Some(e), + E::Hash256(ref e) => Some(e), + E::Hash256Preimage(ref e) => Some(e), + E::Proprietary(ref e) => Some(e), + E::Unknown(ref e) => Some(e), + } + } +} + +/// Error when converting one of the `DecodePsbt` outputs failed. +#[derive(Debug)] +pub enum PsbtOutputError { + /// Conversion of the `redeem_script` field failed. + RedeemScript(hex::HexToBytesError), + /// Conversion of the `witness_script` field failed. + WitnessScript(hex::HexToBytesError), + /// Conversion of the `bip32_derivs` field failed. + Bip32Derivs(Bip32DerivError), + /// Conversion of the `proprietary` field failed. + Proprietary(hex::HexToBytesError), + /// Conversion of the `unknown` field failed. + Unknown(hex::HexToBytesError), +} + +impl fmt::Display for PsbtOutputError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use PsbtOutputError as E; + + match *self { + E::RedeemScript(ref e) => + write_err!(f, "conversion of the `redeem_script` field failed"; e), + E::WitnessScript(ref e) => + write_err!(f, "conversion of the `witness_script` field failed"; e), + E::Bip32Derivs(ref e) => + write_err!(f, "conversion of the `bip32_derivs` field failed"; e), + E::Proprietary(ref e) => + write_err!(f, "conversion of one the map items in the `proprietray` field failed"; e), + E::Unknown(ref e) => write_err!(f, "conversion of the `unknown` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for PsbtOutputError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use PsbtOutputError as E; + + match *self { + E::RedeemScript(ref e) => Some(e), + E::WitnessScript(ref e) => Some(e), + E::Bip32Derivs(ref e) => Some(e), + E::Proprietary(ref e) => Some(e), + E::Unknown(ref e) => Some(e), + } + } +} diff --git a/types/src/v23/raw_transactions/into.rs b/types/src/v23/raw_transactions/into.rs new file mode 100644 index 00000000..043a496f --- /dev/null +++ b/types/src/v23/raw_transactions/into.rs @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: CC0-1.0 + +use std::collections::BTreeMap; + +use bitcoin::bip32::{DerivationPath, Fingerprint, KeySource, Xpub}; +use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d}; +use bitcoin::hex::{self, FromHex as _}; +use bitcoin::psbt::{self, raw, PsbtSighashType}; +use bitcoin::Amount; + +use super::{ + DecodePsbt, DecodePsbtError, GlobalXpub, GlobalXpubError, Proprietary, PsbtInput, + PsbtInputError, PsbtOutput, PsbtOutputError, +}; +use crate::model; + +impl DecodePsbt { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + use DecodePsbtError as E; + + let unsigned_tx = self.tx.to_transaction().map_err(E::Tx)?; + let version = self.psbt_version; + + let mut xpubs = BTreeMap::default(); + for g in self.global_xpubs { + let (xpub, key_source) = g.to_key_value_pair().map_err(E::GlobalXpubs)?; + xpubs.insert(xpub, key_source); + } + + let proprietary = match self.proprietary { + Some(props) => { + let mut map = BTreeMap::default(); + for prop in props { + let (key, vec) = prop.to_key_value_pair().map_err(E::Proprietary)?; + map.insert(key, vec); + } + map + } + None => BTreeMap::default(), + }; + + let unknown = match self.unknown { + Some(map) => crate::psbt::into_unknown(map).map_err(E::Unknown)?, + None => BTreeMap::default(), + }; + + let inputs = self + .inputs + .into_iter() + .map(|input| input.into_input()) + .collect::>() + .map_err(E::Inputs)?; + let outputs = self + .outputs + .into_iter() + .map(|output| output.into_output()) + .collect::>() + .map_err(E::Outputs)?; + + let psbt = bitcoin::Psbt { + unsigned_tx, + version, + xpub: xpubs, + proprietary, + unknown, + inputs, + outputs, + }; + let fee = self.fee.map(Amount::from_sat); + + Ok(model::DecodePsbt { psbt, fee }) + } +} + +impl GlobalXpub { + /// Converts this global xpub list element to a map entry suitable to use in `bitcoin::Psbt`. + pub fn to_key_value_pair(&self) -> Result<(Xpub, KeySource), GlobalXpubError> { + use GlobalXpubError as E; + + let xpub = self.xpub.parse::().map_err(E::Xpub)?; + let fp = Fingerprint::from_hex(&self.master_fingerprint).map_err(E::MasterFingerprint)?; + let path = self.path.parse::().map_err(E::Path)?; + Ok((xpub, (fp, path))) + } +} + +impl Proprietary { + /// Converts this proprietary list element to a map entry suitable to use in `bitcoin::Psbt`. + pub fn to_key_value_pair( + &self, + ) -> Result<(raw::ProprietaryKey, Vec), hex::HexToBytesError> { + // FIXME: Remove cast once rust-bitcoin 0.33 is out. + // + // This is changed to a u64 in the upcoming rust-bitcoin + // release, until then just ignore any additional bits. + let subtype = self.subtype as u8; + + let prefix = Vec::from_hex(&self.identifier)?; + let key = Vec::from_hex(&self.key)?; + let value = Vec::from_hex(&self.value)?; + + Ok((raw::ProprietaryKey { prefix, subtype, key }, value)) + } +} + +impl PsbtInput { + /// Converts this PSBT data into a PSBT input. + pub fn into_input(self) -> Result { + use PsbtInputError as E; + + let non_witness_utxo = self + .non_witness_utxo + .map(|raw| raw.to_transaction()) + .transpose() + .map_err(E::NonWitnessUtxo)?; + let witness_utxo = + self.witness_utxo.map(|utxo| utxo.to_tx_out()).transpose().map_err(E::WitnessUtxo)?; + let partial_sigs = match self.partial_signatures { + Some(map) => crate::psbt::into_partial_signatures(map).map_err(E::PartialSignatures)?, + None => BTreeMap::default(), + }; + let sighash_type = self + .sighash + .map(|partial| partial.parse::()) + .transpose() + .map_err(E::Sighash)?; + let redeem_script = self + .redeem_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::RedeemScript)?; + let witness_script = self + .witness_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::WitnessScript)?; + let bip32_derivation = match self.bip32_derivs { + Some(derivs) => + crate::psbt::vec_into_bip32_derivation(derivs).map_err(E::Bip32Derivs)?, + None => BTreeMap::default(), + }; + let final_script_sig = self + .final_script_sig + .map(|script| script.script_buf()) + .transpose() + .map_err(E::FinalScriptSig)?; + let final_script_witness = self + .final_script_witness + .map(|v| crate::witness_from_hex_slice(&v)) + .transpose() + .map_err(E::FinalScriptWitness)?; + + let ripemd160_preimages = match self.ripemd160_preimages { + Some(map) => { + let mut preimages = BTreeMap::default(); + for (hash, preimage) in map.iter() { + let hash = hash.parse::().map_err(E::Ripemd160)?; + let preimage = Vec::from_hex(preimage).map_err(E::Ripemd160Preimage)?; + preimages.insert(hash, preimage); + } + preimages + } + None => BTreeMap::default(), + }; + let sha256_preimages = match self.sha256_preimages { + Some(map) => { + let mut preimages = BTreeMap::default(); + for (hash, preimage) in map.iter() { + let hash = hash.parse::().map_err(E::Sha256)?; + let preimage = Vec::from_hex(preimage).map_err(E::Sha256Preimage)?; + preimages.insert(hash, preimage); + } + preimages + } + None => BTreeMap::default(), + }; + let hash160_preimages = match self.hash160_preimages { + Some(map) => { + let mut preimages = BTreeMap::default(); + for (hash, preimage) in map.iter() { + let hash = hash.parse::().map_err(E::Hash160)?; + let preimage = Vec::from_hex(preimage).map_err(E::Hash160Preimage)?; + preimages.insert(hash, preimage); + } + preimages + } + None => BTreeMap::default(), + }; + let hash256_preimages = match self.hash256_preimages { + Some(map) => { + let mut preimages = BTreeMap::default(); + for (hash, preimage) in map.iter() { + let hash = hash.parse::().map_err(E::Hash256)?; + let preimage = Vec::from_hex(preimage).map_err(E::Hash256Preimage)?; + preimages.insert(hash, preimage); + } + preimages + } + None => BTreeMap::default(), + }; + + let proprietary = match self.proprietary { + Some(props) => { + let mut map = BTreeMap::default(); + for prop in props { + let (key, vec) = prop.to_key_value_pair().map_err(E::Proprietary)?; + map.insert(key, vec); + } + map + } + None => BTreeMap::default(), + }; + + let unknown = match self.unknown { + Some(map) => crate::psbt::into_unknown(map).map_err(E::Unknown)?, + None => BTreeMap::default(), + }; + + // These fields do not appear until Core v24. + let tap_key_sig = None; + let tap_script_sigs = BTreeMap::default(); + let tap_scripts = BTreeMap::default(); + let tap_key_origins = BTreeMap::default(); + let tap_internal_key = None; + let tap_merkle_root = None; + + Ok(psbt::Input { + non_witness_utxo, + witness_utxo, + partial_sigs, + sighash_type, + redeem_script, + witness_script, + bip32_derivation, + final_script_sig, + final_script_witness, + ripemd160_preimages, + sha256_preimages, + hash160_preimages, + hash256_preimages, + tap_key_sig, + tap_script_sigs, + tap_scripts, + tap_key_origins, + tap_internal_key, + tap_merkle_root, + proprietary, + unknown, + }) + } +} + +impl PsbtOutput { + /// Converts this PSBT data into a PSBT output. + pub fn into_output(self) -> Result { + use PsbtOutputError as E; + + let redeem_script = self + .redeem_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::RedeemScript)?; + let witness_script = self + .witness_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::WitnessScript)?; + let bip32_derivation = match self.bip32_derivs { + Some(derivs) => + crate::psbt::vec_into_bip32_derivation(derivs).map_err(E::Bip32Derivs)?, + None => BTreeMap::default(), + }; + + let proprietary = match self.proprietary { + Some(props) => { + let mut map = BTreeMap::default(); + for prop in props { + let (key, vec) = prop.to_key_value_pair().map_err(E::Proprietary)?; + map.insert(key, vec); + } + map + } + None => BTreeMap::default(), + }; + + let unknown = match self.unknown { + Some(map) => crate::psbt::into_unknown(map).map_err(E::Unknown)?, + None => BTreeMap::default(), + }; + + // These fields do not appear until Core v24. + let tap_internal_key = None; + let tap_tree = None; + let tap_key_origins = BTreeMap::default(); + + Ok(psbt::Output { + redeem_script, + witness_script, + bip32_derivation, + tap_internal_key, + tap_tree, + tap_key_origins, + proprietary, + unknown, + }) + } +} diff --git a/types/src/v23/raw_transactions/mod.rs b/types/src/v23/raw_transactions/mod.rs new file mode 100644 index 00000000..28c3cece --- /dev/null +++ b/types/src/v23/raw_transactions/mod.rs @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! The JSON-RPC API for Bitcoin Core `v23` - raw transactions. +//! +//! Types for methods found under the `== Rawtransactions ==` section of the API docs. + +mod error; +mod into; + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use crate::ScriptSig; + +#[rustfmt::skip] // Keep public re-exports separate. +pub use self::error::{DecodePsbtError, GlobalXpubError, PsbtInputError, PsbtOutputError}; +// Re-export types that appear in the public API of this module. +pub use crate::psbt::{Bip32Deriv, PsbtScript, RawTransaction, WitnessUtxo}; + +/// Result of JSON-RPC method `decodepsbt`. +/// +/// > decodepsbt "psbt" +/// > +/// > Return a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction. +/// > +/// > Arguments: +/// > 1. "psbt" (string, required) The PSBT base64 string +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct DecodePsbt { + /// The decoded network-serialized unsigned transaction. + pub tx: RawTransaction, + /// The global xpubs. + pub global_xpubs: Vec, + /// The PSBT version number. Not to be confused with the unsigned transaction version. + pub psbt_version: u32, + /// The global proprietary map. + pub proprietary: Option>, + /// The unknown global fields. + pub unknown: Option>, + /// Array of transaction inputs. + pub inputs: Vec, + /// Array of transaction outputs. + pub outputs: Vec, + /// The transaction fee paid if all UTXOs slots in the PSBT have been filled. + pub fee: Option, +} + +/// An item from the global xpubs list of `DecodePsbt`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct GlobalXpub { + /// The extended public key this path corresponds to. + pub xpub: String, + /// The fingerprint of the master key. + pub master_fingerprint: String, + /// The path. + pub path: String, +} + +/// An item from the global proprietary list of `DecodePsbt`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct Proprietary { + /// The hex string for the proprietary identifier. + identifier: String, + /// The number for the subtype. + subtype: i64, + /// The hex for the key. + key: String, + /// The hex for the value. + value: String, +} + +/// An input in a partially signed Bitcoin transaction. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct PsbtInput { + /// Decoded network transaction for non-witness UTXOs. + pub non_witness_utxo: Option, + /// Transaction output for witness UTXOs. + pub witness_utxo: Option, + /// The public key and signature that corresponds to it. + pub partial_signatures: Option>, + /// The sighash type to be used. + pub sighash: Option, + /// The redeem script. + pub redeem_script: Option, + /// The witness script. + pub witness_script: Option, + /// The public key with the derivation path as the value. + pub bip32_derivs: Option>, + /// The final scriptsig. + #[serde(rename = "final_scriptsig")] + pub final_script_sig: Option, + /// Hex-encoded witness data (if any). + #[serde(rename = "final_scriptwitness")] + pub final_script_witness: Option>, + /// The hash and preimage that corresponds to it. + pub ripemd160_preimages: Option>, + /// The hash and preimage that corresponds to it. + pub sha256_preimages: Option>, + /// The hash and preimage that corresponds to it. + pub hash160_preimages: Option>, + /// The hash and preimage that corresponds to it. + pub hash256_preimages: Option>, + /// The input proprietary map. + pub proprietary: Option>, + /// The unknown input fields. + pub unknown: Option>, +} + +/// An output in a partially signed Bitcoin transaction. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct PsbtOutput { + /// The redeem script. + pub redeem_script: Option, + /// The witness script. + pub witness_script: Option, + /// The public key with the derivation path as the value. + pub bip32_derivs: Option>, + /// The output proprietary map. + pub proprietary: Option>, + /// The unknown global fields. + pub unknown: Option>, +} diff --git a/types/src/v24/mod.rs b/types/src/v24/mod.rs index 63488831..e7b23cbe 100644 --- a/types/src/v24/mod.rs +++ b/types/src/v24/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: CC0-1.0 -//! # JSON-RPC types for Bitcoin Core `v0.24` +//! # JSON-RPC types for Bitcoin Core `v24` //! //! These structs are shaped for the JSON data returned by the JSON-RPC API. They use stdlib types //! (or custom types) and where necessary implement an `into_model` function to convert the type to @@ -102,23 +102,23 @@ //! //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| -//! | analyzepsbt | todo | -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | analyzepsbt | done | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | finalizepsbt | todo | -//! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | -//! | joinpsbts | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | finalizepsbt | done (untested) | +//! | fundrawtransaction | done | +//! | getrawtransaction | done | +//! | joinpsbts | done (untested) | //! | sendrawtransaction | done | -//! | signrawtransactionwithkey | todo | -//! | testmempoolaccept | todo | -//! | utxoupdatepsbt | todo | +//! | signrawtransactionwithkey | done | +//! | testmempoolaccept | done (untested) | +//! | utxoupdatepsbt | done (untested) | //! //! //! @@ -229,41 +229,52 @@ //! | getzmqnotifications | done (untested) | //! //! -//! -//! -//! **Items marked omitted were omitted because:** -//! -//! - Method does not return anything. -//! - Method returns a simple type (e.g. bool or integer). -//! - Method is deprecated. + +// JSON-RPC types by API section. +mod raw_transactions; +#[doc(inline)] +pub use self::raw_transactions::{ + DecodePsbt, DecodePsbtError, GlobalXpub, Proprietary, PsbtInput, PsbtOutput, TaprootBip32Deriv, + TaprootLeaf, TaprootScript, TaprootScriptPathSig, +}; #[doc(inline)] pub use crate::{ v17::{ AddMultisigAddress, AddMultisigAddressError, AddedNode, AddedNodeAddress, AddressInformation, Banned, BumpFee, BumpFeeError, ChainTips, ChainTipsError, - ChainTipsStatus, CreateRawTransaction, CreateWallet, DumpPrivKey, DumpWallet, - FundRawTransaction, FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, - GetAddressInfo, GetAddressInfoEmbedded, GetAddressInfoError, GetAddressInfoLabel, - GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, GetBlockHash, - GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, GetBlockHeaderVerboseError, - GetBlockStats, GetBlockStatsError, GetBlockTemplate, GetBlockTemplateError, - GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseZero, GetChainTips, - GetChainTxStats, GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, GetMempoolInfo, - GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, GetNetworkInfoAddress, - GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, GetPeerInfo, - GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetReceivedByAddress, - GetTransaction, GetTransactionDetail, GetTransactionError, GetTxOutSetInfo, - GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, GetZmqNotifications, - ListAddressGroupings, ListAddressGroupingsItem, ListBanned, ListLabels, ListLockUnspent, - ListLockUnspentItem, ListReceivedByAddress, ListReceivedByAddressItem, ListSinceBlock, - ListSinceBlockTransaction, ListTransactions, ListTransactionsItem, ListUnspent, - ListUnspentItem, ListWallets, LoadWallet, Locked, PeerInfo, RescanBlockchain, SendMany, - SendRawTransaction, SendToAddress, SignErrorData, SignMessage, - SignRawTransactionWithWallet, SoftforkReject, TransactionCategory, UploadTarget, - VerifyTxOutProof, WalletCreateFundedPsbt, WalletProcessPsbt, + ChainTipsStatus, CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, + CreateRawTransaction, CreateWallet, DecodeRawTransaction, DecodeScript, DecodeScriptError, + DumpPrivKey, DumpWallet, FinalizePsbt, FinalizePsbtError, FundRawTransaction, + FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, + GetAddressInfoEmbedded, GetAddressInfoEmbeddedError, GetAddressInfoError, + GetAddressInfoLabel, GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, + GetBlockHash, GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, + GetBlockHeaderVerboseError, GetBlockStats, GetBlockStatsError, GetBlockTemplate, + GetBlockTemplateError, GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseZero, + GetChainTips, GetChainTxStats, GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, + GetMempoolInfo, GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, + GetNetworkInfoAddress, GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, + GetPeerInfo, GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetRawTransaction, + GetRawTransactionVerbose, GetRawTransactionVerboseError, GetReceivedByAddress, + GetTransaction, GetTransactionDetail, GetTransactionDetailError, GetTransactionError, + GetTxOutSetInfo, GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, + GetWalletInfoError, GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsError, + ListAddressGroupingsItem, ListBanned, ListLabels, ListLockUnspent, ListLockUnspentItem, + ListLockUnspentItemError, ListReceivedByAddress, ListReceivedByAddressError, + ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockError, ListSinceBlockTransaction, + ListSinceBlockTransactionError, ListTransactions, ListTransactionsItem, + ListTransactionsItemError, ListUnspent, ListUnspentItem, ListUnspentItemError, ListWallets, + LoadWallet, Locked, PeerInfo, RawTransactionError, RawTransactionInput, + RawTransactionOutput, RescanBlockchain, SendMany, SendRawTransaction, SendToAddress, + SignMessage, SignRawTransaction, SignRawTransactionError, SoftforkReject, + TestMempoolAccept, TransactionCategory, UploadTarget, VerifyTxOutProof, + WalletCreateFundedPsbt, WalletCreateFundedPsbtError, WalletProcessPsbt, WitnessUtxo, + }, + v18::{ + ActiveCommand, AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, GetRpcInfo, JoinPsbts, UtxoUpdatePsbt, }, - v18::{ActiveCommand, GetRpcInfo}, v19::{ Bip9SoftforkInfo, Bip9SoftforkStatistics, Bip9SoftforkStatus, GetBalances, GetBalancesMine, GetBalancesWatchOnly, GetBlockFilter, GetBlockFilterError, GetBlockchainInfo, diff --git a/types/src/v24/raw_transactions/error.rs b/types/src/v24/raw_transactions/error.rs new file mode 100644 index 00000000..458f29fc --- /dev/null +++ b/types/src/v24/raw_transactions/error.rs @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: CC0-1.0 + +use core::fmt; + +use bitcoin::taproot::{IncompleteBuilderError, TaprootBuilderError, TaprootError}; +use bitcoin::{bip32, hex, secp256k1, sighash}; + +use crate::error::write_err; +use crate::v17::{Bip32DerivError, PartialSignatureError, RawTransactionError, WitnessUtxoError}; + +/// Error when converting a `DecodePsbt` type into the model type. +#[derive(Debug)] +pub enum DecodePsbtError { + /// Conversion of the `tx` field failed. + Tx(RawTransactionError), + /// Conversion of the `global_xpubs` field failed. + GlobalXpubs(GlobalXpubError), + /// Conversion of the `proprietary` field failed. + Proprietary(hex::HexToBytesError), + /// Conversion of one the map items in the `unknown` field failed. + Unknown(hex::HexToBytesError), + /// Conversion of one of the PSBT inputs failed. + Inputs(PsbtInputError), + /// Conversion of one of the PSBT outputs failed. + Outputs(PsbtOutputError), +} + +impl fmt::Display for DecodePsbtError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use DecodePsbtError as E; + + match *self { + E::Tx(ref e) => write_err!(f, "conversion of raw transaction data field failed"; e), + E::GlobalXpubs(ref e) => + write_err!(f, "conversion of one the map items in the `global_xbubs` field failed"; e), + E::Proprietary(ref e) => + write_err!(f, "conversion of one the map items in the `proprietray` field failed"; e), + E::Unknown(ref e) => + write_err!(f, "conversion of one the map items in the `unknown` field failed"; e), + E::Inputs(ref e) => write_err!(f, "conversion of one of the PSBT inputs failed"; e), + E::Outputs(ref e) => write_err!(f, "conversion of one of the PSBT outputs failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DecodePsbtError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use DecodePsbtError as E; + + match *self { + E::Tx(ref e) => Some(e), + E::GlobalXpubs(ref e) => Some(e), + E::Proprietary(ref e) => Some(e), + E::Unknown(ref e) => Some(e), + E::Inputs(ref e) => Some(e), + E::Outputs(ref e) => Some(e), + } + } +} + +/// Error when converting one of the global xpubs failed. +#[derive(Debug)] +pub enum GlobalXpubError { + /// Conversion of the `xpub` field failed. + Xpub(bip32::Error), + /// Conversion of the `master_fingerprint` field failed. + MasterFingerprint(hex::HexToArrayError), + /// Conversion of the `path` field failed. + Path(bip32::Error), +} + +impl fmt::Display for GlobalXpubError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use GlobalXpubError as E; + + match *self { + E::Xpub(ref e) => write_err!(f, "conversion of the xpub failed"; e), + E::MasterFingerprint(ref e) => + write_err!(f, "conversion of the `master_fingerprint` field failed"; e), + E::Path(ref e) => write_err!(f, "conversion of the `path` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for GlobalXpubError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use GlobalXpubError as E; + + match *self { + E::Xpub(ref e) => Some(e), + E::MasterFingerprint(ref e) => Some(e), + E::Path(ref e) => Some(e), + } + } +} + +/// Error when converting one of the `DecodePsbt` inputs failed. +#[derive(Debug)] +pub enum PsbtInputError { + /// Conversion of the `non_witness_utxo` field failed. + NonWitnessUtxo(RawTransactionError), + /// Conversion of the `witness_utxo` field failed. + WitnessUtxo(WitnessUtxoError), + /// Conversion of the `partial_signatures` field failed. + PartialSignatures(PartialSignatureError), + /// Conversion of the `sighash` field failed. + Sighash(sighash::SighashTypeParseError), + /// Conversion of the `redeem_script` field failed. + RedeemScript(hex::HexToBytesError), + /// Conversion of the `witness_script` field failed. + WitnessScript(hex::HexToBytesError), + /// Conversion of the `bip32_derivs` field failed. + Bip32Derivs(Bip32DerivError), + /// Conversion of the `final_script_sig` field failed. + FinalScriptSig(hex::HexToBytesError), + /// Conversion of the `final_script_witness` field failed. + FinalScriptWitness(hex::HexToBytesError), + /// Conversion of the `ripemd160` hash failed. + Ripemd160(hex::HexToArrayError), + /// Conversion of the `ripemd160` preimage failed. + Ripemd160Preimage(hex::HexToBytesError), + /// Conversion of the `sha256` hash failed. + Sha256(hex::HexToArrayError), + /// Conversion of the `sha256` preimage failed. + Sha256Preimage(hex::HexToBytesError), + /// Conversion of the `hash160` hash failed. + Hash160(hex::HexToArrayError), + /// Conversion of the `hash160` preimage failed. + Hash160Preimage(hex::HexToBytesError), + /// Conversion of the `hash256` hash failed. + Hash256(hex::HexToArrayError), + /// Conversion of the `hash256` preimage failed. + Hash256Preimage(hex::HexToBytesError), + /// Conversion of the `taproot_key_path_sig` field failed. + TaprootKeyPathSig(super::taproot::Error), + /// Conversion of the `taproot_script_path_sigs` field failed. + TaprootScriptPathSigs(TaprootScriptPathSigError), + /// Conversion of the `taproot_scripts` field failed. + TaprootScripts(TaprootScriptError), + /// Conversion of the `taproot_bip32_derives` field failed. + TaprootBip32Derivs(TaprootBip32DerivsError), + /// Conversion of the `taproot_internal_key` field failed. + TaprootInternalKey(secp256k1::Error), + /// Conversion of the `taproot_merkle_root` field failed. + TaprootMerkleRoot(hex::HexToArrayError), + /// Conversion of the `proprietary` field failed. + Proprietary(hex::HexToBytesError), + /// Conversion of the `unknown` field failed. + Unknown(hex::HexToBytesError), +} + +impl fmt::Display for PsbtInputError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use PsbtInputError as E; + + match *self { + E::NonWitnessUtxo(ref e) => + write_err!(f, "conversion of the `non_witness_utxo` field failed"; e), + E::WitnessUtxo(ref e) => + write_err!(f, "conversion of the `witness_utxo` field failed"; e), + E::PartialSignatures(ref e) => + write_err!(f, "conversion of the `partial_signatures` field failed"; e), + E::Sighash(ref e) => write_err!(f, "conversion of the `sighash` field failed"; e), + E::RedeemScript(ref e) => + write_err!(f, "conversion of the `redeem_script` field failed"; e), + E::WitnessScript(ref e) => + write_err!(f, "conversion of the `witness_script` field failed"; e), + E::Bip32Derivs(ref e) => + write_err!(f, "conversion of the `bip32_derivs` field failed"; e), + E::FinalScriptSig(ref e) => + write_err!(f, "conversion of the `final_script_sig` field failed"; e), + E::FinalScriptWitness(ref e) => + write_err!(f, "conversion of the `final_script_witness` field failed"; e), + E::Ripemd160(ref e) => write_err!(f, "conversion of the `ripemd160` hash failed"; e), + E::Ripemd160Preimage(ref e) => + write_err!(f, "conversion of the `ripemd160` preimage failed"; e), + E::Sha256(ref e) => write_err!(f, "conversion of the `sha256` hash failed"; e), + E::Sha256Preimage(ref e) => + write_err!(f, "conversion of the `sha256` preimage failed"; e), + E::Hash160(ref e) => write_err!(f, "conversion of the `hash160` hash failed"; e), + E::Hash160Preimage(ref e) => + write_err!(f, "conversion of the `hash160` preimage failed"; e), + E::Hash256(ref e) => write_err!(f, "conversion of the `hash256` hash failed"; e), + E::Hash256Preimage(ref e) => + write_err!(f, "conversion of the `hash256` preimage failed"; e), + E::TaprootKeyPathSig(ref e) => + write_err!(f, "conversion of the `taproot_key_path_sig` field failed"; e), + E::TaprootScriptPathSigs(ref e) => + write_err!(f, "conversion of the `taproot_script_path_sigs` field failed"; e), + E::TaprootScripts(ref e) => + write_err!(f, "conversion of the `taproot_scripts` field failed"; e), + E::TaprootBip32Derivs(ref e) => + write_err!(f, "conversion of the `taproot_bip32_derivs` field failed"; e), + E::TaprootInternalKey(ref e) => + write_err!(f, "conversion of the `taproot_internal_key` field failed"; e), + E::TaprootMerkleRoot(ref e) => + write_err!(f, "conversion of the `taproot_merkle_root` field failed"; e), + E::Proprietary(ref e) => + write_err!(f, "conversion of one the map items in the `proprietray` field failed"; e), + E::Unknown(ref e) => write_err!(f, "conversion of the `unknown` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for PsbtInputError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use PsbtInputError as E; + + match *self { + E::NonWitnessUtxo(ref e) => Some(e), + E::WitnessUtxo(ref e) => Some(e), + E::PartialSignatures(ref e) => Some(e), + E::Sighash(ref e) => Some(e), + E::RedeemScript(ref e) => Some(e), + E::WitnessScript(ref e) => Some(e), + E::Bip32Derivs(ref e) => Some(e), + E::FinalScriptSig(ref e) => Some(e), + E::FinalScriptWitness(ref e) => Some(e), + E::Ripemd160(ref e) => Some(e), + E::Ripemd160Preimage(ref e) => Some(e), + E::Sha256(ref e) => Some(e), + E::Sha256Preimage(ref e) => Some(e), + E::Hash160(ref e) => Some(e), + E::Hash160Preimage(ref e) => Some(e), + E::Hash256(ref e) => Some(e), + E::Hash256Preimage(ref e) => Some(e), + E::TaprootKeyPathSig(ref e) => Some(e), + E::TaprootScriptPathSigs(ref e) => Some(e), + E::TaprootScripts(ref e) => Some(e), + E::TaprootBip32Derivs(ref e) => Some(e), + E::TaprootInternalKey(ref e) => Some(e), + E::TaprootMerkleRoot(ref e) => Some(e), + E::Proprietary(ref e) => Some(e), + E::Unknown(ref e) => Some(e), + } + } +} + +/// Error when converting one of the `DecodePsbt` outputs failed. +#[derive(Debug)] +pub enum PsbtOutputError { + /// Conversion of the `redeem_script` field failed. + RedeemScript(hex::HexToBytesError), + /// Conversion of the `witness_script` field failed. + WitnessScript(hex::HexToBytesError), + /// Conversion of the `bip32_derivs` field failed. + Bip32Derivs(Bip32DerivError), + /// Conversion of the `taproot_internal_key` field failed. + TaprootInternalKey(secp256k1::Error), + /// Conversion of the `taproot_tree` field failed. + TaprootTree(TaprootLeafError), + /// Conversion of the `taproot_bip32_derives` field failed. + TaprootBip32Derivs(TaprootBip32DerivsError), + /// Conversion of the `proprietary` field failed. + Proprietary(hex::HexToBytesError), + /// Conversion of the `unknown` field failed. + Unknown(hex::HexToBytesError), +} + +impl fmt::Display for PsbtOutputError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use PsbtOutputError as E; + + match *self { + E::RedeemScript(ref e) => + write_err!(f, "conversion of the `redeem_script` field failed"; e), + E::WitnessScript(ref e) => + write_err!(f, "conversion of the `witness_script` field failed"; e), + E::Bip32Derivs(ref e) => + write_err!(f, "conversion of the `bip32_derivs` field failed"; e), + E::TaprootInternalKey(ref e) => + write_err!(f, "conversion of the `taproot_internal_key` field failed"; e), + E::TaprootTree(ref e) => + write_err!(f, "conversion of the `taproot_tree` field failed"; e), + E::TaprootBip32Derivs(ref e) => + write_err!(f, "conversion of the `taproot_bip32_derivs` field failed"; e), + E::Proprietary(ref e) => + write_err!(f, "conversion of one the map items in the `proprietray` field failed"; e), + E::Unknown(ref e) => write_err!(f, "conversion of the `unknown` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for PsbtOutputError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use PsbtOutputError as E; + + match *self { + E::RedeemScript(ref e) => Some(e), + E::WitnessScript(ref e) => Some(e), + E::Bip32Derivs(ref e) => Some(e), + E::TaprootInternalKey(ref e) => Some(e), + E::TaprootTree(ref e) => Some(e), + E::TaprootBip32Derivs(ref e) => Some(e), + E::Proprietary(ref e) => Some(e), + E::Unknown(ref e) => Some(e), + } + } +} + +/// Error when converting a taproot script path sig. +#[derive(Debug)] +pub enum TaprootScriptPathSigError { + /// Conversion of the `pubkey` field failed. + Pubkey(secp256k1::Error), + /// Conversion of the `leaf_hash` field failed. + LeafHash(hex::HexToArrayError), + /// Conversion of the `sig` field failed. + Sig(super::taproot::Error), +} + +impl fmt::Display for TaprootScriptPathSigError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use TaprootScriptPathSigError as E; + + match *self { + E::Pubkey(ref e) => write_err!(f, "conversion of the `pubkey` field failed"; e), + E::LeafHash(ref e) => write_err!(f, "conversion of the `leaf_hash` field failed"; e), + E::Sig(ref e) => write_err!(f, "conversion of the `sig` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TaprootScriptPathSigError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use TaprootScriptPathSigError as E; + + match *self { + E::Pubkey(ref e) => Some(e), + E::LeafHash(ref e) => Some(e), + E::Sig(ref e) => Some(e), + } + } +} + +/// Error when converting a taproot script. +#[derive(Debug)] +pub enum TaprootScriptError { + /// Conversion of the `script` field failed. + Script(hex::HexToBytesError), + /// Conversion of the `leaf_ver` field failed. + LeafVer(TaprootError), + /// Conversion of the `control_blocks` field failed. + ControlBlocks(ControlBlocksError), +} + +impl fmt::Display for TaprootScriptError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use TaprootScriptError as E; + + match *self { + E::Script(ref e) => write_err!(f, "conversion of the `script` field failed"; e), + E::LeafVer(ref e) => write_err!(f, "conversion of the `leaf_ver` field failed"; e), + E::ControlBlocks(ref e) => + write_err!(f, "conversion of the `control_blocks` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TaprootScriptError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use TaprootScriptError as E; + + match *self { + E::Script(ref e) => Some(e), + E::LeafVer(ref e) => Some(e), + E::ControlBlocks(ref e) => Some(e), + } + } +} + +/// Error when converting the control blocks vector. +#[derive(Debug)] +pub enum ControlBlocksError { + /// No control block returned by Core for this script. + Missing, + /// Multiple control blocks returned by Core for this script. + Multiple(usize), + /// Failed to parse control block hex string. + Parse(hex::HexToBytesError), + /// Failed to decode parsed bytes. + Decode(TaprootError), +} + +impl fmt::Display for ControlBlocksError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use ControlBlocksError as E; + + match *self { + E::Missing => write!(f, "no control block returned by Core for this script"), + E::Multiple(n) => + write!(f, "multiple control blocks returned by Core for this script: {}", n), + E::Parse(ref e) => write_err!(f, "failed to parse control block hex"; e), + E::Decode(ref e) => write_err!(f, "failed to decode control block from bytes"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ControlBlocksError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use ControlBlocksError as E; + + match *self { + E::Missing => None, + E::Multiple(_) => None, + E::Parse(ref e) => Some(e), + E::Decode(ref e) => Some(e), + } + } +} + +/// Error when converting a taproot BIP-32 derivation. +#[derive(Debug)] +pub enum TaprootBip32DerivsError { + /// Conversion of the `pubkey` field failed. + Pubkey(secp256k1::Error), + /// Conversion of the `master_fingerprint` field failed. + MasterFingerprint(hex::HexToArrayError), + /// Conversion of the `path` field failed. + Path(bip32::Error), + /// Conversion of one of the leaf hashes failed. + LeafHashes(hex::HexToArrayError), +} + +impl fmt::Display for TaprootBip32DerivsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use TaprootBip32DerivsError as E; + + match *self { + E::Pubkey(ref e) => write_err!(f, "conversion of the `pubkey` field failed"; e), + E::MasterFingerprint(ref e) => + write_err!(f, "conversion of the `master_fingerprint` field failed"; e), + E::Path(ref e) => write_err!(f, "conversion of the `path` field failed"; e), + E::LeafHashes(ref e) => + write_err!(f, "conversion of the `leaf_hashes` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TaprootBip32DerivsError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use TaprootBip32DerivsError as E; + + match *self { + E::Pubkey(ref e) => Some(e), + E::MasterFingerprint(ref e) => Some(e), + E::Path(ref e) => Some(e), + E::LeafHashes(ref e) => Some(e), + } + } +} + +/// Error when converting a taproot script. +#[derive(Debug)] +pub enum TaprootLeafError { + /// Conversion of the `leaf_ver` field failed. + LeafVer(TaprootError), + /// Conversion of the `script` field failed. + Script(hex::HexToBytesError), + /// Failed to add leaf to builder. + TaprootBuilder(TaprootBuilderError), + /// Failed to convert builder into a tap tree. + IncompleteBuilder(IncompleteBuilderError), +} + +impl fmt::Display for TaprootLeafError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use TaprootLeafError as E; + + match *self { + E::LeafVer(ref e) => write_err!(f, "conversion of the `leaf_ver` field failed"; e), + E::Script(ref e) => write_err!(f, "conversion of the `script` field failed"; e), + E::TaprootBuilder(ref e) => write_err!(f, "failed to add leaf to builder"; e), + E::IncompleteBuilder(ref e) => + write_err!(f, "failed to convert builder into a tap tree"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TaprootLeafError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use TaprootLeafError as E; + + match *self { + E::Script(ref e) => Some(e), + E::LeafVer(ref e) => Some(e), + E::TaprootBuilder(ref e) => Some(e), + E::IncompleteBuilder(ref e) => Some(e), + } + } +} diff --git a/types/src/v24/raw_transactions/into.rs b/types/src/v24/raw_transactions/into.rs new file mode 100644 index 00000000..cf02dcce --- /dev/null +++ b/types/src/v24/raw_transactions/into.rs @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: CC0-1.0 + +use std::collections::BTreeMap; + +use bitcoin::bip32::{DerivationPath, Fingerprint, KeySource, Xpub}; +use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d}; +use bitcoin::hex::{self, FromHex as _}; +use bitcoin::psbt::{self, raw, PsbtSighashType}; +use bitcoin::taproot::{ + ControlBlock, LeafVersion, TapLeafHash, TapNodeHash, TapTree, TaprootBuilder, +}; +use bitcoin::{Amount, ScriptBuf, XOnlyPublicKey}; + +use super::{ + taproot, ControlBlocksError, DecodePsbt, DecodePsbtError, GlobalXpub, GlobalXpubError, + Proprietary, PsbtInput, PsbtInputError, PsbtOutput, PsbtOutputError, TaprootBip32Deriv, + TaprootBip32DerivsError, TaprootLeaf, TaprootLeafError, TaprootScript, TaprootScriptError, + TaprootScriptPathSig, TaprootScriptPathSigError, +}; +use crate::model; + +impl DecodePsbt { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + use DecodePsbtError as E; + + let unsigned_tx = self.tx.to_transaction().map_err(E::Tx)?; + let version = self.psbt_version; + + let mut xpubs = BTreeMap::default(); + for g in self.global_xpubs { + let (xpub, key_source) = g.to_key_value_pair().map_err(E::GlobalXpubs)?; + xpubs.insert(xpub, key_source); + } + + let proprietary = match self.proprietary { + Some(props) => { + let mut map = BTreeMap::default(); + for prop in props { + let (key, vec) = prop.to_key_value_pair().map_err(E::Proprietary)?; + map.insert(key, vec); + } + map + } + None => BTreeMap::default(), + }; + + let unknown = match self.unknown { + Some(map) => crate::psbt::into_unknown(map).map_err(E::Unknown)?, + None => BTreeMap::default(), + }; + + let inputs = self + .inputs + .into_iter() + .map(|input| input.into_input()) + .collect::>() + .map_err(E::Inputs)?; + let outputs = self + .outputs + .into_iter() + .map(|output| output.into_output()) + .collect::>() + .map_err(E::Outputs)?; + + let psbt = bitcoin::Psbt { + unsigned_tx, + version, + xpub: xpubs, + proprietary, + unknown, + inputs, + outputs, + }; + let fee = self.fee.map(Amount::from_sat); + + Ok(model::DecodePsbt { psbt, fee }) + } +} + +impl GlobalXpub { + /// Converts this global xpub list element to a map entry suitable to use in `bitcoin::Psbt`. + pub fn to_key_value_pair(&self) -> Result<(Xpub, KeySource), GlobalXpubError> { + use GlobalXpubError as E; + + let xpub = self.xpub.parse::().map_err(E::Xpub)?; + let fp = Fingerprint::from_hex(&self.master_fingerprint).map_err(E::MasterFingerprint)?; + let path = self.path.parse::().map_err(E::Path)?; + Ok((xpub, (fp, path))) + } +} + +impl Proprietary { + /// Converts this proprietary list element to a map entry suitable to use in `bitcoin::Psbt`. + pub fn to_key_value_pair( + &self, + ) -> Result<(raw::ProprietaryKey, Vec), hex::HexToBytesError> { + // FIXME: Remove cast once rust-bitcoin 0.33 is out. + // + // This is changed to a u64 in the upcoming rust-bitcoin + // release, until then just ignore any additional bits. + let subtype = self.subtype as u8; + + let prefix = Vec::from_hex(&self.identifier)?; + let key = Vec::from_hex(&self.key)?; + let value = Vec::from_hex(&self.value)?; + + Ok((raw::ProprietaryKey { prefix, subtype, key }, value)) + } +} + +impl PsbtInput { + /// Converts this PSBT data into a PSBT input. + pub fn into_input(self) -> Result { + use PsbtInputError as E; + + let non_witness_utxo = self + .non_witness_utxo + .map(|raw| raw.to_transaction()) + .transpose() + .map_err(E::NonWitnessUtxo)?; + let witness_utxo = + self.witness_utxo.map(|utxo| utxo.to_tx_out()).transpose().map_err(E::WitnessUtxo)?; + let partial_sigs = match self.partial_signatures { + Some(map) => crate::psbt::into_partial_signatures(map).map_err(E::PartialSignatures)?, + None => BTreeMap::default(), + }; + let sighash_type = self + .sighash + .map(|partial| partial.parse::()) + .transpose() + .map_err(E::Sighash)?; + let redeem_script = self + .redeem_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::RedeemScript)?; + let witness_script = self + .witness_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::WitnessScript)?; + let bip32_derivation = match self.bip32_derivs { + Some(derivs) => + crate::psbt::vec_into_bip32_derivation(derivs).map_err(E::Bip32Derivs)?, + None => BTreeMap::default(), + }; + let final_script_sig = self + .final_script_sig + .map(|script| script.script_buf()) + .transpose() + .map_err(E::FinalScriptSig)?; + let final_script_witness = self + .final_script_witness + .map(|v| crate::witness_from_hex_slice(&v)) + .transpose() + .map_err(E::FinalScriptWitness)?; + + let ripemd160_preimages = match self.ripemd160_preimages { + Some(map) => { + let mut preimages = BTreeMap::default(); + for (hash, preimage) in map.iter() { + let hash = hash.parse::().map_err(E::Ripemd160)?; + let preimage = Vec::from_hex(preimage).map_err(E::Ripemd160Preimage)?; + preimages.insert(hash, preimage); + } + preimages + } + None => BTreeMap::default(), + }; + let sha256_preimages = match self.sha256_preimages { + Some(map) => { + let mut preimages = BTreeMap::default(); + for (hash, preimage) in map.iter() { + let hash = hash.parse::().map_err(E::Sha256)?; + let preimage = Vec::from_hex(preimage).map_err(E::Sha256Preimage)?; + preimages.insert(hash, preimage); + } + preimages + } + None => BTreeMap::default(), + }; + let hash160_preimages = match self.hash160_preimages { + Some(map) => { + let mut preimages = BTreeMap::default(); + for (hash, preimage) in map.iter() { + let hash = hash.parse::().map_err(E::Hash160)?; + let preimage = Vec::from_hex(preimage).map_err(E::Hash160Preimage)?; + preimages.insert(hash, preimage); + } + preimages + } + None => BTreeMap::default(), + }; + let hash256_preimages = match self.hash256_preimages { + Some(map) => { + let mut preimages = BTreeMap::default(); + for (hash, preimage) in map.iter() { + let hash = hash.parse::().map_err(E::Hash256)?; + let preimage = Vec::from_hex(preimage).map_err(E::Hash256Preimage)?; + preimages.insert(hash, preimage); + } + preimages + } + None => BTreeMap::default(), + }; + + let tap_key_sig = self + .taproot_key_path_sig + .map(|s| taproot::signature_from_str(&s)) + .transpose() + .map_err(E::TaprootKeyPathSig)?; + let tap_script_sigs = match self.taproot_script_path_sigs { + Some(vec) => { + let mut map = BTreeMap::default(); + for elem in vec.iter() { + let ((pubkey, hash), sig) = + elem.to_key_value_pair().map_err(E::TaprootScriptPathSigs)?; + map.insert((pubkey, hash), sig); + } + map + } + None => BTreeMap::default(), + }; + let tap_scripts = match self.taproot_scripts { + Some(vec) => { + let mut map = BTreeMap::default(); + for elem in vec.iter() { + let (control_block, (script, key_source)) = + elem.to_key_value_pair().map_err(E::TaprootScripts)?; + map.insert(control_block, (script, key_source)); + } + map + } + None => BTreeMap::default(), + }; + let tap_key_origins = match self.taproot_bip32_derivs { + Some(vec) => { + let mut map = BTreeMap::default(); + for elem in vec.iter() { + let (pubkey, (leaves, key_source)) = + elem.to_key_value_pair().map_err(E::TaprootBip32Derivs)?; + map.insert(pubkey, (leaves, key_source)); + } + map + } + None => BTreeMap::default(), + }; + let tap_internal_key = self + .taproot_internal_key + .map(|key| key.parse::()) + .transpose() + .map_err(E::TaprootInternalKey)?; + let tap_merkle_root = self + .taproot_merkle_root + .map(|root| root.parse::()) + .transpose() + .map_err(E::TaprootMerkleRoot)?; + + let proprietary = match self.proprietary { + Some(props) => { + let mut map = BTreeMap::default(); + for prop in props { + let (key, vec) = prop.to_key_value_pair().map_err(E::Proprietary)?; + map.insert(key, vec); + } + map + } + None => BTreeMap::default(), + }; + + let unknown = match self.unknown { + Some(map) => crate::psbt::into_unknown(map).map_err(E::Unknown)?, + None => BTreeMap::default(), + }; + + Ok(psbt::Input { + non_witness_utxo, + witness_utxo, + partial_sigs, + sighash_type, + redeem_script, + witness_script, + bip32_derivation, + final_script_sig, + final_script_witness, + ripemd160_preimages, + sha256_preimages, + hash160_preimages, + hash256_preimages, + tap_key_sig, + tap_script_sigs, + tap_scripts, + tap_key_origins, + tap_internal_key, + tap_merkle_root, + proprietary, + unknown, + }) + } +} + +impl PsbtOutput { + /// Converts this PSBT data into a PSBT output. + pub fn into_output(self) -> Result { + use PsbtOutputError as E; + + let redeem_script = self + .redeem_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::RedeemScript)?; + let witness_script = self + .witness_script + .map(|script| script.script_buf()) + .transpose() + .map_err(E::WitnessScript)?; + let bip32_derivation = match self.bip32_derivs { + Some(derivs) => + crate::psbt::vec_into_bip32_derivation(derivs).map_err(E::Bip32Derivs)?, + None => BTreeMap::default(), + }; + + let tap_internal_key = self + .taproot_internal_key + .map(|key| key.parse::()) + .transpose() + .map_err(E::TaprootInternalKey)?; + + let tap_tree = + self.taproot_tree.map(build_taproot_tree).transpose().map_err(E::TaprootTree)?; + let tap_key_origins = match self.taproot_bip32_derivs { + Some(vec) => { + let mut map = BTreeMap::default(); + for elem in vec.iter() { + let (pubkey, (leaves, key_source)) = + elem.to_key_value_pair().map_err(E::TaprootBip32Derivs)?; + map.insert(pubkey, (leaves, key_source)); + } + map + } + None => BTreeMap::default(), + }; + + let proprietary = match self.proprietary { + Some(props) => { + let mut map = BTreeMap::default(); + for prop in props { + let (key, vec) = prop.to_key_value_pair().map_err(E::Proprietary)?; + map.insert(key, vec); + } + map + } + None => BTreeMap::default(), + }; + + let unknown = match self.unknown { + Some(map) => crate::psbt::into_unknown(map).map_err(E::Unknown)?, + None => BTreeMap::default(), + }; + + Ok(psbt::Output { + redeem_script, + witness_script, + bip32_derivation, + tap_internal_key, + tap_tree, + tap_key_origins, + proprietary, + unknown, + }) + } +} + +impl TaprootScriptPathSig { + /// Converts list element to a map entry suitable to use in `bitcoin::psbt::Input`. + pub fn to_key_value_pair( + &self, + ) -> Result<((XOnlyPublicKey, TapLeafHash), taproot::Signature), TaprootScriptPathSigError> + { + use TaprootScriptPathSigError as E; + + let pubkey = self.pubkey.parse::().map_err(E::Pubkey)?; + let hash = self.leaf_hash.parse::().map_err(E::LeafHash)?; + let sig = super::taproot::signature_from_str(&self.sig).map_err(E::Sig)?; + + Ok(((pubkey, hash), sig)) + } +} + +impl TaprootScript { + /// Converts list element to a map entry suitable to use in `bitcoin::psbt::Input`. + pub fn to_key_value_pair( + &self, + ) -> Result<(ControlBlock, (ScriptBuf, LeafVersion)), TaprootScriptError> { + use TaprootScriptError as E; + + let script = ScriptBuf::from_hex(&self.script).map_err(E::Script)?; + + let leaf_ver = self.leaf_ver as u8; // FIXME: Is this cast ok? + let version = LeafVersion::from_consensus(leaf_ver).map_err(E::LeafVer)?; + + let control_block = control_block(&self.control_blocks).map_err(E::ControlBlocks)?; + + Ok((control_block, (script, version))) + } +} + +// FIXME: I (Tobin) cannot work out why Core returns a vector of control blocks. From my +// reading of rust-bitcoin code and also BIP-341 there is exactly one control block per script? +fn control_block(control_blocks: &[String]) -> Result { + use ControlBlocksError as E; + + match control_blocks.len() { + // FIXME: How can this be empty, there would be nothing to key the `tap_scripts` map by? + 0 => Err(E::Missing), + 1 => { + let bytes = Vec::from_hex(&control_blocks[0]).map_err(E::Parse)?; + Ok(ControlBlock::decode(&bytes).map_err(E::Decode)?) + } + n => Err(E::Multiple(n)), + } +} + +impl TaprootBip32Deriv { + /// Converts list element to a map entry suitable to use in `bitcoin::psbt::Input`. + pub fn to_key_value_pair( + &self, + ) -> Result<(XOnlyPublicKey, (Vec, KeySource)), TaprootBip32DerivsError> { + use TaprootBip32DerivsError as E; + + let pubkey = self.pubkey.parse::().map_err(E::Pubkey)?; + let fp = Fingerprint::from_hex(&self.master_fingerprint).map_err(E::MasterFingerprint)?; + let path = self.path.parse::().map_err(E::Path)?; + let hashes = self + .leaf_hashes + .iter() + .map(|leaf| leaf.parse::()) + .collect::>() + .map_err(E::LeafHashes)?; + + Ok((pubkey, (hashes, (fp, path)))) + } +} + +fn build_taproot_tree(leaves: Vec) -> Result { + use TaprootLeafError as E; + + let mut builder = TaprootBuilder::with_capacity(leaves.len()); + + for leaf in leaves.iter() { + // Cast ok because depth can never exceed 128. + let depth = leaf.depth as u8; + + let leaf_ver = leaf.leaf_ver as u8; // FIXME: Is this cast ok? + let version = LeafVersion::from_consensus(leaf_ver).map_err(E::LeafVer)?; + + let script = ScriptBuf::from_hex(&leaf.script).map_err(E::Script)?; + + builder = builder.add_leaf_with_ver(depth, script, version).map_err(E::TaprootBuilder)?; + } + let tree = builder.try_into_taptree().map_err(E::IncompleteBuilder)?; + Ok(tree) +} diff --git a/types/src/v24/raw_transactions/mod.rs b/types/src/v24/raw_transactions/mod.rs new file mode 100644 index 00000000..6894130b --- /dev/null +++ b/types/src/v24/raw_transactions/mod.rs @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! The JSON-RPC API for Bitcoin Core `v23` - raw transactions. +//! +//! Types for methods found under the `== Rawtransactions ==` section of the API docs. + +mod error; +mod into; + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use crate::ScriptSig; + +#[rustfmt::skip] // Keep public re-exports separate. +pub use self::error::{DecodePsbtError, GlobalXpubError, PsbtInputError, PsbtOutputError, TaprootScriptPathSigError, TaprootScriptError, TaprootBip32DerivsError, ControlBlocksError, TaprootLeafError}; +// Re-export types that appear in the public API of this module. +pub use crate::psbt::{Bip32Deriv, PsbtScript, RawTransaction, WitnessUtxo}; + +/// Result of JSON-RPC method `decodepsbt`. +/// +/// > decodepsbt "psbt" +/// > +/// > Return a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction. +/// > +/// > Arguments: +/// > 1. "psbt" (string, required) The PSBT base64 string +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct DecodePsbt { + /// The decoded network-serialized unsigned transaction. + pub tx: RawTransaction, + /// The global xpubs. + pub global_xpubs: Vec, + /// The PSBT version number. Not to be confused with the unsigned transaction version. + pub psbt_version: u32, + /// The global proprietary map. + pub proprietary: Option>, + /// The unknown global fields. + pub unknown: Option>, + /// Array of transaction inputs. + pub inputs: Vec, + /// Array of transaction outputs. + pub outputs: Vec, + /// The transaction fee paid if all UTXOs slots in the PSBT have been filled. + pub fee: Option, +} + +/// An item from the global xpubs list of `DecodePsbt`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct GlobalXpub { + /// The extended public key this path corresponds to. + pub xpub: String, + /// The fingerprint of the master key. + pub master_fingerprint: String, + /// The path. + pub path: String, +} + +/// An item from the global proprietary list of `DecodePsbt`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct Proprietary { + /// The hex string for the proprietary identifier. + identifier: String, + /// The number for the subtype. + subtype: i64, + /// The hex for the key. + key: String, + /// The hex for the value. + value: String, +} + +/// An input in a partially signed Bitcoin transaction. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct PsbtInput { + /// Decoded network transaction for non-witness UTXOs. + pub non_witness_utxo: Option, + /// Transaction output for witness UTXOs. + pub witness_utxo: Option, + /// The public key and signature that corresponds to it. + pub partial_signatures: Option>, + /// The sighash type to be used. + pub sighash: Option, + /// The redeem script. + pub redeem_script: Option, + /// The witness script. + pub witness_script: Option, + /// The public key with the derivation path as the value. + pub bip32_derivs: Option>, + /// The final scriptsig. + #[serde(rename = "final_scriptsig")] + pub final_script_sig: Option, + /// Hex-encoded witness data (if any). + #[serde(rename = "final_scriptwitness")] + pub final_script_witness: Option>, + /// The hash and preimage that corresponds to it. + pub ripemd160_preimages: Option>, + /// The hash and preimage that corresponds to it. + pub sha256_preimages: Option>, + /// The hash and preimage that corresponds to it. + pub hash160_preimages: Option>, + /// The hash and preimage that corresponds to it. + pub hash256_preimages: Option>, + /// Hex-encoded signature for the Taproot key path spend. + pub taproot_key_path_sig: Option, + /// The signature for the pubkey and leaf hash combination. + pub taproot_script_path_sigs: Option>, + /// Scripts and control blocks for script path spends. + pub taproot_scripts: Option>, + /// BIP-32 derivation paths for keys. + pub taproot_bip32_derivs: Option>, + /// The hex-encoded Taproot x-only internal key. + pub taproot_internal_key: Option, + /// The hex-encoded Taproot merkle root. + pub taproot_merkle_root: Option, + /// The input proprietary map. + pub proprietary: Option>, + /// The unknown input fields. + pub unknown: Option>, +} + +/// An output in a partially signed Bitcoin transaction. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct PsbtOutput { + /// The redeem script. + pub redeem_script: Option, + /// The witness script. + pub witness_script: Option, + /// The public key with the derivation path as the value. + pub bip32_derivs: Option>, + /// The hex-encoded Taproot x-only internal key. + pub taproot_internal_key: Option, + /// The tuples that make up the Taproot tree, in depth first search order. + pub taproot_tree: Option>, + /// BIP32 derivation paths for keys. + pub taproot_bip32_derivs: Option>, + /// The output proprietary map. + pub proprietary: Option>, + /// The unknown global fields. + pub unknown: Option>, +} + +/// An item from the `taproot_script_path_sigs` list of `PsbtInput`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct TaprootScriptPathSig { + /// The x-only pubkey for this signature. + pub pubkey: String, + /// The leaf hash for this signature. + pub leaf_hash: String, + /// The signature itself. + pub sig: String, +} + +/// An item from the `taproot_scripts` list of `PsbtInput`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct TaprootScript { + /// A leaf script. + pub script: String, + /// The version number for the leaf script. + pub leaf_ver: u32, + /// The control blocks for this script. + pub control_blocks: Vec, +} + +/// An item from the `taproot_bip32_derivs` list of `PsbtInput`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct TaprootBip32Deriv { + /// The x-only public key this path corresponds to. + pub pubkey: String, + /// The fingerprint of the master key. + pub master_fingerprint: String, + /// The path. + pub path: String, + /// The hashes of the leaves this pubkey appears in. + pub leaf_hashes: Vec, +} + +/// A Taproot leaf script at depth with version. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct TaprootLeaf { + /// The depth of this element in the tree. + pub depth: u32, + /// The version of this leaf. + pub leaf_ver: u32, + /// The hex-encoded script itself. + pub script: String, +} + +// TODO: Remove all this code once it is implemented and backported to 0.32.x +// https://github.com/rust-bitcoin/rust-bitcoin/issues/3285 +pub mod taproot { + use core::fmt; + + use bitcoin::hex::{self, FromHex as _}; + use bitcoin::sighash::InvalidSighashTypeError; + // Re-export this because this module is named the same as the one from `bitcoin`. + pub use bitcoin::taproot::Signature; + use bitcoin::{secp256k1, taproot, TapSighashType}; + + use crate::error::write_err; + + /// Parses a Taproot signature from a hex string. + pub fn signature_from_str(sig: &str) -> Result { + use Error as E; + + let bytes = Vec::from_hex(sig).map_err(E::Hex)?; + let (sighash_byte, signature) = bytes.split_last().ok_or(E::EmptySignature)?; + Ok(Signature { + signature: secp256k1::schnorr::Signature::from_slice(signature) + .map_err(E::Secp256k1)?, + sighash_type: TapSighashType::from_consensus_u8(*sighash_byte) + .map_err(E::SighashType)?, + }) + } + + /// A Taproot signature-related error. + #[derive(Debug, Clone, PartialEq, Eq)] + #[non_exhaustive] + pub enum Error { + /// Hex decoding error. + Hex(hex::HexToBytesError), + /// Non-standard sighash type. + SighashType(InvalidSighashTypeError), + /// Signature was empty. + EmptySignature, + /// A secp256k1 error while creating signature from a slice. + Secp256k1(secp256k1::Error), + } + + impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Error::*; + + match *self { + Hex(ref e) => write_err!(f, "signature hex decoding error"; e), + SighashType(ref e) => write_err!(f, "non-standard signature hash type"; e), + EmptySignature => write!(f, "empty Taproot signature"), + Secp256k1(ref e) => write_err!(f, "secp256k1"; e), + } + } + } + + #[cfg(feature = "std")] + impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use Error::*; + + match *self { + Hex(ref e) => Some(e), + Secp256k1(ref e) => Some(e), + SighashType(ref e) => Some(e), + EmptySignature => None, + } + } + } +} diff --git a/types/src/v25/mod.rs b/types/src/v25/mod.rs index 9d029430..dfdcaf60 100644 --- a/types/src/v25/mod.rs +++ b/types/src/v25/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: CC0-1.0 -//! # JSON-RPC types for Bitcoin Core `v0.25` +//! # JSON-RPC types for Bitcoin Core `v25` //! //! These structs are shaped for the JSON data returned by the JSON-RPC API. They use stdlib types //! (or custom types) and where necessary implement an `into_model` function to convert the type to @@ -103,23 +103,23 @@ //! //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| -//! | analyzepsbt | todo | -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | analyzepsbt | done | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | finalizepsbt | todo | -//! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | -//! | joinpsbts | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | finalizepsbt | done (untested) | +//! | fundrawtransaction | done | +//! | getrawtransaction | done | +//! | joinpsbts | done (untested) | //! | sendrawtransaction | done | -//! | signrawtransactionwithkey | todo | -//! | testmempoolaccept | todo | -//! | utxoupdatepsbt | todo | +//! | signrawtransactionwithkey | done | +//! | testmempoolaccept | done (untested) | +//! | utxoupdatepsbt | done (untested) | //! //! //! @@ -230,13 +230,6 @@ //! | getzmqnotifications | done (untested) | //! //! -//! -//! -//! **Items marked omitted were omitted because:** -//! -//! - Method does not return anything. -//! - Method returns a simple type (e.g. bool or integer). -//! - Method is deprecated. mod wallet; @@ -247,9 +240,11 @@ pub use crate::{ v17::{ AddMultisigAddress, AddMultisigAddressError, AddedNode, AddedNodeAddress, AddressInformation, Banned, BumpFee, BumpFeeError, ChainTips, ChainTipsError, - ChainTipsStatus, CreateRawTransaction, DumpPrivKey, DumpWallet, FundRawTransaction, - FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, - GetAddressInfoEmbedded, GetAddressInfoError, GetAddressInfoLabel, GetAddressesByLabel, + ChainTipsStatus, CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, + CreateRawTransaction, DecodeRawTransaction, DecodeScript, DecodeScriptError, DumpPrivKey, + DumpWallet, FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError, + Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, GetAddressInfoEmbedded, + GetAddressInfoEmbeddedError, GetAddressInfoError, GetAddressInfoLabel, GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, GetBlockHash, GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, GetBlockHeaderVerboseError, GetBlockStats, GetBlockStatsError, GetBlockTemplate, GetBlockTemplateError, GetBlockVerboseOne, @@ -257,18 +252,26 @@ pub use crate::{ GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, GetMempoolInfo, GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, GetNetworkInfoAddress, GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, GetPeerInfo, - GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetReceivedByAddress, - GetTransaction, GetTransactionDetail, GetTransactionError, GetTxOutSetInfo, - GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, GetZmqNotifications, - ListAddressGroupings, ListAddressGroupingsItem, ListBanned, ListLabels, ListLockUnspent, - ListLockUnspentItem, ListReceivedByAddress, ListReceivedByAddressItem, ListSinceBlock, - ListSinceBlockTransaction, ListTransactions, ListTransactionsItem, ListUnspent, - ListUnspentItem, ListWallets, Locked, PeerInfo, RescanBlockchain, SendMany, - SendRawTransaction, SendToAddress, SignErrorData, SignMessage, - SignRawTransactionWithWallet, SoftforkReject, TransactionCategory, UploadTarget, - VerifyTxOutProof, WalletCreateFundedPsbt, WalletProcessPsbt, + GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetRawTransaction, + GetRawTransactionVerbose, GetRawTransactionVerboseError, GetReceivedByAddress, + GetTransaction, GetTransactionDetail, GetTransactionDetailError, GetTransactionError, + GetTxOutSetInfo, GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfo, + GetWalletInfoError, GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsError, + ListAddressGroupingsItem, ListBanned, ListLabels, ListLockUnspent, ListLockUnspentItem, + ListLockUnspentItemError, ListReceivedByAddress, ListReceivedByAddressError, + ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockError, ListSinceBlockTransaction, + ListSinceBlockTransactionError, ListTransactions, ListTransactionsItem, + ListTransactionsItemError, ListUnspent, ListUnspentItem, ListUnspentItemError, ListWallets, + Locked, PeerInfo, RawTransactionError, RawTransactionInput, RawTransactionOutput, + RescanBlockchain, SendMany, SendRawTransaction, SendToAddress, SignMessage, + SignRawTransaction, SignRawTransactionError, SoftforkReject, TestMempoolAccept, + TransactionCategory, UploadTarget, VerifyTxOutProof, WalletCreateFundedPsbt, + WalletCreateFundedPsbtError, WalletProcessPsbt, WitnessUtxo, + }, + v18::{ + ActiveCommand, AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, GetRpcInfo, JoinPsbts, UtxoUpdatePsbt, }, - v18::{ActiveCommand, GetRpcInfo}, v19::{ Bip9SoftforkInfo, Bip9SoftforkStatistics, Bip9SoftforkStatus, GetBalances, GetBalancesMine, GetBalancesWatchOnly, GetBlockFilter, GetBlockFilterError, GetBlockchainInfo, @@ -278,4 +281,8 @@ pub use crate::{ SoftforkType, }, v22::{GetTxOut, GetTxOutError, Logging, ScriptPubkey}, + v24::{ + DecodePsbt, DecodePsbtError, GlobalXpub, Proprietary, PsbtInput, PsbtOutput, + TaprootBip32Deriv, TaprootLeaf, TaprootScript, TaprootScriptPathSig, + }, }; diff --git a/types/src/v26/mod.rs b/types/src/v26/mod.rs index ad5b9f51..65fe6229 100644 --- a/types/src/v26/mod.rs +++ b/types/src/v26/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: CC0-1.0 -//! # JSON-RPC types for Bitcoin Core `v0.26` +//! # JSON-RPC types for Bitcoin Core `v26` //! //! These structs are shaped for the JSON data returned by the JSON-RPC API. They use stdlib types //! (or custom types) and where necessary implement an `into_model` function to convert the type to @@ -109,25 +109,25 @@ //! //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| -//! | analyzepsbt | todo | -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | analyzepsbt | done | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | descriptorprocesspsbt | todo | -//! | finalizepsbt | todo | -//! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | -//! | joinpsbts | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | descriptorprocesspsbt | done (untested) | +//! | finalizepsbt | done (untested) | +//! | fundrawtransaction | done | +//! | getrawtransaction | done | +//! | joinpsbts | done (untested) | //! | sendrawtransaction | done | -//! | signrawtransactionwithkey | todo | -//! | submitpackage | todo | -//! | testmempoolaccept | todo | -//! | utxoupdatepsbt | todo | +//! | signrawtransactionwithkey | done | +//! | submitpackage | done (untested) | +//! | testmempoolaccept | done (untested) | +//! | utxoupdatepsbt | done (untested) | //! //! //! @@ -238,22 +238,21 @@ //! | getzmqnotifications | done (untested) | //! //! -//! -//! -//! **Items marked omitted were omitted because:** -//! -//! - Method does not return anything. -//! - Method returns a simple type (e.g. bool or integer). -//! - Method is deprecated. mod blockchain; mod mining; +mod raw_transactions; mod wallet; #[doc(inline)] pub use self::{ blockchain::{GetTxOutSetInfo, GetTxOutSetInfoError}, mining::{GetPrioritisedTransactions, PrioritisedTransaction}, + raw_transactions::{ + DescriptorProcessPsbt, DescriptorProcessPsbtError, SubmitPackage, SubmitPackageError, + SubmitPackageTxResult, SubmitPackageTxResultError, SubmitPackageTxResultFees, + SubmitPackageTxResultFeesError, + }, wallet::{CreateWallet, LoadWallet, UnloadWallet}, }; #[doc(inline)] @@ -261,9 +260,11 @@ pub use crate::{ v17::{ AddMultisigAddress, AddMultisigAddressError, AddedNode, AddedNodeAddress, AddressInformation, Banned, BumpFee, BumpFeeError, ChainTips, ChainTipsError, - ChainTipsStatus, CreateRawTransaction, DumpPrivKey, DumpWallet, FundRawTransaction, - FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, - GetAddressInfoEmbedded, GetAddressInfoError, GetAddressInfoLabel, GetAddressesByLabel, + ChainTipsStatus, CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, + CreateRawTransaction, DecodeRawTransaction, DecodeScript, DecodeScriptError, DumpPrivKey, + DumpWallet, FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError, + Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, GetAddressInfoEmbedded, + GetAddressInfoEmbeddedError, GetAddressInfoError, GetAddressInfoLabel, GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, GetBlockHash, GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, GetBlockHeaderVerboseError, GetBlockStats, GetBlockStatsError, GetBlockTemplate, GetBlockTemplateError, GetBlockVerboseOne, @@ -271,17 +272,26 @@ pub use crate::{ GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, GetMempoolInfo, GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, GetNetworkInfoAddress, GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, GetPeerInfo, - GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetReceivedByAddress, - GetTransaction, GetTransactionDetail, GetTransactionError, GetUnconfirmedBalance, - GetWalletInfo, GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsItem, - ListBanned, ListLabels, ListLockUnspent, ListLockUnspentItem, ListReceivedByAddress, - ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockTransaction, ListTransactions, - ListTransactionsItem, ListUnspent, ListUnspentItem, ListWallets, Locked, PeerInfo, - RescanBlockchain, SendMany, SendRawTransaction, SendToAddress, SignErrorData, SignMessage, - SignRawTransactionWithWallet, SoftforkReject, TransactionCategory, UploadTarget, - VerifyTxOutProof, WalletCreateFundedPsbt, WalletProcessPsbt, + GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetRawTransaction, + GetRawTransactionVerbose, GetRawTransactionVerboseError, GetReceivedByAddress, + GetTransaction, GetTransactionDetail, GetTransactionDetailError, GetTransactionError, + GetUnconfirmedBalance, GetWalletInfo, GetWalletInfoError, GetZmqNotifications, + ListAddressGroupings, ListAddressGroupingsError, ListAddressGroupingsItem, ListBanned, + ListLabels, ListLockUnspent, ListLockUnspentItem, ListLockUnspentItemError, + ListReceivedByAddress, ListReceivedByAddressError, ListReceivedByAddressItem, + ListSinceBlock, ListSinceBlockError, ListSinceBlockTransaction, + ListSinceBlockTransactionError, ListTransactions, ListTransactionsItem, + ListTransactionsItemError, ListUnspent, ListUnspentItem, ListUnspentItemError, ListWallets, + Locked, PeerInfo, RawTransactionError, RawTransactionInput, RawTransactionOutput, + RescanBlockchain, SendMany, SendRawTransaction, SendToAddress, SignMessage, + SignRawTransaction, SignRawTransactionError, SoftforkReject, TestMempoolAccept, + TransactionCategory, UploadTarget, VerifyTxOutProof, WalletCreateFundedPsbt, + WalletCreateFundedPsbtError, WalletProcessPsbt, WitnessUtxo, + }, + v18::{ + ActiveCommand, AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, GetRpcInfo, JoinPsbts, UtxoUpdatePsbt, }, - v18::{ActiveCommand, GetRpcInfo}, v19::{ Bip9SoftforkInfo, Bip9SoftforkStatistics, Bip9SoftforkStatus, GetBalances, GetBalancesMine, GetBalancesWatchOnly, GetBlockFilter, GetBlockFilterError, GetBlockchainInfo, @@ -291,4 +301,8 @@ pub use crate::{ SoftforkType, }, v22::{GetTxOut, GetTxOutError, Logging, ScriptPubkey}, + v24::{ + DecodePsbt, DecodePsbtError, GlobalXpub, Proprietary, PsbtInput, PsbtOutput, + TaprootBip32Deriv, TaprootLeaf, TaprootScript, TaprootScriptPathSig, + }, }; diff --git a/types/src/v26/raw_transactions/error.rs b/types/src/v26/raw_transactions/error.rs new file mode 100644 index 00000000..775e949d --- /dev/null +++ b/types/src/v26/raw_transactions/error.rs @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: CC0-1.0 + +use core::fmt; + +use bitcoin::amount::ParseAmountError; +use bitcoin::consensus::encode; +use bitcoin::hex::HexToArrayError; +use bitcoin::psbt::PsbtParseError; + +use crate::error::write_err; +use crate::NumericError; + +/// Error when converting a `DescriptorProcessPsbt` type into the model type. +#[derive(Debug)] +pub enum DescriptorProcessPsbtError { + /// Conversion of the transaction `psbt` field failed. + Psbt(PsbtParseError), + /// Conversion of the transaction `hex` field failed. + Hex(encode::FromHexError), +} + +impl fmt::Display for DescriptorProcessPsbtError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use DescriptorProcessPsbtError as E; + + match *self { + E::Psbt(ref e) => write_err!(f, "conversion of the `psbt` field failed"; e), + E::Hex(ref e) => write_err!(f, "conversion of the `hex` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DescriptorProcessPsbtError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use DescriptorProcessPsbtError as E; + + match *self { + E::Psbt(ref e) => Some(e), + E::Hex(ref e) => Some(e), + } + } +} + +/// Error when converting a `SubmitPackage` type into the model type. +#[derive(Debug)] +pub enum SubmitPackageError { + /// Conversion of key from `tx_results` map failed. + TxResultKey(HexToArrayError), + /// Conversion of value from `tx_results` map failed. + TxResultValue(SubmitPackageTxResultError), + /// Conversion of a list item from `replaced_transactions` field failed. + ReplaceTransactions(HexToArrayError), +} + +impl fmt::Display for SubmitPackageError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use SubmitPackageError as E; + + match *self { + E::TxResultKey(ref e) => + write_err!(f, "conversion of key from `tx_results` map failed"; e), + E::TxResultValue(ref e) => + write_err!(f, "conversion of value from `tx_results` map failed"; e), + E::ReplaceTransactions(ref e) => + write_err!(f, "conversion of a list item from `replaced_transactions` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SubmitPackageError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use SubmitPackageError as E; + + match *self { + E::TxResultKey(ref e) => Some(e), + E::TxResultValue(ref e) => Some(e), + E::ReplaceTransactions(ref e) => Some(e), + } + } +} + +/// Error when converting a `SubmitPackageTxResult` type into the model type. +#[derive(Debug)] +pub enum SubmitPackageTxResultError { + /// Conversion of numeric type to expected type failed. + Numeric(NumericError), + /// Conversion of the `txid` field failed. + Txid(HexToArrayError), + /// Conversion of the `other_wtxid` field failed. + OtherWtxid(HexToArrayError), + /// Conversion of the `fees` field failed. + Fees(SubmitPackageTxResultFeesError), +} + +impl fmt::Display for SubmitPackageTxResultError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use SubmitPackageTxResultError as E; + + match *self { + E::Numeric(ref e) => write_err!(f, "numeric"; e), + E::Txid(ref e) => write_err!(f, "conversion of the `txid` field failed"; e), + E::OtherWtxid(ref e) => + write_err!(f, "conversion of the `other_wtxid` field failed"; e), + E::Fees(ref e) => write_err!(f, "conversion of the `fees` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SubmitPackageTxResultError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use SubmitPackageTxResultError as E; + + match *self { + E::Numeric(ref e) => Some(e), + E::Txid(ref e) => Some(e), + E::OtherWtxid(ref e) => Some(e), + E::Fees(ref e) => Some(e), + } + } +} + +impl From for SubmitPackageTxResultError { + fn from(e: NumericError) -> Self { Self::Numeric(e) } +} + +/// Error when converting a `SubmitPackageTxResultFees` type into the model type. +#[derive(Debug)] +pub enum SubmitPackageTxResultFeesError { + /// Conversion of the `base_fee` field failed. + BaseFee(ParseAmountError), + /// Conversion of the `effective_fee_rate` field failed. + EffectiveFeeRate(ParseAmountError), + /// Conversion of a list item from `effective_includes` field failed. + EffectiveIncludes(HexToArrayError), +} + +impl fmt::Display for SubmitPackageTxResultFeesError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use SubmitPackageTxResultFeesError as E; + + match *self { + E::BaseFee(ref e) => write_err!(f, "conversion of the `base_fee` field failed"; e), + E::EffectiveFeeRate(ref e) => + write_err!(f, "conversion of the `effective_fee_rate` field failed"; e), + E::EffectiveIncludes(ref e) => write_err!(f, "effective_includes"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SubmitPackageTxResultFeesError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use SubmitPackageTxResultFeesError as E; + + match *self { + E::BaseFee(ref e) => Some(e), + E::EffectiveFeeRate(ref e) => Some(e), + E::EffectiveIncludes(ref e) => Some(e), + } + } +} diff --git a/types/src/v26/raw_transactions/into.rs b/types/src/v26/raw_transactions/into.rs new file mode 100644 index 00000000..71c0b156 --- /dev/null +++ b/types/src/v26/raw_transactions/into.rs @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: CC0-1.0 + +use bitcoin::{consensus, Amount, Psbt, Txid, Wtxid}; + +// TODO: Use explicit imports? +use super::*; + +impl DescriptorProcessPsbt { + /// Converts version specific type to a version in-specific, more strongly typed type. + pub fn into_model(self) -> Result { + use DescriptorProcessPsbtError as E; + + let psbt = self.psbt.parse::().map_err(E::Psbt)?; + let tx = match self.hex { + Some(hex) => Some(consensus::encode::deserialize_hex(&hex).map_err(E::Hex)?), + None => None, + }; + + Ok(model::DescriptorProcessPsbt { psbt, complete: self.complete, tx }) + } +} + +impl SubmitPackage { + /// Converts version specific type to a version in-specific, more strongly typed type. + pub fn into_model(self) -> Result { + use SubmitPackageError as E; + + let mut tx_results = BTreeMap::new(); + for (k, v) in self.tx_results { + let wtxid = k.parse::().map_err(E::TxResultKey)?; + let result = v.into_model().map_err(E::TxResultValue)?; + tx_results.insert(wtxid, result); + } + + let replaced_transactions = self + .replaced_transactions + .iter() + .map(|tx| tx.parse::().map_err(E::ReplaceTransactions)) + .collect::, _>>()?; + + Ok(model::SubmitPackage { + package_msg: self.package_msg, + tx_results, + replaced_transactions, + }) + } +} + +impl SubmitPackageTxResult { + /// Converts version specific type to a version in-specific, more strongly typed type. + pub fn into_model(self) -> Result { + use SubmitPackageTxResultError as E; + + let txid = self.txid.parse::().map_err(E::Txid)?; + let other_wtxid = + self.other_wtxid.map(|s| s.parse::().map_err(E::OtherWtxid)).transpose()?; + let vsize = self.vsize.map(|vsize| crate::to_u32(vsize, "vsize")).transpose()?; + let fees = self.fees.map(|fees| fees.into_model().map_err(E::Fees)).transpose()?; + + Ok(model::SubmitPackageTxResult { txid, other_wtxid, vsize, fees, error: self.error }) + } +} + +impl SubmitPackageTxResultFees { + /// Converts version specific type to a version in-specific, more strongly typed type. + pub fn into_model( + self, + ) -> Result { + use SubmitPackageTxResultFeesError as E; + + let base_fee = Amount::from_btc(self.base_fee).map_err(E::BaseFee)?; + let effective_fee_rate = self + .effective_fee_rate + .map(|f| crate::btc_per_kb(f).map_err(E::EffectiveFeeRate)) + .transpose()? + .flatten(); + let effective_includes = self + .effective_includes + .iter() + .map(|s| s.parse::().map_err(E::EffectiveIncludes)) + .collect::, _>>()?; + + Ok(model::SubmitPackageTxResultFees { base_fee, effective_fee_rate, effective_includes }) + } +} diff --git a/types/src/v26/raw_transactions/mod.rs b/types/src/v26/raw_transactions/mod.rs new file mode 100644 index 00000000..b3e60df1 --- /dev/null +++ b/types/src/v26/raw_transactions/mod.rs @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! The JSON-RPC API for Bitcoin Core `v26` - raw transactions. +//! +//! Types for methods found under the `== Rawtransactions ==` section of the API docs. + +mod error; +mod into; + +use alloc::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; + +// TODO: Remove wildcard, use explicit types. +pub use self::error::*; +use crate::model; + +/// Result of JSON-RPC method `descriptorprocesspsbt`. +/// +/// > descriptorprocesspsbt "psbt" ["",{"desc":"str","range":n or [n, n]},...] ( "sighashtype" bip32derivs finalize ) +/// > +/// > Update all segwit inputs in a PSBT with information from output descriptors, the UTXO set or the mempool. +/// > Then, sign the inputs we are able to with information from the output descriptors. +/// > +/// > Arguments: +/// > 1. psbt (string, required) The transaction base64 string +/// > 2. descriptors (json array, required) An array of either strings or objects +/// > [ +/// > "", (string) An output descriptor +/// > { (json object) An object with an output descriptor and extra information +/// > "desc": "str", (string, required) An output descriptor +/// > "range": n or [n, n], (numeric or array, optional, default=1000) Up to what index HD chains should be explored (either end or [begin, end]) +/// > }, +/// > ... +/// > ] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct DescriptorProcessPsbt { + /// The base64-encoded partially signed transaction. + pub psbt: String, + /// If the transaction has a complete set of signatures. + pub complete: bool, + /// The hex-encoded network transaction if complete. + pub hex: Option, +} + +/// Result of JSON-RPC method `submitpackage`. +/// +/// > submitpackage ["rawtx",...] ( maxfeerate maxburnamount ) +/// > +/// > Submit a package of raw transactions (serialized, hex-encoded) to local node. +/// > The package will be validated according to consensus and mempool policy rules. If any transaction passes, it will be accepted to mempool. +/// > This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies. +/// > Warning: successful submission does not mean the transactions will propagate throughout the network. +/// > +/// > Arguments: +/// > 1. package (json array, required) An array of raw transactions. +/// > The package must solely consist of a child and its parents. None of the parents may depend on each other. +/// > The package must be topologically sorted, with the child being the last element in the array. +/// > [ +/// > "rawtx", (string) +/// > ... +/// > ] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct SubmitPackage { + /// The transaction package result message. + /// + /// "success" indicates all transactions were accepted into or are already in the mempool. + pub package_msg: String, + /// Transaction results keyed by wtxid. + #[serde(rename = "tx-results")] + pub tx_results: BTreeMap, + /// List of txids of replaced transactions. + #[serde(rename = "replaced-transactions")] + pub replaced_transactions: Vec, +} + +/// Models the per-transaction result included in the JSON-RPC method `submitpackage`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct SubmitPackageTxResult { + /// The transaction id. + pub txid: String, + /// The wtxid of a different transaction with the same txid but different witness found in the mempool. + /// + /// If set, this means the submitted transaction was ignored. + #[serde(rename = "other-wtxid")] + pub other_wtxid: Option, + /// Sigops-adjusted virtual transaction size. + pub vsize: Option, + /// Transaction fees. + pub fees: Option, + /// The transaction error string, if it was rejected by the mempool + pub error: Option, +} + +/// Models the fees included in the per-transaction result of the JSON-RPC method `submitpackage`. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct SubmitPackageTxResultFees { + /// Transaction fee. + #[serde(rename = "base")] + pub base_fee: f64, + /// The effective feerate. + /// + /// Will be `None` if the transaction was already in the mempool. For example, the package + /// feerate and/or feerate with modified fees from the `prioritisetransaction` JSON-RPC method. + #[serde(rename = "effective-feerate")] + pub effective_fee_rate: Option, + /// If [`Self::effective_fee_rate`] is provided, this holds the wtxid's of the transactions + /// whose fees and vsizes are included in effective-feerate. + #[serde(rename = "effective-includes")] + pub effective_includes: Vec, +} diff --git a/types/src/v27/mod.rs b/types/src/v27/mod.rs index 0a5d56cd..0e1105c8 100644 --- a/types/src/v27/mod.rs +++ b/types/src/v27/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: CC0-1.0 -//! # JSON-RPC types for Bitcoin Core `v0.26` +//! # JSON-RPC types for Bitcoin Core `v27` //! //! These structs are shaped for the JSON data returned by the JSON-RPC API. They use stdlib types //! (or custom types) and where necessary implement an `into_model` function to convert the type to @@ -109,25 +109,25 @@ //! //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| -//! | analyzepsbt | todo | -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | analyzepsbt | done | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | descriptorprocesspsbt | todo | -//! | finalizepsbt | todo | -//! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | -//! | joinpsbts | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | descriptorprocesspsbt | done (untested) | +//! | finalizepsbt | done (untested) | +//! | fundrawtransaction | done | +//! | getrawtransaction | done | +//! | joinpsbts | done (untested) | //! | sendrawtransaction | done | -//! | signrawtransactionwithkey | todo | -//! | submitpackage | todo | -//! | testmempoolaccept | todo | -//! | utxoupdatepsbt | todo | +//! | signrawtransactionwithkey | done | +//! | submitpackage | done (untested) | +//! | testmempoolaccept | done (untested) | +//! | utxoupdatepsbt | done (untested) | //! //! //! @@ -238,22 +238,17 @@ //! | getzmqnotifications | done (untested) | //! //! -//! -//! -//! **Items marked omitted were omitted because:** -//! -//! - Method does not return anything. -//! - Method returns a simple type (e.g. bool or integer). -//! - Method is deprecated. #[doc(inline)] pub use crate::{ v17::{ AddMultisigAddress, AddMultisigAddressError, AddedNode, AddedNodeAddress, AddressInformation, Banned, BumpFee, BumpFeeError, ChainTips, ChainTipsError, - ChainTipsStatus, CreateRawTransaction, DumpPrivKey, DumpWallet, FundRawTransaction, - FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, - GetAddressInfoEmbedded, GetAddressInfoError, GetAddressInfoLabel, GetAddressesByLabel, + ChainTipsStatus, CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, + CreateRawTransaction, DecodeRawTransaction, DecodeScript, DecodeScriptError, DumpPrivKey, + DumpWallet, FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError, + Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, GetAddressInfoEmbedded, + GetAddressInfoEmbeddedError, GetAddressInfoError, GetAddressInfoLabel, GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, GetBlockHash, GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, GetBlockHeaderVerboseError, GetBlockStats, GetBlockStatsError, GetBlockTemplate, GetBlockTemplateError, GetBlockVerboseOne, @@ -261,17 +256,26 @@ pub use crate::{ GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, GetMempoolInfo, GetMempoolInfoError, GetMiningInfo, GetNetTotals, GetNetworkInfo, GetNetworkInfoAddress, GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, GetPeerInfo, - GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetReceivedByAddress, - GetTransaction, GetTransactionDetail, GetTransactionError, GetUnconfirmedBalance, - GetWalletInfo, GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsItem, - ListBanned, ListLabels, ListLockUnspent, ListLockUnspentItem, ListReceivedByAddress, - ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockTransaction, ListTransactions, - ListTransactionsItem, ListUnspent, ListUnspentItem, ListWallets, Locked, PeerInfo, - RescanBlockchain, SendMany, SendRawTransaction, SendToAddress, SignErrorData, SignMessage, - SignRawTransactionWithWallet, SoftforkReject, TransactionCategory, UploadTarget, - VerifyTxOutProof, WalletCreateFundedPsbt, WalletProcessPsbt, + GetRawChangeAddress, GetRawMempool, GetRawMempoolVerbose, GetRawTransaction, + GetRawTransactionVerbose, GetRawTransactionVerboseError, GetReceivedByAddress, + GetTransaction, GetTransactionDetail, GetTransactionDetailError, GetTransactionError, + GetUnconfirmedBalance, GetWalletInfo, GetWalletInfoError, GetZmqNotifications, + ListAddressGroupings, ListAddressGroupingsError, ListAddressGroupingsItem, ListBanned, + ListLabels, ListLockUnspent, ListLockUnspentItem, ListLockUnspentItemError, + ListReceivedByAddress, ListReceivedByAddressError, ListReceivedByAddressItem, + ListSinceBlock, ListSinceBlockError, ListSinceBlockTransaction, + ListSinceBlockTransactionError, ListTransactions, ListTransactionsItem, + ListTransactionsItemError, ListUnspent, ListUnspentItem, ListUnspentItemError, ListWallets, + Locked, PeerInfo, RawTransactionError, RawTransactionInput, RawTransactionOutput, + RescanBlockchain, SendMany, SendRawTransaction, SendToAddress, SignMessage, + SignRawTransaction, SignRawTransactionError, SoftforkReject, TestMempoolAccept, + TransactionCategory, UploadTarget, VerifyTxOutProof, WalletCreateFundedPsbt, + WalletCreateFundedPsbtError, WalletProcessPsbt, WitnessUtxo, + }, + v18::{ + ActiveCommand, AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, GetRpcInfo, JoinPsbts, UtxoUpdatePsbt, }, - v18::{ActiveCommand, GetRpcInfo}, v19::{ Bip9SoftforkInfo, Bip9SoftforkStatistics, Bip9SoftforkStatus, GetBalances, GetBalancesMine, GetBalancesWatchOnly, GetBlockFilter, GetBlockFilterError, GetBlockchainInfo, @@ -281,8 +285,15 @@ pub use crate::{ SoftforkType, }, v22::{GetTxOut, GetTxOutError, Logging, ScriptPubkey}, + v24::{ + DecodePsbt, DecodePsbtError, GlobalXpub, Proprietary, PsbtInput, PsbtOutput, + TaprootBip32Deriv, TaprootLeaf, TaprootScript, TaprootScriptPathSig, + }, v26::{ - CreateWallet, GetPrioritisedTransactions, GetTxOutSetInfo, GetTxOutSetInfoError, - LoadWallet, PrioritisedTransaction, UnloadWallet, + CreateWallet, DescriptorProcessPsbt, DescriptorProcessPsbtError, + GetPrioritisedTransactions, GetTxOutSetInfo, GetTxOutSetInfoError, LoadWallet, + PrioritisedTransaction, SubmitPackage, SubmitPackageError, SubmitPackageTxResult, + SubmitPackageTxResultError, SubmitPackageTxResultFees, SubmitPackageTxResultFeesError, + UnloadWallet, }, }; diff --git a/types/src/v28/mod.rs b/types/src/v28/mod.rs index b49ff179..4fbd4817 100644 --- a/types/src/v28/mod.rs +++ b/types/src/v28/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: CC0-1.0 -//! # JSON-RPC types for Bitcoin Core `v0.26` +//! # JSON-RPC types for Bitcoin Core `v28` //! //! These structs are shaped for the JSON data returned by the JSON-RPC API. They use stdlib types //! (or custom types) and where necessary implement an `into_model` function to convert the type to @@ -109,25 +109,25 @@ //! //! | JSON-PRC Method Name | Status | //! |:-----------------------------------|:---------------:| -//! | analyzepsbt | todo | -//! | combinepsbt | todo | -//! | combinerawtransaction | todo | -//! | converttopsbt | todo | -//! | createpsbt | todo | +//! | analyzepsbt | done | +//! | combinepsbt | done | +//! | combinerawtransaction | done | +//! | converttopsbt | done | +//! | createpsbt | done | //! | createrawtransaction | done | -//! | decodepsbt | todo | -//! | decoderawtransaction | todo | -//! | decodescript | todo | -//! | descriptorprocesspsbt | todo | -//! | finalizepsbt | todo | -//! | fundrawtransaction | done (untested) | -//! | getrawtransaction | todo | -//! | joinpsbts | todo | +//! | decodepsbt | done | +//! | decoderawtransaction | done | +//! | decodescript | done | +//! | descriptorprocesspsbt | done (untested) | +//! | finalizepsbt | done (untested) | +//! | fundrawtransaction | done | +//! | getrawtransaction | done | +//! | joinpsbts | done (untested) | //! | sendrawtransaction | done | -//! | signrawtransactionwithkey | todo | -//! | submitpackage | done | -//! | testmempoolaccept | todo | -//! | utxoupdatepsbt | todo | +//! | signrawtransactionwithkey | done | +//! | submitpackage | done (untested) | +//! | testmempoolaccept | done (untested) | +//! | utxoupdatepsbt | done (untested) | //! //! //! @@ -240,24 +240,11 @@ //! | getzmqnotifications | done (untested) | //! //! -//! -//! -//! **Items marked omitted were omitted because:** -//! -//! - Method does not return anything. -//! - Method returns a simple type (e.g. bool or integer). -//! - Method is deprecated. mod blockchain; mod mining; mod network; -mod raw_transactions; -#[doc(inline)] -pub use self::raw_transactions::{ - SubmitPackage, SubmitPackageError, SubmitPackageTxResult, SubmitPackageTxResultError, - SubmitPackageTxResultFees, SubmitPackageTxResultFeesError, -}; #[doc(inline)] pub use self::{blockchain::GetBlockchainInfo, mining::GetMiningInfo, network::GetNetworkInfo}; #[doc(inline)] @@ -265,9 +252,11 @@ pub use crate::{ v17::{ AddMultisigAddress, AddMultisigAddressError, AddedNode, AddedNodeAddress, AddressInformation, Banned, BumpFee, BumpFeeError, ChainTips, ChainTipsError, - ChainTipsStatus, CreateRawTransaction, DumpPrivKey, DumpWallet, FundRawTransaction, - FundRawTransactionError, Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, - GetAddressInfoEmbedded, GetAddressInfoError, GetAddressInfoLabel, GetAddressesByLabel, + ChainTipsStatus, CombinePsbt, CombineRawTransaction, ConvertToPsbt, CreatePsbt, + CreateRawTransaction, DecodeRawTransaction, DecodeScript, DecodeScriptError, DumpPrivKey, + DumpWallet, FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError, + Generate, GenerateToAddress, GetAddedNodeInfo, GetAddressInfo, GetAddressInfoEmbedded, + GetAddressInfoEmbeddedError, GetAddressInfoError, GetAddressInfoLabel, GetAddressesByLabel, GetBalance, GetBestBlockHash, GetBlockCount, GetBlockHash, GetBlockHeader, GetBlockHeaderError, GetBlockHeaderVerbose, GetBlockHeaderVerboseError, GetBlockStats, GetBlockStatsError, GetBlockTemplate, GetBlockTemplateError, GetBlockVerboseOne, @@ -275,17 +264,25 @@ pub use crate::{ GetChainTxStatsError, GetDifficulty, GetMemoryInfoStats, GetMempoolInfo, GetMempoolInfoError, GetNetTotals, GetNetworkInfoAddress, GetNetworkInfoError, GetNetworkInfoNetwork, GetNewAddress, GetPeerInfo, GetRawChangeAddress, GetRawMempool, - GetRawMempoolVerbose, GetReceivedByAddress, GetTransaction, GetTransactionDetail, - GetTransactionError, GetUnconfirmedBalance, GetWalletInfo, GetZmqNotifications, - ListAddressGroupings, ListAddressGroupingsItem, ListBanned, ListLabels, ListLockUnspent, - ListLockUnspentItem, ListReceivedByAddress, ListReceivedByAddressItem, ListSinceBlock, - ListSinceBlockTransaction, ListTransactions, ListTransactionsItem, ListUnspent, - ListUnspentItem, ListWallets, Locked, PeerInfo, RescanBlockchain, SendMany, - SendRawTransaction, SendToAddress, SignErrorData, SignMessage, - SignRawTransactionWithWallet, SoftforkReject, TransactionCategory, UploadTarget, - VerifyTxOutProof, WalletCreateFundedPsbt, WalletProcessPsbt, + GetRawMempoolVerbose, GetRawTransaction, GetRawTransactionVerbose, + GetRawTransactionVerboseError, GetReceivedByAddress, GetTransaction, GetTransactionDetail, + GetTransactionDetailError, GetTransactionError, GetUnconfirmedBalance, GetWalletInfo, + GetWalletInfoError, GetZmqNotifications, ListAddressGroupings, ListAddressGroupingsError, + ListAddressGroupingsItem, ListBanned, ListLabels, ListLockUnspent, ListLockUnspentItem, + ListLockUnspentItemError, ListReceivedByAddress, ListReceivedByAddressError, + ListReceivedByAddressItem, ListSinceBlock, ListSinceBlockError, ListSinceBlockTransaction, + ListSinceBlockTransactionError, ListTransactions, ListTransactionsItem, + ListTransactionsItemError, ListUnspent, ListUnspentItem, ListUnspentItemError, ListWallets, + Locked, PeerInfo, RawTransactionError, RawTransactionInput, RawTransactionOutput, + RescanBlockchain, SendMany, SendRawTransaction, SendToAddress, SignMessage, + SignRawTransaction, SignRawTransactionError, SoftforkReject, TestMempoolAccept, + TransactionCategory, UploadTarget, VerifyTxOutProof, WalletCreateFundedPsbt, + WalletCreateFundedPsbtError, WalletProcessPsbt, WitnessUtxo, + }, + v18::{ + ActiveCommand, AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing, + AnalyzePsbtInputMissingError, GetRpcInfo, JoinPsbts, UtxoUpdatePsbt, }, - v18::{ActiveCommand, GetRpcInfo}, v19::{ Bip9SoftforkInfo, Bip9SoftforkStatistics, Bip9SoftforkStatus, GetBalances, GetBalancesMine, GetBalancesWatchOnly, GetBlockFilter, GetBlockFilterError, GetBlockchainInfoError, @@ -294,8 +291,15 @@ pub use crate::{ MempoolEntryError, MempoolEntryFees, MempoolEntryFeesError, Softfork, SoftforkType, }, v22::{GetTxOut, GetTxOutError, Logging, ScriptPubkey}, + v24::{ + DecodePsbt, DecodePsbtError, GlobalXpub, Proprietary, PsbtInput, PsbtOutput, + TaprootBip32Deriv, TaprootLeaf, TaprootScript, TaprootScriptPathSig, + }, v26::{ - CreateWallet, GetPrioritisedTransactions, GetTxOutSetInfo, GetTxOutSetInfoError, - LoadWallet, PrioritisedTransaction, UnloadWallet, + CreateWallet, DescriptorProcessPsbt, DescriptorProcessPsbtError, + GetPrioritisedTransactions, GetTxOutSetInfo, GetTxOutSetInfoError, LoadWallet, + PrioritisedTransaction, SubmitPackage, SubmitPackageError, SubmitPackageTxResult, + SubmitPackageTxResultError, SubmitPackageTxResultFees, SubmitPackageTxResultFeesError, + UnloadWallet, }, }; diff --git a/verify/src/method/v17.rs b/verify/src/method/v17.rs index 303ae2f2..39ff689a 100644 --- a/verify/src/method/v17.rs +++ b/verify/src/method/v17.rs @@ -47,7 +47,7 @@ pub const METHODS: &[Method] = &[ // mining Method::new_modelled("getblocktemplate", "GetBlockTemplate", "get_block_template"), Method::new_no_model("getmininginfo", "GetMiningInfo", "get_mining_info"), - Method::new_numeric("getnetworkhashps", "get_network_hashes_per_second"), + Method::new_numeric("getnetworkhashps", "get_network_hash_ps"), Method::new_bool("prioritisetransaction", "prioritise_transaction"), Method::new_nothing("submitblock", "submit_block"), // network @@ -64,21 +64,21 @@ pub const METHODS: &[Method] = &[ Method::new_nothing("setban", "set_ban"), Method::new_nothing("setnetworkactive", "set_network_active"), // raw transactions - Method::new_nothing("combinepsbt", "combine_psbt"), - Method::new_nothing("combinerawtransaction", "combine_raw_transaction"), - Method::new_nothing("converttopsbt", "convert_to_psbt"), - Method::new_nothing("createpsbt", "create_psbt"), - Method::new_nothing("createrawtransaction", "create_raw_transaction"), - Method::new_nothing("decodepsbt", "decode_psbt"), - Method::new_nothing("decoderawtransaction", "decode_raw_transaction"), - Method::new_nothing("decodescript", "decode_script"), - Method::new_nothing("finalizepsbt", "finalize_psbt"), - Method::new_nothing("fundrawtransaction", "fund_raw_transaciton"), - Method::new_nothing("getrawtransaction", "get_raw_transaction"), + Method::new_modelled("combinepsbt", "CombinePsbt", "combine_psbt"), + Method::new_modelled("combinerawtransaction", "CombineRawTransaction", "combine_raw_transaction"), + Method::new_modelled("converttopsbt", "ConvertToPsbt", "convert_to_psbt"), + Method::new_modelled("createpsbt", "CreatePsbt", "create_psbt"), + Method::new_modelled("createrawtransaction", "CreateRawTransaction", "create_raw_transaction"), + Method::new_modelled("decodepsbt", "DecodePsbt", "decode_psbt"), + Method::new_modelled("decoderawtransaction", "DecodeRawTransaction", "decode_raw_transaction"), + Method::new_modelled("decodescript", "DecodeScript", "decode_script"), + Method::new_modelled("finalizepsbt", "FinalizePsbt", "finalize_psbt"), + Method::new_modelled("fundrawtransaction", "FundRawTransaction", "fund_raw_transaction"), + Method::new_modelled("getrawtransaction", "GetRawTransaction", "get_raw_transaction"), Method::new_modelled("sendrawtransaction", "SendRawTransaction", "send_raw_transaction"), - Method::new_nothing("signrawtransaction", "sign_raw_transaction"), - Method::new_nothing("signrawtransactionwithkey", "sign_raw_transaction_with_key"), - Method::new_nothing("testmempoolaccept", "test_mempool_accept"), + Method::new_modelled("signrawtransaction", "SignRawTransaction", "sign_raw_transaction"), + Method::new_modelled("signrawtransactionwithkey", "SignRawTransaction", "sign_raw_transaction_with_key"), + Method::new_modelled("testmempoolaccept", "TestMempoolAccept", "test_mempool_accept"), // util Method::new_modelled("createmultisig", "CreateMultisig", "create_multisig"), Method::new_nothing("estimatesmartfee", "estimate_smart_fee"), @@ -147,7 +147,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_nothing("unloadwallet", "unload_wallet"), diff --git a/verify/src/method/v18.rs b/verify/src/method/v18.rs index 0aa49696..fbd4605f 100644 --- a/verify/src/method/v18.rs +++ b/verify/src/method/v18.rs @@ -6,6 +6,7 @@ use super::Method; /// Data for the JSON RPC methods provided by Bitcoin Core v18. pub const METHODS: &[Method] = &[ + // blockchain Method::new_modelled("getbestblockhash", "GetBestBlockHash", "get_best_block_hash"), Method::new_modelled("getblock", "GetBlockVerboseZero", "get_block"), // We only check one of the types. Method::new_modelled("getblockchaininfo", "GetBlockchainInfo", "get_blockchain_info"), @@ -34,20 +35,24 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("scantxoutset", "ScanTxOutSet", "scan_tx_out_set"), Method::new_bool("verifychain", "verify_chain"), Method::new_modelled("verifytxoutproof", "VerifyTxOutProof", "verify_tx_out_proof"), - Method::new_no_model("getrpcinfo", "GetRpcInfo", "get_rpc_info"), + // control Method::new_no_model("getmemoryinfo", "GetMemoryInfoStats", "get_memory_info"), + Method::new_no_model("getrpcinfo", "GetRpcInfo", "get_rpc_info"), Method::new_string("help", "help"), Method::new_no_model("logging", "Logging", "logging"), Method::new_nothing("stop", "stop"), Method::new_numeric("uptime", "uptime"), + // generating Method::new_modelled("generate", "Generate", "generate"), Method::new_modelled("generatetoaddress", "GenerateToAddress", "generate_to_address"), + // mining Method::new_modelled("getblocktemplate", "GetBlockTemplate", "get_block_template"), Method::new_no_model("getmininginfo", "GetMiningInfo", "get_mining_info"), Method::new_nothing("getnetworkhashps", "get_network_hashes_per_second"), Method::new_bool("prioritisetransaction", "prioritise_transaction"), Method::new_nothing("submitblock", "submit_block"), Method::new_nothing("submitheader", "submit_header"), + // network Method::new_nothing("addnode", "add_node"), Method::new_nothing("clearbanned", "clear_banned"), Method::new_nothing("disconnectnode", "disconnect_node"), @@ -61,23 +66,25 @@ pub const METHODS: &[Method] = &[ Method::new_nothing("ping", "ping"), Method::new_nothing("setban", "set_ban"), Method::new_nothing("setnetworkactive", "set_network_active"), + // rawtransactions Method::new_modelled("analyzepsbt", "AnalyzePsbt", "analyze_psbt"), - Method::new_nothing("combinepsbt", "combine_psbt"), - Method::new_nothing("combinerawtransaction", "combine_raw_transaction"), - Method::new_nothing("converttopsbt", "convert_to_psbt"), - Method::new_nothing("createpsbt", "create_psbt"), - Method::new_nothing("createrawtransaction", "create_raw_transaction"), - Method::new_nothing("decodepsbt", "decode_psbt"), - Method::new_nothing("decoderawtransaction", "decode_raw_transaction"), - Method::new_nothing("decodescript", "decode_script"), - Method::new_nothing("finalizepsbt", "finalize_psbt"), - Method::new_nothing("fundrawtransaction", "fund_raw_transaciton"), - Method::new_nothing("getrawtransaction", "get_raw_transaction"), + Method::new_modelled("combinepsbt", "CombinePsbt", "combine_psbt"), + Method::new_modelled("combinerawtransaction", "CombineRawTransaction", "combine_raw_transaction"), + Method::new_modelled("converttopsbt", "ConvertToPsbt", "convert_to_psbt"), + Method::new_modelled("createpsbt", "CreatePsbt", "create_psbt"), + Method::new_modelled("createrawtransaction", "CreateRawTransaction", "create_raw_transaction"), + Method::new_modelled("decodepsbt", "DecodePsbt", "decode_psbt"), + Method::new_modelled("decoderawtransaction", "DecodeRawTransaction", "decode_raw_transaction"), + Method::new_modelled("decodescript", "DecodeScript", "decode_script"), + Method::new_modelled("finalizepsbt", "FinalizePsbt", "finalize_psbt"), + Method::new_modelled("fundrawtransaction", "FundRawTransaction", "fund_raw_transaction"), + Method::new_modelled("getrawtransaction", "GetRawTransaction", "get_raw_transaction"), Method::new_modelled("joinpsbts", "JoinPsbts", "join_psbts"), Method::new_modelled("sendrawtransaction", "SendRawTransaction", "send_raw_transaction"), - Method::new_nothing("signrawtransactionwithkey", "sign_raw_transaction_with_key"), - Method::new_nothing("testmempoolaccept", "test_mempool_accept"), + Method::new_modelled("signrawtransactionwithkey", "SignRawTransaction", "sign_raw_transaction_with_key"), + Method::new_modelled("testmempoolaccept", "TestMempoolAccept", "test_mempool_accept"), Method::new_modelled("utxoupdatepsbt", "UtxoUpdatePsbt", "utxo_update_psbt"), + // util Method::new_modelled("createmultisig", "CreateMultisig", "create_multisig"), Method::new_modelled("deriveaddresses", "DeriveAddresses", "derive_addresses"), Method::new_nothing("estimatesmartfee", "estimate_smart_fee"), @@ -85,6 +92,7 @@ pub const METHODS: &[Method] = &[ Method::new_string("signmessagewithprivkey", "sign_message_with_priv_key"), Method::new_modelled("validateaddress", "ValidateAddress", "validate_address"), Method::new_bool("verifymessage", "verify_message"), + // wallet Method::new_nothing("abandontransaction", "abandon_transaction"), Method::new_nothing("abortrescan", "abort_rescan"), Method::new_modelled("addmultisigaddress", "AddMultisigAddress", "add_multisig_address"), @@ -141,7 +149,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_nothing("unloadwallet", "unload_wallet"), @@ -154,5 +162,6 @@ pub const METHODS: &[Method] = &[ Method::new_nothing("walletpassphrase", "wallet_passphrase"), Method::new_nothing("walletpassphrasechange", "wallet_passphrase_change"), Method::new_modelled("walletprocesspsbt", "WalletProcessPsbt", "wallet_process_psbt"), + // zmq Method::new_no_model("getzmqnotifications", "GetZmqNotifications", "get_zmq_notifications"), ]; diff --git a/verify/src/method/v19.rs b/verify/src/method/v19.rs index 6cb27897..875fd046 100644 --- a/verify/src/method/v19.rs +++ b/verify/src/method/v19.rs @@ -143,7 +143,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_nothing("unloadwallet", "unload_wallet"), diff --git a/verify/src/method/v20.rs b/verify/src/method/v20.rs index 0783ead3..6d261daf 100644 --- a/verify/src/method/v20.rs +++ b/verify/src/method/v20.rs @@ -144,7 +144,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_nothing("unloadwallet", "unload_wallet"), diff --git a/verify/src/method/v21.rs b/verify/src/method/v21.rs index e0fd0601..6152992a 100644 --- a/verify/src/method/v21.rs +++ b/verify/src/method/v21.rs @@ -149,7 +149,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_nothing("unloadwallet", "unload_wallet"), diff --git a/verify/src/method/v22.rs b/verify/src/method/v22.rs index 207599a4..dd2d58ed 100644 --- a/verify/src/method/v22.rs +++ b/verify/src/method/v22.rs @@ -151,7 +151,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_nothing("unloadwallet", "unload_wallet"), diff --git a/verify/src/method/v23.rs b/verify/src/method/v23.rs index 0b2d06b4..f1850891 100644 --- a/verify/src/method/v23.rs +++ b/verify/src/method/v23.rs @@ -152,7 +152,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_nothing("unloadwallet", "unload_wallet"), diff --git a/verify/src/method/v24.rs b/verify/src/method/v24.rs index bb16af93..15de1220 100644 --- a/verify/src/method/v24.rs +++ b/verify/src/method/v24.rs @@ -155,7 +155,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_modelled("simulaterawtransaction", "SimulateRawTransaction", "simulate_raw_transaction"), diff --git a/verify/src/method/v25.rs b/verify/src/method/v25.rs index 2c5ea011..c511e7ed 100644 --- a/verify/src/method/v25.rs +++ b/verify/src/method/v25.rs @@ -156,7 +156,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_modelled("simulaterawtransaction", "SimulateRawTransaction", "simulate_raw_transaction"), diff --git a/verify/src/method/v26.rs b/verify/src/method/v26.rs index db26e8c8..642b2f48 100644 --- a/verify/src/method/v26.rs +++ b/verify/src/method/v26.rs @@ -164,7 +164,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_modelled("simulaterawtransaction", "SimulateRawTransaction", "simulate_raw_transaction"), diff --git a/verify/src/method/v27.rs b/verify/src/method/v27.rs index 2dfb3cb3..7c51589d 100644 --- a/verify/src/method/v27.rs +++ b/verify/src/method/v27.rs @@ -166,7 +166,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_modelled("simulaterawtransaction", "SimulateRawTransaction", "simulate_raw_transaction"), diff --git a/verify/src/method/v28.rs b/verify/src/method/v28.rs index e757f65a..e95b1e54 100644 --- a/verify/src/method/v28.rs +++ b/verify/src/method/v28.rs @@ -168,7 +168,7 @@ pub const METHODS: &[Method] = &[ Method::new_modelled("signmessage", "SignMessage", "sign_message"), Method::new_modelled( "signrawtransactionwithwallet", - "SignRawTransactionWithWallet", + "SignRawTransaction", "sign_raw_transaction_with_wallet", ), Method::new_modelled("simulaterawtransaction", "SimulateRawTransaction", "simulate_raw_transaction"),