From 7a59cffa79e7307582e076ea4969f892080beeb7 Mon Sep 17 00:00:00 2001 From: 0xb10c Date: Tue, 3 Feb 2026 12:23:52 +0100 Subject: [PATCH] Avoid concrete rust-bitcoin type in hidden module A concrete rust-bitcoin type was added to the version-specific `GetOrphanTxs` struct during #435. This is corrected by using a list of strings and converting to the model type via `into_model`. Affects v29 and v30. Also, use the explicit error in the itegration tests to check that the re-exports are good. Fixes #484 --- integration_test/tests/hidden.rs | 12 ++++++++---- types/src/v29/hidden/error.rs | 29 +++++++++++++++++++++++++++-- types/src/v29/hidden/into.rs | 11 +++++++++-- types/src/v29/hidden/mod.rs | 7 ++++--- types/src/v29/mod.rs | 2 +- types/src/v30/hidden/error.rs | 29 +++++++++++++++++++++++++++-- types/src/v30/hidden/into.rs | 11 +++++++++-- types/src/v30/hidden/mod.rs | 7 ++++--- types/src/v30/mod.rs | 2 +- 9 files changed, 90 insertions(+), 20 deletions(-) diff --git a/integration_test/tests/hidden.rs b/integration_test/tests/hidden.rs index 313f49b62..5eba3216b 100644 --- a/integration_test/tests/hidden.rs +++ b/integration_test/tests/hidden.rs @@ -148,10 +148,14 @@ fn hidden__get_orphan_txs__modelled() { let json_v1: GetOrphanTxsVerboseOne = node2.client.get_orphan_txs_verbosity_1().expect("getorphantxs 1"); let json_v2: GetOrphanTxsVerboseTwo = node2.client.get_orphan_txs_verbosity_2().expect("getorphantxs 2"); - let model_v0: mtype::GetOrphanTxs = json_v0.into_model(); - let model_v1: mtype::GetOrphanTxsVerboseOne = json_v1.into_model().unwrap(); - let model_v2: mtype::GetOrphanTxsVerboseTwo = json_v2.into_model().unwrap(); - + // use explicit errors here to check that the re-exports are good + let model_v0: Result = json_v0.into_model(); + let model_v1: Result = json_v1.into_model(); + let model_v2: Result = json_v2.into_model(); + + let model_v0 = model_v0.unwrap(); + let model_v1 = model_v1.unwrap(); + let model_v2 = model_v2.unwrap(); assert_eq!(model_v0.0.len(), NUM_ORPHANS as usize); assert_eq!(model_v1.0.len(), NUM_ORPHANS as usize); diff --git a/types/src/v29/hidden/error.rs b/types/src/v29/hidden/error.rs index 93a62dd30..27e415dbd 100644 --- a/types/src/v29/hidden/error.rs +++ b/types/src/v29/hidden/error.rs @@ -7,6 +7,30 @@ use bitcoin::hex; use crate::error::write_err; +/// Error when converting a `GetOrphanTxs` type into the model type. +#[derive(Debug)] +pub enum GetOrphanTxsError { + /// Conversion of a `txid` from the orphanage failed. + Txid(hex::HexToArrayError), +} + +impl fmt::Display for GetOrphanTxsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Txid(ref e) => write_err!(f, "conversion of the `txid` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for GetOrphanTxsError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::Txid(ref e) => Some(e), + } + } +} + /// Error when converting a `GetOrphanTxsVerboseOneEntry` type into the model type. #[derive(Debug)] pub enum GetOrphanTxsVerboseOneEntryError { @@ -54,8 +78,9 @@ impl fmt::Display for GetOrphanTxsVerboseTwoEntryError { Self::Txid(ref e) => write_err!(f, "conversion of the `txid` field failed"; e), Self::Wtxid(ref e) => write_err!(f, "conversion of the `wtxid` field failed"; e), Self::Hex(ref e) => write_err!(f, "conversion of hex data to bytes failed"; e), - Self::Consensus(ref e) => - write_err!(f, "consensus decoding of `hex` to transaction failed"; e), + Self::Consensus(ref e) => { + write_err!(f, "consensus decoding of `hex` to transaction failed"; e) + } } } } diff --git a/types/src/v29/hidden/into.rs b/types/src/v29/hidden/into.rs index 6f62dcaa9..202469163 100644 --- a/types/src/v29/hidden/into.rs +++ b/types/src/v29/hidden/into.rs @@ -5,7 +5,7 @@ use bitcoin::hashes::hex::FromHex; use bitcoin::{Transaction, Txid, Wtxid}; use super::{ - GetOrphanTxs, GetOrphanTxsVerboseOne, GetOrphanTxsVerboseOneEntry, + GetOrphanTxs, GetOrphanTxsError, GetOrphanTxsVerboseOne, GetOrphanTxsVerboseOneEntry, GetOrphanTxsVerboseOneEntryError, GetOrphanTxsVerboseTwo, GetOrphanTxsVerboseTwoEntry, GetOrphanTxsVerboseTwoEntryError, }; @@ -13,7 +13,14 @@ use crate::model; impl GetOrphanTxs { /// Converts version specific type to a version nonspecific, more strongly typed type. - pub fn into_model(self) -> model::GetOrphanTxs { model::GetOrphanTxs(self.0) } + pub fn into_model(self) -> Result { + use GetOrphanTxsError as E; + + let txids: Result, E> = + self.0.into_iter().map(|s| s.parse::().map_err(E::Txid)).collect(); + + Ok(model::GetOrphanTxs(txids?)) + } } impl GetOrphanTxsVerboseOneEntry { diff --git a/types/src/v29/hidden/mod.rs b/types/src/v29/hidden/mod.rs index 366a01b47..6192ba594 100644 --- a/types/src/v29/hidden/mod.rs +++ b/types/src/v29/hidden/mod.rs @@ -7,10 +7,11 @@ mod error; mod into; -use bitcoin::Txid; use serde::{Deserialize, Serialize}; -pub use self::error::{GetOrphanTxsVerboseOneEntryError, GetOrphanTxsVerboseTwoEntryError}; +pub use self::error::{ + GetOrphanTxsError, GetOrphanTxsVerboseOneEntryError, GetOrphanTxsVerboseTwoEntryError, +}; /// Result of JSON-RPC method `getorphantxs` verbosity 0. /// @@ -19,7 +20,7 @@ pub use self::error::{GetOrphanTxsVerboseOneEntryError, GetOrphanTxsVerboseTwoEn /// > Shows transactions in the tx orphanage. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))] -pub struct GetOrphanTxs(pub Vec); +pub struct GetOrphanTxs(pub Vec); /// Result of JSON-RPC method `getorphantxs` verbosity 1. /// diff --git a/types/src/v29/mod.rs b/types/src/v29/mod.rs index 687585cbd..ed59ceed4 100644 --- a/types/src/v29/mod.rs +++ b/types/src/v29/mod.rs @@ -270,7 +270,7 @@ pub use self::{ SpendActivity, }, hidden::{ - GetOrphanTxs, GetOrphanTxsVerboseOne, GetOrphanTxsVerboseOneEntry, + GetOrphanTxs, GetOrphanTxsError, GetOrphanTxsVerboseOne, GetOrphanTxsVerboseOneEntry, GetOrphanTxsVerboseOneEntryError, GetOrphanTxsVerboseTwo, GetOrphanTxsVerboseTwoEntry, GetOrphanTxsVerboseTwoEntryError, }, diff --git a/types/src/v30/hidden/error.rs b/types/src/v30/hidden/error.rs index 93a62dd30..ad4f7f108 100644 --- a/types/src/v30/hidden/error.rs +++ b/types/src/v30/hidden/error.rs @@ -7,6 +7,30 @@ use bitcoin::hex; use crate::error::write_err; +/// Error when converting a `GetOrphanTxs` type into the model type. +#[derive(Debug)] +pub enum GetOrphanTxsError { + /// Conversion of the transaction `txid` field failed. + Txid(hex::HexToArrayError), +} + +impl fmt::Display for GetOrphanTxsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Txid(ref e) => write_err!(f, "conversion of the `txid` field failed"; e), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for GetOrphanTxsError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::Txid(ref e) => Some(e), + } + } +} + /// Error when converting a `GetOrphanTxsVerboseOneEntry` type into the model type. #[derive(Debug)] pub enum GetOrphanTxsVerboseOneEntryError { @@ -54,8 +78,9 @@ impl fmt::Display for GetOrphanTxsVerboseTwoEntryError { Self::Txid(ref e) => write_err!(f, "conversion of the `txid` field failed"; e), Self::Wtxid(ref e) => write_err!(f, "conversion of the `wtxid` field failed"; e), Self::Hex(ref e) => write_err!(f, "conversion of hex data to bytes failed"; e), - Self::Consensus(ref e) => - write_err!(f, "consensus decoding of `hex` to transaction failed"; e), + Self::Consensus(ref e) => { + write_err!(f, "consensus decoding of `hex` to transaction failed"; e) + } } } } diff --git a/types/src/v30/hidden/into.rs b/types/src/v30/hidden/into.rs index e4ab66bc7..2785aa2f1 100644 --- a/types/src/v30/hidden/into.rs +++ b/types/src/v30/hidden/into.rs @@ -5,7 +5,7 @@ use bitcoin::hashes::hex::FromHex; use bitcoin::{Transaction, Txid, Wtxid}; use super::{ - GetOrphanTxs, GetOrphanTxsVerboseOne, GetOrphanTxsVerboseOneEntry, + GetOrphanTxs, GetOrphanTxsError, GetOrphanTxsVerboseOne, GetOrphanTxsVerboseOneEntry, GetOrphanTxsVerboseOneEntryError, GetOrphanTxsVerboseTwo, GetOrphanTxsVerboseTwoEntry, GetOrphanTxsVerboseTwoEntryError, }; @@ -13,7 +13,14 @@ use crate::model; impl GetOrphanTxs { /// Converts version specific type to a version nonspecific, more strongly typed type. - pub fn into_model(self) -> model::GetOrphanTxs { model::GetOrphanTxs(self.0) } + pub fn into_model(self) -> Result { + use GetOrphanTxsError as E; + + let txids: Result, E> = + self.0.into_iter().map(|s| s.parse::().map_err(E::Txid)).collect(); + + Ok(model::GetOrphanTxs(txids?)) + } } impl GetOrphanTxsVerboseOneEntry { diff --git a/types/src/v30/hidden/mod.rs b/types/src/v30/hidden/mod.rs index 72f886205..15d0405e6 100644 --- a/types/src/v30/hidden/mod.rs +++ b/types/src/v30/hidden/mod.rs @@ -7,10 +7,11 @@ mod error; mod into; -use bitcoin::Txid; use serde::{Deserialize, Serialize}; -pub use self::error::{GetOrphanTxsVerboseOneEntryError, GetOrphanTxsVerboseTwoEntryError}; +pub use self::error::{ + GetOrphanTxsError, GetOrphanTxsVerboseOneEntryError, GetOrphanTxsVerboseTwoEntryError, +}; /// Result of JSON-RPC method `getorphantxs` verbosity 0. /// @@ -19,7 +20,7 @@ pub use self::error::{GetOrphanTxsVerboseOneEntryError, GetOrphanTxsVerboseTwoEn /// > Shows transactions in the tx orphanage. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))] -pub struct GetOrphanTxs(pub Vec); +pub struct GetOrphanTxs(pub Vec); /// Result of JSON-RPC method `getorphantxs` verbosity 1. /// diff --git a/types/src/v30/mod.rs b/types/src/v30/mod.rs index a90a173c6..5c9998b71 100644 --- a/types/src/v30/mod.rs +++ b/types/src/v30/mod.rs @@ -252,7 +252,7 @@ mod wallet; pub use self::{ blockchain::GetMempoolInfo, hidden::{ - GetOrphanTxs, GetOrphanTxsVerboseOne, GetOrphanTxsVerboseOneEntry, + GetOrphanTxs, GetOrphanTxsError, GetOrphanTxsVerboseOne, GetOrphanTxsVerboseOneEntry, GetOrphanTxsVerboseOneEntryError, GetOrphanTxsVerboseTwo, GetOrphanTxsVerboseTwoEntry, GetOrphanTxsVerboseTwoEntryError, },