From 029fbf52047b7adae50b8c3faa1ded581e9612e0 Mon Sep 17 00:00:00 2001 From: "satsfy (Renato Britto)" Date: Wed, 17 Jun 2026 13:41:35 -0300 Subject: [PATCH 1/4] test: simplify control logging test The field access did not assert anything. Just check that the call deserializes into a Logging value, like the other tests in the file. --- integration_test/tests/control.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/integration_test/tests/control.rs b/integration_test/tests/control.rs index 8c569787..6a066693 100644 --- a/integration_test/tests/control.rs +++ b/integration_test/tests/control.rs @@ -29,10 +29,7 @@ fn control__help() { #[test] fn control__logging() { let node = BitcoinD::with_wallet(Wallet::None, &[]); - let logging: Logging = node.client.logging().unwrap(); - #[cfg(not(feature = "v30_and_below"))] - let _ = (logging.kernel, logging.privatebroadcast); - let _ = logging; + let _: Logging = node.client.logging().unwrap(); } #[test] From a68aedd325cd0ab048cb87ec8bd48123a0059455 Mon Sep 17 00:00:00 2001 From: "satsfy (Renato Britto)" Date: Wed, 17 Jun 2026 13:41:35 -0300 Subject: [PATCH 2/4] types: add a v31 getwalletinfo error type v31 getwalletinfo no longer returns paytxfee, so reusing the v30 error left an unused PayTxFee variant. Give v31 its own GetWalletInfoError. Reuses scanning and last processed block types from v30. --- types/src/v31/mod.rs | 14 ++++++------ types/src/v31/wallet/error.rs | 40 +++++++++++++++++++++++++++++++++++ types/src/v31/wallet/mod.rs | 4 +++- 3 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 types/src/v31/wallet/error.rs diff --git a/types/src/v31/mod.rs b/types/src/v31/mod.rs index b6f12f8f..847e2f23 100644 --- a/types/src/v31/mod.rs +++ b/types/src/v31/mod.rs @@ -267,7 +267,7 @@ pub use self::{ AbortPrivateBroadcast, GetPrivateBroadcastInfo, PrivateBroadcastPeer, PrivateBroadcastTransaction, RemovedTransaction, }, - wallet::GetWalletInfo, + wallet::{GetWalletInfo, GetWalletInfoError}, }; #[doc(inline)] pub use crate::{ @@ -366,11 +366,11 @@ pub use crate::{ v30::{ ControlBlocksError, DecodePsbt, DecodePsbtError, GetMiningInfo, GetMiningInfoError, GetOrphanTxs, GetOrphanTxsVerboseOne, GetOrphanTxsVerboseOneEntry, GetOrphanTxsVerboseTwo, - GetOrphanTxsVerboseTwoEntry, GetWalletInfoError, GetWalletInfoScanning, GlobalXpub, - GlobalXpubError, LastProcessedBlock, LastProcessedBlockError, ListWalletDir, - ListWalletDirWallet, Musig2PartialSig, Musig2ParticipantPubKeys, Musig2Pubnonce, - Proprietary, PsbtInput, PsbtInputError, PsbtOutput, PsbtOutputError, TaprootBip32Deriv, - TaprootBip32DerivsError, TaprootLeaf, TaprootLeafError, TaprootScript, TaprootScriptError, - TaprootScriptPathSig, TaprootScriptPathSigError, + GetOrphanTxsVerboseTwoEntry, GetWalletInfoScanning, GlobalXpub, GlobalXpubError, + LastProcessedBlock, LastProcessedBlockError, ListWalletDir, ListWalletDirWallet, + Musig2PartialSig, Musig2ParticipantPubKeys, Musig2Pubnonce, Proprietary, PsbtInput, + PsbtInputError, PsbtOutput, PsbtOutputError, TaprootBip32Deriv, TaprootBip32DerivsError, + TaprootLeaf, TaprootLeafError, TaprootScript, TaprootScriptError, TaprootScriptPathSig, + TaprootScriptPathSigError, }, }; diff --git a/types/src/v31/wallet/error.rs b/types/src/v31/wallet/error.rs new file mode 100644 index 00000000..ebf824a3 --- /dev/null +++ b/types/src/v31/wallet/error.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: CC0-1.0 + +use core::fmt; + +use crate::error::write_err; +use crate::v30::LastProcessedBlockError; +use crate::NumericError; + +/// Error when converting a `GetWalletInfo` type into the model type. +#[derive(Debug)] +pub enum GetWalletInfoError { + /// Conversion of numeric type to expected type failed. + Numeric(NumericError), + /// Conversion of the `last_processed_block` field failed. + LastProcessedBlock(LastProcessedBlockError), +} + +impl fmt::Display for GetWalletInfoError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Numeric(ref e) => write_err!(f, "numeric"; e), + Self::LastProcessedBlock(ref e) => + write_err!(f, "conversion of the `last_processed_block` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for GetWalletInfoError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::Numeric(ref e) => Some(e), + Self::LastProcessedBlock(ref e) => Some(e), + } + } +} + +impl From for GetWalletInfoError { + fn from(e: NumericError) -> Self { Self::Numeric(e) } +} diff --git a/types/src/v31/wallet/mod.rs b/types/src/v31/wallet/mod.rs index 2ac62504..eb6300ea 100644 --- a/types/src/v31/wallet/mod.rs +++ b/types/src/v31/wallet/mod.rs @@ -4,11 +4,13 @@ //! //! Types for methods found under the `== Wallet ==` section of the API docs. +mod error; mod into; use serde::{Deserialize, Serialize}; -pub use super::{GetWalletInfoError, GetWalletInfoScanning, LastProcessedBlock}; +pub use self::error::GetWalletInfoError; +pub use crate::v30::{GetWalletInfoScanning, LastProcessedBlock}; /// Result of the JSON-RPC method `getwalletinfo`. /// From 4d998eab637f14e441a23c9112a7c08051c3a79b Mon Sep 17 00:00:00 2001 From: "satsfy (Renato Britto)" Date: Wed, 17 Jun 2026 13:41:35 -0300 Subject: [PATCH 3/4] types: use rust-bitcoin types in the coinbase tx model Model CoinbaseTransaction beyond primitive types so into_model does real conversions. This needs v31 specific GetBlockVerbose error types to carry the new coinbase_tx conversion error. --- types/src/model/blockchain.rs | 37 +++-- types/src/v31/blockchain/error.rs | 243 ++++++++++++++++++++++++++++++ types/src/v31/blockchain/into.rs | 50 +++--- types/src/v31/blockchain/mod.rs | 10 +- types/src/v31/mod.rs | 20 +-- 5 files changed, 304 insertions(+), 56 deletions(-) diff --git a/types/src/model/blockchain.rs b/types/src/model/blockchain.rs index 722cf2c0..bddba9a5 100644 --- a/types/src/model/blockchain.rs +++ b/types/src/model/blockchain.rs @@ -10,8 +10,9 @@ use alloc::collections::BTreeMap; use bitcoin::address::NetworkUnchecked; use bitcoin::hashes::sha256; use bitcoin::{ - block, Address, Amount, Block, BlockHash, CompactTarget, FeeRate, Network, OutPoint, ScriptBuf, - Target, Transaction, TxMerkleNode, TxOut, Txid, Weight, Work, Wtxid, + absolute, block, transaction, Address, Amount, Block, BlockHash, CompactTarget, FeeRate, + Network, OutPoint, ScriptBuf, Sequence, Target, Transaction, TxMerkleNode, TxOut, Txid, Weight, + Witness, Work, Wtxid, }; use serde::{Deserialize, Serialize}; @@ -42,23 +43,6 @@ pub struct GetBestBlockHash(pub BlockHash); #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct GetBlockVerboseZero(pub Block); -/// The coinbase transaction of a block. Part of `getblock` at verbosity 1, 2 and 3. -/// -/// Introduced in Bitcoin Core v31. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct CoinbaseTransaction { - /// The coinbase transaction version. - pub version: i32, - /// The coinbase transaction's locktime. - pub locktime: u32, - /// The coinbase input's sequence number (nSequence). - pub sequence: u32, - /// The coinbase input's script. - pub coinbase: String, - /// The coinbase input's first (and only) witness stack element, if present. - pub witness: Option, -} - /// Models the result of JSON-RPC method `getblock` with verbosity set to 1. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct GetBlockVerboseOne { @@ -203,6 +187,21 @@ pub struct GetBlockVerboseThree { pub next_block_hash: Option, } +/// Coinbase transaction metadata. Part of `getblock`. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct CoinbaseTransaction { + /// The coinbase transaction version. + pub version: transaction::Version, + /// The coinbase transaction's locktime + pub locktime: absolute::LockTime, + /// The coinbase input's sequence number (nSequence). + pub sequence: Sequence, + /// The coinbase input's script. + pub coinbase: ScriptBuf, + /// The coinbase input's first (and only) witness stack element, if present. + pub witness: Option, +} + /// A transaction entry for `getblock` verbosity 3. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct GetBlockVerboseThreeTransaction { diff --git a/types/src/v31/blockchain/error.rs b/types/src/v31/blockchain/error.rs index 4557af9b..a87b2223 100644 --- a/types/src/v31/blockchain/error.rs +++ b/types/src/v31/blockchain/error.rs @@ -4,9 +4,11 @@ use core::fmt; use bitcoin::amount::ParseAmountError; use bitcoin::consensus::encode; +use bitcoin::error::UnprefixedHexError; use bitcoin::hex; use crate::error::write_err; +use crate::v17::GetRawTransactionVerboseError; use crate::NumericError; /// Error when converting a `GetMempoolCluster` type into the model type. @@ -220,3 +222,244 @@ impl std::error::Error for GetTxSpendingPrevoutError { } } } + +/// Error when converting a `CoinbaseTransaction` type into the model type. +#[derive(Debug)] +pub enum CoinbaseTransactionError { + /// Conversion of the `coinbase` field failed. + Coinbase(hex::HexToBytesError), + /// Conversion of the `witness` field failed. + Witness(hex::HexToBytesError), +} + +impl fmt::Display for CoinbaseTransactionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Coinbase(ref e) => write_err!(f, "conversion of the `coinbase` field failed"; e), + Self::Witness(ref e) => write_err!(f, "conversion of the `witness` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for CoinbaseTransactionError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::Coinbase(ref e) => Some(e), + Self::Witness(ref e) => Some(e), + } + } +} + +/// Error when converting a `GetBlockVerboseOne` type into the model type. +#[derive(Debug)] +pub enum GetBlockVerboseOneError { + /// Conversion of numeric type to expected type failed. + Numeric(NumericError), + /// Conversion of the transaction `hash` field failed. + Hash(hex::HexToArrayError), + /// Conversion of the transaction `merkle_root` field failed. + MerkleRoot(hex::HexToArrayError), + /// Conversion of the transaction `bits` field failed. + Bits(UnprefixedHexError), + /// Conversion of the `target` field failed. + Target(UnprefixedHexError), + /// Conversion of the transaction `chain_work` field failed. + ChainWork(UnprefixedHexError), + /// Conversion of the transaction `previous_block_hash` field failed. + PreviousBlockHash(hex::HexToArrayError), + /// Conversion of the transaction `next_block_hash` field failed. + NextBlockHash(hex::HexToArrayError), + /// Conversion of the `coinbase_tx` field failed. + CoinbaseTx(CoinbaseTransactionError), +} + +impl fmt::Display for GetBlockVerboseOneError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Numeric(ref e) => write_err!(f, "numeric"; e), + Self::Hash(ref e) => write_err!(f, "conversion of the `hash` field failed"; e), + Self::MerkleRoot(ref e) => + write_err!(f, "conversion of the `merkle_root` field failed"; e), + Self::Bits(ref e) => write_err!(f, "conversion of the `bits` field failed"; e), + Self::Target(ref e) => write_err!(f, "conversion of the `target` field failed"; e), + Self::ChainWork(ref e) => + write_err!(f, "conversion of the `chain_work` field failed"; e), + Self::PreviousBlockHash(ref e) => + write_err!(f, "conversion of the `previous_block_hash` field failed"; e), + Self::NextBlockHash(ref e) => + write_err!(f, "conversion of the `next_block_hash` field failed"; e), + Self::CoinbaseTx(ref e) => + write_err!(f, "conversion of the `coinbase_tx` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for GetBlockVerboseOneError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::Numeric(ref e) => Some(e), + Self::Hash(ref e) => Some(e), + Self::MerkleRoot(ref e) => Some(e), + Self::Bits(ref e) => Some(e), + Self::Target(ref e) => Some(e), + Self::ChainWork(ref e) => Some(e), + Self::PreviousBlockHash(ref e) => Some(e), + Self::NextBlockHash(ref e) => Some(e), + Self::CoinbaseTx(ref e) => Some(e), + } + } +} + +impl From for GetBlockVerboseOneError { + fn from(e: NumericError) -> Self { Self::Numeric(e) } +} + +/// Error when converting a `GetBlockVerboseTwo` type into the model type. +#[derive(Debug)] +pub enum GetBlockVerboseTwoError { + /// Conversion of numeric type to expected type failed. + Numeric(NumericError), + /// Conversion of the transaction `hash` field failed. + Hash(hex::HexToArrayError), + /// Conversion of the transaction `merkle_root` field failed. + MerkleRoot(hex::HexToArrayError), + /// Conversion of the transaction `bits` field failed. + Bits(UnprefixedHexError), + /// Conversion of the `target` field failed. + Target(UnprefixedHexError), + /// Conversion of the transaction `chain_work` field failed. + ChainWork(UnprefixedHexError), + /// Conversion of the transaction `previous_block_hash` field failed. + PreviousBlockHash(hex::HexToArrayError), + /// Conversion of the transaction `next_block_hash` field failed. + NextBlockHash(hex::HexToArrayError), + /// Conversion of a transaction entry failed. + Transaction(GetRawTransactionVerboseError), + /// Conversion of the transaction `fee` field failed. + Fee(ParseAmountError), + /// Conversion of the `coinbase_tx` field failed. + CoinbaseTx(CoinbaseTransactionError), +} + +impl fmt::Display for GetBlockVerboseTwoError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Numeric(ref e) => write_err!(f, "numeric"; e), + Self::Hash(ref e) => write_err!(f, "conversion of the `hash` field failed"; e), + Self::MerkleRoot(ref e) => + write_err!(f, "conversion of the `merkle_root` field failed"; e), + Self::Bits(ref e) => write_err!(f, "conversion of the `bits` field failed"; e), + Self::Target(ref e) => write_err!(f, "conversion of the `target` field failed"; e), + Self::ChainWork(ref e) => + write_err!(f, "conversion of the `chain_work` field failed"; e), + Self::PreviousBlockHash(ref e) => + write_err!(f, "conversion of the `previous_block_hash` field failed"; e), + Self::NextBlockHash(ref e) => + write_err!(f, "conversion of the `next_block_hash` field failed"; e), + Self::Transaction(ref e) => + write_err!(f, "conversion of a transaction entry failed"; e), + Self::Fee(ref e) => write_err!(f, "conversion of the `fee` field failed"; e), + Self::CoinbaseTx(ref e) => + write_err!(f, "conversion of the `coinbase_tx` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for GetBlockVerboseTwoError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::Numeric(ref e) => Some(e), + Self::Hash(ref e) => Some(e), + Self::MerkleRoot(ref e) => Some(e), + Self::Bits(ref e) => Some(e), + Self::Target(ref e) => Some(e), + Self::ChainWork(ref e) => Some(e), + Self::PreviousBlockHash(ref e) => Some(e), + Self::NextBlockHash(ref e) => Some(e), + Self::Transaction(ref e) => Some(e), + Self::Fee(ref e) => Some(e), + Self::CoinbaseTx(ref e) => Some(e), + } + } +} + +impl From for GetBlockVerboseTwoError { + fn from(e: NumericError) -> Self { Self::Numeric(e) } +} + +/// Error when converting a `GetBlockVerboseThree` type into the model type. +#[derive(Debug)] +pub enum GetBlockVerboseThreeError { + /// Conversion of numeric type to expected type failed. + Numeric(NumericError), + /// Conversion of the transaction `hash` field failed. + Hash(hex::HexToArrayError), + /// Conversion of the transaction `merkle_root` field failed. + MerkleRoot(hex::HexToArrayError), + /// Conversion of the transaction `bits` field failed. + Bits(UnprefixedHexError), + /// Conversion of the `target` field failed. + Target(UnprefixedHexError), + /// Conversion of the transaction `chain_work` field failed. + ChainWork(UnprefixedHexError), + /// Conversion of the transaction `previous_block_hash` field failed. + PreviousBlockHash(hex::HexToArrayError), + /// Conversion of the transaction `next_block_hash` field failed. + NextBlockHash(hex::HexToArrayError), + /// Conversion of a transaction entry failed. + Transaction(crate::v29::GetBlockVerboseThreeError), + /// Conversion of the transaction `fee` field failed. + Fee(ParseAmountError), + /// Conversion of the `coinbase_tx` field failed. + CoinbaseTx(CoinbaseTransactionError), +} + +impl fmt::Display for GetBlockVerboseThreeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Numeric(ref e) => write_err!(f, "numeric"; e), + Self::Hash(ref e) => write_err!(f, "conversion of the `hash` field failed"; e), + Self::MerkleRoot(ref e) => + write_err!(f, "conversion of the `merkle_root` field failed"; e), + Self::Bits(ref e) => write_err!(f, "conversion of the `bits` field failed"; e), + Self::Target(ref e) => write_err!(f, "conversion of the `target` field failed"; e), + Self::ChainWork(ref e) => + write_err!(f, "conversion of the `chain_work` field failed"; e), + Self::PreviousBlockHash(ref e) => + write_err!(f, "conversion of the `previous_block_hash` field failed"; e), + Self::NextBlockHash(ref e) => + write_err!(f, "conversion of the `next_block_hash` field failed"; e), + Self::Transaction(ref e) => + write_err!(f, "conversion of a transaction entry failed"; e), + Self::Fee(ref e) => write_err!(f, "conversion of the `fee` field failed"; e), + Self::CoinbaseTx(ref e) => + write_err!(f, "conversion of the `coinbase_tx` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for GetBlockVerboseThreeError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::Numeric(ref e) => Some(e), + Self::Hash(ref e) => Some(e), + Self::MerkleRoot(ref e) => Some(e), + Self::Bits(ref e) => Some(e), + Self::Target(ref e) => Some(e), + Self::ChainWork(ref e) => Some(e), + Self::PreviousBlockHash(ref e) => Some(e), + Self::NextBlockHash(ref e) => Some(e), + Self::Transaction(ref e) => Some(e), + Self::Fee(ref e) => Some(e), + Self::CoinbaseTx(ref e) => Some(e), + } + } +} + +impl From for GetBlockVerboseThreeError { + fn from(e: NumericError) -> Self { Self::Numeric(e) } +} diff --git a/types/src/v31/blockchain/into.rs b/types/src/v31/blockchain/into.rs index f39b4c20..c0c8077b 100644 --- a/types/src/v31/blockchain/into.rs +++ b/types/src/v31/blockchain/into.rs @@ -4,18 +4,19 @@ use alloc::collections::BTreeMap; use bitcoin::consensus::encode; use bitcoin::{ - block, Amount, BlockHash, CompactTarget, OutPoint, Target, Transaction, TxMerkleNode, Txid, - Weight, Work, Wtxid, + absolute, block, transaction, Amount, BlockHash, CompactTarget, OutPoint, ScriptBuf, Sequence, + Target, Transaction, TxMerkleNode, Txid, Weight, Work, Wtxid, }; use super::{ - CoinbaseTransaction, GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseThree, - GetBlockVerboseThreeError, GetBlockVerboseTwo, GetBlockVerboseTwoError, GetDeploymentInfo, - GetDeploymentInfoError, GetMempoolAncestorsVerbose, GetMempoolCluster, GetMempoolClusterError, - GetMempoolDescendantsVerbose, GetMempoolEntry, GetMempoolFeerateDiagram, - GetMempoolFeerateDiagramError, GetMempoolInfo, GetMempoolInfoError, GetRawMempoolVerbose, - GetTxSpendingPrevout, GetTxSpendingPrevoutError, GetTxSpendingPrevoutItem, - MapMempoolEntryError, MempoolEntry, MempoolEntryError, MempoolEntryFees, MempoolEntryFeesError, + CoinbaseTransaction, CoinbaseTransactionError, GetBlockVerboseOne, GetBlockVerboseOneError, + GetBlockVerboseThree, GetBlockVerboseThreeError, GetBlockVerboseTwo, GetBlockVerboseTwoError, + GetDeploymentInfo, GetDeploymentInfoError, GetMempoolAncestorsVerbose, GetMempoolCluster, + GetMempoolClusterError, GetMempoolDescendantsVerbose, GetMempoolEntry, + GetMempoolFeerateDiagram, GetMempoolFeerateDiagramError, GetMempoolInfo, GetMempoolInfoError, + GetRawMempoolVerbose, GetTxSpendingPrevout, GetTxSpendingPrevoutError, + GetTxSpendingPrevoutItem, MapMempoolEntryError, MempoolEntry, MempoolEntryError, + MempoolEntryFees, MempoolEntryFeesError, }; use crate::model; @@ -269,14 +270,20 @@ impl GetTxSpendingPrevoutItem { impl CoinbaseTransaction { /// Converts version specific type to a version nonspecific, more strongly typed type. - pub fn into_model(self) -> model::CoinbaseTransaction { - model::CoinbaseTransaction { - version: self.version, - locktime: self.locktime, - sequence: self.sequence, - coinbase: self.coinbase, - witness: self.witness, - } + pub fn into_model(self) -> Result { + use CoinbaseTransactionError as E; + + let coinbase = ScriptBuf::from_hex(&self.coinbase).map_err(E::Coinbase)?; + let witness = self + .witness + .map(|w| crate::witness_from_hex_slice(&[w])) + .transpose() + .map_err(E::Witness)?; + let version = transaction::Version::non_standard(self.version); + let locktime = absolute::LockTime::from_consensus(self.locktime); + let sequence = Sequence::from_consensus(self.sequence); + + Ok(model::CoinbaseTransaction { version, locktime, sequence, coinbase, witness }) } } @@ -311,7 +318,7 @@ impl GetBlockVerboseOne { .transpose() .map_err(E::NextBlockHash)?; let size = crate::to_u32(self.size, "size")?; - let coinbase_tx = Some(self.coinbase_tx.into_model()); + let coinbase_tx = Some(self.coinbase_tx.into_model().map_err(E::CoinbaseTx)?); let height = crate::to_u32(self.height, "height")?; let time = crate::to_u32(self.time, "time")?; let nonce = crate::to_u32(self.nonce, "nonce")?; @@ -377,7 +384,7 @@ impl GetBlockVerboseTwo { .transpose() .map_err(E::NextBlockHash)?; let size = crate::to_u32(self.size, "size")?; - let coinbase_tx = Some(self.coinbase_tx.into_model()); + let coinbase_tx = Some(self.coinbase_tx.into_model().map_err(E::CoinbaseTx)?); let height = crate::to_u32(self.height, "height")?; let time = crate::to_u32(self.time, "time")?; let nonce = crate::to_u32(self.nonce, "nonce")?; @@ -423,7 +430,8 @@ impl GetBlockVerboseThree { .tx .into_iter() .map(|entry| { - let (transaction, prevouts) = entry.transaction.into_model_with_prevouts()?; + let (transaction, prevouts) = + entry.transaction.into_model_with_prevouts().map_err(E::Transaction)?; let fee = entry.fee.map(Amount::from_btc).transpose().map_err(E::Fee)?; Ok(model::GetBlockVerboseThreeTransaction { transaction, prevouts, fee }) }) @@ -443,7 +451,7 @@ impl GetBlockVerboseThree { .transpose() .map_err(E::NextBlockHash)?; let size = crate::to_u32(self.size, "size")?; - let coinbase_tx = Some(self.coinbase_tx.into_model()); + let coinbase_tx = Some(self.coinbase_tx.into_model().map_err(E::CoinbaseTx)?); let height = crate::to_u32(self.height, "height")?; let time = crate::to_u32(self.time, "time")?; let nonce = crate::to_u32(self.nonce, "nonce")?; diff --git a/types/src/v31/blockchain/mod.rs b/types/src/v31/blockchain/mod.rs index df9d3cf4..7bccb0e6 100644 --- a/types/src/v31/blockchain/mod.rs +++ b/types/src/v31/blockchain/mod.rs @@ -12,14 +12,12 @@ use alloc::collections::BTreeMap; use serde::{Deserialize, Serialize}; pub use self::error::{ - GetMempoolClusterError, GetMempoolFeerateDiagramError, GetTxSpendingPrevoutError, - MapMempoolEntryError, MempoolEntryError, MempoolEntryFeesError, + CoinbaseTransactionError, GetBlockVerboseOneError, GetBlockVerboseThreeError, + GetBlockVerboseTwoError, GetMempoolClusterError, GetMempoolFeerateDiagramError, + GetTxSpendingPrevoutError, MapMempoolEntryError, MempoolEntryError, MempoolEntryFeesError, }; use super::{DeploymentInfo, GetBlockVerboseThreeTransaction, GetBlockVerboseTwoTransaction}; -pub use super::{ - GetBlockVerboseOneError, GetBlockVerboseThreeError, GetBlockVerboseTwoError, - GetDeploymentInfoError, GetMempoolInfoError, -}; +pub use super::{GetDeploymentInfoError, GetMempoolInfoError}; /// Result of JSON-RPC method `getmempoolcluster`. /// diff --git a/types/src/v31/mod.rs b/types/src/v31/mod.rs index 847e2f23..c4973f74 100644 --- a/types/src/v31/mod.rs +++ b/types/src/v31/mod.rs @@ -253,13 +253,14 @@ mod wallet; #[doc(inline)] pub use self::{ blockchain::{ - Chunk, CoinbaseTransaction, FeerateDiagramEntry, GetBlockVerboseOne, GetBlockVerboseThree, - GetBlockVerboseTwo, GetDeploymentInfo, GetMempoolAncestorsVerbose, GetMempoolCluster, - GetMempoolClusterError, GetMempoolDescendantsVerbose, GetMempoolEntry, - GetMempoolFeerateDiagram, GetMempoolFeerateDiagramError, GetMempoolInfo, - GetRawMempoolVerbose, GetTxSpendingPrevout, GetTxSpendingPrevoutError, - GetTxSpendingPrevoutItem, MapMempoolEntryError, MempoolEntry, MempoolEntryError, - MempoolEntryFees, MempoolEntryFeesError, + Chunk, CoinbaseTransaction, CoinbaseTransactionError, FeerateDiagramEntry, + GetBlockVerboseOne, GetBlockVerboseOneError, GetBlockVerboseThree, + GetBlockVerboseThreeError, GetBlockVerboseTwo, GetBlockVerboseTwoError, GetDeploymentInfo, + GetMempoolAncestorsVerbose, GetMempoolCluster, GetMempoolClusterError, + GetMempoolDescendantsVerbose, GetMempoolEntry, GetMempoolFeerateDiagram, + GetMempoolFeerateDiagramError, GetMempoolInfo, GetRawMempoolVerbose, GetTxSpendingPrevout, + GetTxSpendingPrevoutError, GetTxSpendingPrevoutItem, MapMempoolEntryError, MempoolEntry, + MempoolEntryError, MempoolEntryFees, MempoolEntryFeesError, }, control::Logging, network::{ConnectionType, GetPeerInfo, PeerInfo, TransportProtocolType}, @@ -353,9 +354,8 @@ pub use crate::{ }, v29::{ ActivityEntry, ChainState, DeriveAddressesMultipath, GetBlockHeader, GetBlockHeaderError, - GetBlockHeaderVerbose, GetBlockHeaderVerboseError, GetBlockVerboseOneError, - GetBlockVerboseThreeError, GetBlockVerboseThreePrevout, GetBlockVerboseThreeTransaction, - GetBlockVerboseTwoError, GetBlockVerboseTwoTransaction, GetBlockchainInfo, + GetBlockHeaderVerbose, GetBlockHeaderVerboseError, GetBlockVerboseThreePrevout, + GetBlockVerboseThreeTransaction, GetBlockVerboseTwoTransaction, GetBlockchainInfo, GetBlockchainInfoError, GetChainStates, GetChainStatesError, GetDescriptorActivity, GetDescriptorActivityError, GetDescriptorInfo, GetOrphanTxsError, GetOrphanTxsVerboseOneEntryError, GetOrphanTxsVerboseTwoEntryError, From 78a776232b0e37289b65fbbaba8f6e29e3a1c29c Mon Sep 17 00:00:00 2001 From: "satsfy (Renato Britto)" Date: Wed, 17 Jun 2026 13:41:35 -0300 Subject: [PATCH 4/4] client: drop options from gettxspendingprevout Drop the mempool_only and return_spending_tx arguments from the typed method to match corepc style. The test that needs return_spending_tx issues the RPC directly. --- client/src/client_sync/v31/blockchain.rs | 8 +------- integration_test/tests/blockchain.rs | 9 +++++++-- integration_test/tests/blockchain_core.rs | 3 +-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/client/src/client_sync/v31/blockchain.rs b/client/src/client_sync/v31/blockchain.rs index 6d70dc6a..60ca001f 100644 --- a/client/src/client_sync/v31/blockchain.rs +++ b/client/src/client_sync/v31/blockchain.rs @@ -41,8 +41,6 @@ macro_rules! impl_client_v31__get_tx_spending_prevout { pub fn get_tx_spending_prevout( &self, outputs: &[bitcoin::OutPoint], - mempool_only: bool, - return_spending_tx: bool, ) -> Result { let json_outputs: Vec<_> = outputs.iter().map(|out| { serde_json::json!({ @@ -50,11 +48,7 @@ macro_rules! impl_client_v31__get_tx_spending_prevout { "vout": out.vout, }) }).collect(); - let options = serde_json::json!({ - "mempool_only": mempool_only, - "return_spending_tx": return_spending_tx, - }); - self.call("gettxspendingprevout", &[json_outputs.into(), options]) + self.call("gettxspendingprevout", &[json_outputs.into()]) } } }; diff --git a/integration_test/tests/blockchain.rs b/integration_test/tests/blockchain.rs index c8dbec7e..3b0e722e 100644 --- a/integration_test/tests/blockchain.rs +++ b/integration_test/tests/blockchain.rs @@ -684,7 +684,7 @@ fn blockchain__get_tx_spending_prevout__modelled() { node.client.get_tx_spending_prevout(&inputs).expect("gettxspendingprevout"); #[cfg(not(feature = "v30_and_below"))] let json: GetTxSpendingPrevout = - node.client.get_tx_spending_prevout(&inputs, true, false).expect("gettxspendingprevout"); + node.client.get_tx_spending_prevout(&inputs).expect("gettxspendingprevout"); let model: Result = json.into_model(); let spending_prevout = model.unwrap(); @@ -703,9 +703,14 @@ fn blockchain__get_tx_spending_prevout_spending_tx_and_block_hash__modelled() { let (_address, tx) = node.create_mined_transaction(); let outpoint = tx.input[0].previous_output; + let outputs = bitcoind::serde_json::json!([{ + "txid": outpoint.txid.to_string(), + "vout": outpoint.vout, + }]); + let options = bitcoind::serde_json::json!({ "return_spending_tx": true }); let json: GetTxSpendingPrevout = node .client - .get_tx_spending_prevout(&[outpoint], false, true) + .call("gettxspendingprevout", &[outputs, options]) .expect("gettxspendingprevout"); let item = &json.0[0]; assert!(item.spending_txid.is_some()); diff --git a/integration_test/tests/blockchain_core.rs b/integration_test/tests/blockchain_core.rs index f6fc0db9..0a7e0242 100644 --- a/integration_test/tests/blockchain_core.rs +++ b/integration_test/tests/blockchain_core.rs @@ -242,8 +242,7 @@ fn get_tx_spending_prevout_for_unspent_output() { #[cfg(feature = "v30_and_below")] let json: GetTxSpendingPrevout = node.client.get_tx_spending_prevout(&outpoints).unwrap(); #[cfg(not(feature = "v30_and_below"))] - let json: GetTxSpendingPrevout = - node.client.get_tx_spending_prevout(&outpoints, true, false).unwrap(); + let json: GetTxSpendingPrevout = node.client.get_tx_spending_prevout(&outpoints).unwrap(); assert_eq!(json.0.len(), 1); assert_eq!(json.0[0].txid, txid.to_string());