Skip to content

Commit adbb6a0

Browse files
author
+Sharon
committed
Add DecodeScriptSegwit struct and support in DecodeScript conversion
- Add `DecodeScriptSegwit` struct to model the `segwit` field returned by the `decodescript` RPC. - Update `DecodeScript` to include an optional `segwit` field. Add DecodeScriptSegwit struct, conversions, and model support - Add `DecodeScriptSegwit` struct to both versioned and model representations. - Implement `into_model()` for `DecodeScriptSegwit` and update `DecodeScript` accordingly. - Use `ScriptBuf` instead of `String` for `hex` to strongly type the field. - Replace `String` with `Address<NetworkUnchecked>` for `p2sh_segwit` and other fields. - Normalize and correct field comments to match Core `decodescript` RPC output. - Clean up formatting errors Add DecodeScriptSegwit into_model to v17 and refactor error handling - Add `into_model` implementation for `DecodeScriptSegwit` in v17. - Return `segwit` in v17, as it is present in RPC output despite not being documented until v19. - Add `DecodeScriptSegwitError` enum in v17, as `address` is sometimes `None` and error handling is needed. - Remove duplicate `DecodeScriptSegwitError` from v23 and reuse the one from v22 via import. - Move `descriptor` field in `DecodeScriptSegwit` model struct to match the field order in Bitcoin Core's `decodescript` RPC response.
1 parent 6f92d18 commit adbb6a0

File tree

13 files changed

+253
-46
lines changed

13 files changed

+253
-46
lines changed

types/src/model/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ pub use self::{
4141
raw_transactions::{
4242
AnalyzePsbt, AnalyzePsbtInput, AnalyzePsbtInputMissing, CombinePsbt, CombineRawTransaction,
4343
ConvertToPsbt, CreatePsbt, CreateRawTransaction, DecodePsbt, DecodeRawTransaction,
44-
DecodeScript, DescriptorProcessPsbt, FinalizePsbt, FundRawTransaction, GetRawTransaction,
45-
GetRawTransactionVerbose, JoinPsbts, MempoolAcceptance, SendRawTransaction, SignFail,
46-
SignRawTransaction, SubmitPackage, SubmitPackageTxResult, SubmitPackageTxResultFees,
47-
TestMempoolAccept, UtxoUpdatePsbt,
44+
DecodeScript, DecodeScriptSegwit, DescriptorProcessPsbt, FinalizePsbt, FundRawTransaction,
45+
GetRawTransaction, GetRawTransactionVerbose, JoinPsbts, MempoolAcceptance,
46+
SendRawTransaction, SignFail, SignRawTransaction, SubmitPackage, SubmitPackageTxResult,
47+
SubmitPackageTxResultFees, TestMempoolAccept, UtxoUpdatePsbt,
4848
},
4949
util::{
5050
CreateMultisig, DeriveAddresses, EstimateSmartFee, SignMessageWithPrivKey, ValidateAddress,

types/src/model/raw_transactions.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,31 @@ pub struct DecodeScript {
116116
pub addresses: Vec<Address<NetworkUnchecked>>,
117117
/// Address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).
118118
pub p2sh: Option<Address<NetworkUnchecked>>,
119-
/// Address of the P2SH script wrapping this witness redeem script
120-
pub p2sh_segwit: Option<String>,
119+
/// Result of a witness output script wrapping this redeem script (not returned for types that should not be wrapped).
120+
pub segwit: Option<DecodeScriptSegwit>,
121+
/// Address of the P2SH script wrapping this witness redeem script.
122+
pub p2sh_segwit: Option<Address<NetworkUnchecked>>,
123+
}
124+
/// Models the `segwit` field returned by the `decodescript` RPC.
125+
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
126+
#[serde(deny_unknown_fields)]
127+
pub struct DecodeScriptSegwit {
128+
/// Disassembly of the script.
129+
pub asm: String,
130+
/// The raw output script bytes, hex-encoded.
131+
pub hex: ScriptBuf,
132+
/// The output type (e.g. nonstandard, anchor, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_scripthash, witness_v0_keyhash, witness_v1_taproot, witness_unknown).
133+
pub type_: String,
134+
/// Bitcoin address (only if a well-defined address exists)v22 and later only.
135+
pub address: Option<Address<NetworkUnchecked>>,
136+
/// The required signatures.
137+
pub required_signatures: Option<u64>,
138+
/// List of bitcoin addresses.
139+
pub addresses: Vec<Address<NetworkUnchecked>>,
140+
/// Inferred descriptor for the script. v23 and later only.
141+
pub descriptor: Option<String>,
142+
/// Address of the P2SH script wrapping this witness redeem script.
143+
pub p2sh_segwit: Option<Address<NetworkUnchecked>>,
121144
}
122145

123146
/// Models the result of JSON-RPC method `descriptorprocesspsbt`.

types/src/v17/raw_transactions/error.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ pub enum DecodeScriptError {
178178
Addresses(address::ParseError),
179179
/// Conversion of the transaction `p2sh` field failed.
180180
P2sh(address::ParseError),
181+
/// Conversion of the transaction `segwit` field failed.
182+
Segwit(DecodeScriptSegwitError),
181183
}
182184

183185
impl fmt::Display for DecodeScriptError {
@@ -188,6 +190,7 @@ impl fmt::Display for DecodeScriptError {
188190
E::Hex(ref e) => write_err!(f, "conversion of the `hex` field failed"; e),
189191
E::Addresses(ref e) => write_err!(f, "conversion of the `addresses` field failed"; e),
190192
E::P2sh(ref e) => write_err!(f, "conversion of the `p2sh` field failed"; e),
193+
E::Segwit(ref e) => write_err!(f, "conversion of the `segwit` field failed"; e),
191194
}
192195
}
193196
}
@@ -201,6 +204,34 @@ impl std::error::Error for DecodeScriptError {
201204
E::Hex(ref e) => Some(e),
202205
E::Addresses(ref e) => Some(e),
203206
E::P2sh(ref e) => Some(e),
207+
E::Segwit(ref e) => Some(e),
208+
}
209+
}
210+
}
211+
212+
/// Error when converting a `DecodeScriptSegwit` type into the model type.
213+
#[derive(Debug)]
214+
pub enum DecodeScriptSegwitError {
215+
/// Conversion of the transaction `addresses` field failed.
216+
Addresses(address::ParseError),
217+
}
218+
219+
impl fmt::Display for DecodeScriptSegwitError {
220+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221+
use DecodeScriptSegwitError as E;
222+
match *self {
223+
E::Addresses(ref e) =>
224+
write_err!(f, "conversion of the `addresses` field in `segwit` failed"; e),
225+
}
226+
}
227+
}
228+
229+
#[cfg(feature = "std")]
230+
impl std::error::Error for DecodeScriptSegwitError {
231+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
232+
use DecodeScriptSegwitError as E;
233+
match *self {
234+
E::Addresses(ref e) => Some(e),
204235
}
205236
}
206237
}

types/src/v17/raw_transactions/into.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use super::{
1414
FinalizePsbt, FinalizePsbtError, FundRawTransaction, FundRawTransactionError,
1515
GetRawTransaction, GetRawTransactionVerbose, GetRawTransactionVerboseError, MempoolAcceptance,
1616
PsbtInput, PsbtInputError, PsbtOutput, PsbtOutputError, SendRawTransaction, SignFail,
17-
SignFailError, SignRawTransaction, SignRawTransactionError, TestMempoolAccept,
17+
SignFailError, SignRawTransaction, SignRawTransactionError, TestMempoolAccept,DecodeScriptSegwit,
18+
DecodeScriptSegwitError,
1819
};
1920
use crate::model;
2021
use crate::psbt::RawTransactionError;
@@ -310,6 +311,38 @@ impl DecodeScript {
310311
addresses,
311312
p2sh,
312313
p2sh_segwit: self.p2sh_segwit,
314+
segwit: self.segwit.map(|s| s.into_model()).transpose().map_err(E::Segwit)?,
315+
})
316+
}
317+
}
318+
319+
impl DecodeScriptSegwit {
320+
/// Converts version specific type to a version nonspecific, more strongly typed type.
321+
pub fn into_model(self) -> Result<model::DecodeScriptSegwit, DecodeScriptSegwitError> {
322+
use DecodeScriptSegwitError as E;
323+
324+
// Convert `Option<Vec<String>>` to `Vec<Address<NetworkUnchecked>>`
325+
let addresses = match self.addresses {
326+
Some(addrs) => addrs
327+
.into_iter()
328+
.map(|s| s.parse::<Address<_>>())
329+
.collect::<Result<_, _>>()
330+
.map_err(E::Addresses)?,
331+
None => vec![],
332+
};
333+
334+
let required_signatures = self.required_signatures;
335+
let p2sh_segwit = self.p2sh_segwit;
336+
337+
Ok(model::DecodeScriptSegwit {
338+
asm: self.asm,
339+
hex: self.hex,
340+
descriptor: None,
341+
address:None,
342+
type_: self.type_,
343+
required_signatures,
344+
addresses,
345+
p2sh_segwit,
313346
})
314347
}
315348
}

types/src/v17/raw_transactions/mod.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ mod into;
99

1010
use std::collections::HashMap;
1111

12+
use bitcoin::address::{Address, NetworkUnchecked};
13+
use bitcoin::ScriptBuf;
1214
use serde::{Deserialize, Serialize};
1315

1416
use crate::ScriptSig;
@@ -17,6 +19,7 @@ use crate::ScriptSig;
1719
pub use self::error::{
1820
DecodePsbtError, DecodeScriptError, FundRawTransactionError, GetRawTransactionVerboseError,
1921
PsbtInputError, PsbtOutputError, SignFailError, SignRawTransactionError, FinalizePsbtError,
22+
DecodeScriptSegwitError,
2023
};
2124
// Re-export types that appear in the public API of this module.
2225
pub use crate::psbt::{
@@ -227,33 +230,33 @@ pub struct DecodeScript {
227230
pub addresses: Option<Vec<String>>,
228231
/// Address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).
229232
pub p2sh: Option<String>,
230-
/// Segwit data (see `DecodeScriptSegwit` for explanation).
233+
/// Result of a witness output script wrapping this redeem script (not returned for types that should not be wrapped).
231234
pub segwit: Option<DecodeScriptSegwit>,
232235
/// Address of the P2SH script wrapping this witness redeem script
233236
#[serde(rename = "p2sh-segwit")]
234-
pub p2sh_segwit: Option<String>,
237+
pub p2sh_segwit: Option<Address<NetworkUnchecked>>,
235238
}
236239

237240
/// Seemingly undocumented data returned in the `segwit` field of `DecodeScript`.
238241
// This seems to be the same as `DecodeScript` except the `p2sh` field is called `p2sh-segwit`.
239242
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
240243
#[serde(deny_unknown_fields)]
241244
pub struct DecodeScriptSegwit {
242-
/// Script public key.
245+
/// Disassembly of the script.
243246
pub asm: String,
244-
/// Hex encoded public key.
245-
pub hex: String,
246-
/// The output type.
247+
/// The raw output script bytes, hex-encoded.
248+
pub hex: ScriptBuf,
249+
/// The type of the output script (e.g. witness_v0_keyhash or witness_v0_scripthash).
247250
#[serde(rename = "type")]
248251
pub type_: String,
249252
/// The required signatures.
250253
#[serde(rename = "reqSigs")]
251254
pub required_signatures: Option<u64>,
252255
/// List of bitcoin addresses.
253256
pub addresses: Option<Vec<String>>,
254-
/// Address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).
257+
/// Address of the P2SH script wrapping this witness redeem script.
255258
#[serde(rename = "p2sh-segwit")]
256-
pub p2sh_segtwit: Option<String>,
259+
pub p2sh_segwit: Option<Address<NetworkUnchecked>>,
257260
}
258261

259262
/// Result of JSON-RPC method `finalizepsbt`.

types/src/v22/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,9 @@ pub use self::{
253253
blockchain::GetMempoolInfo,
254254
control::Logging,
255255
network::{Banned, GetPeerInfo, ListBanned},
256-
raw_transactions::{DecodeScript, DecodeScriptError},
256+
raw_transactions::{
257+
DecodeScript, DecodeScriptError, DecodeScriptSegwit, DecodeScriptSegwitError,
258+
},
257259
};
258260
#[doc(inline)]
259261
pub use crate::{

types/src/v22/raw_transactions/error.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub enum DecodeScriptError {
1717
Addresses(address::ParseError),
1818
/// Conversion of the transaction `p2sh` field failed.
1919
P2sh(address::ParseError),
20+
/// Conversion of the transaction `segwit` field failed.
21+
Segwit(DecodeScriptSegwitError),
2022
}
2123

2224
impl fmt::Display for DecodeScriptError {
@@ -28,6 +30,7 @@ impl fmt::Display for DecodeScriptError {
2830
E::Address(ref e) => write_err!(f, "conversion of the `address` field failed"; e),
2931
E::Addresses(ref e) => write_err!(f, "conversion of the `addresses` field failed"; e),
3032
E::P2sh(ref e) => write_err!(f, "conversion of the `p2sh` field failed"; e),
33+
E::Segwit(ref e) => write_err!(f, "conversion of the `segwit` field failed"; e),
3134
}
3235
}
3336
}
@@ -42,6 +45,39 @@ impl std::error::Error for DecodeScriptError {
4245
E::Address(ref e) => Some(e),
4346
E::Addresses(ref e) => Some(e),
4447
E::P2sh(ref e) => Some(e),
48+
E::Segwit(ref e) => Some(e),
49+
}
50+
}
51+
}
52+
53+
/// Error when converting a `DecodeScriptSegwit` type into the model type.
54+
#[derive(Debug)]
55+
pub enum DecodeScriptSegwitError {
56+
/// Conversion of the transaction `address` field failed.
57+
Address(address::ParseError),
58+
/// Conversion of the transaction `addresses` field failed.
59+
Addresses(address::ParseError),
60+
}
61+
62+
impl fmt::Display for DecodeScriptSegwitError {
63+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64+
use DecodeScriptSegwitError as E;
65+
match *self {
66+
E::Address(ref e) =>
67+
write_err!(f, "conversion of the `address` field in `segwit` failed"; e),
68+
E::Addresses(ref e) =>
69+
write_err!(f, "conversion of the `addresses` field in `segwit` failed"; e),
70+
}
71+
}
72+
}
73+
74+
#[cfg(feature = "std")]
75+
impl std::error::Error for DecodeScriptSegwitError {
76+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
77+
use DecodeScriptSegwitError as E;
78+
match *self {
79+
E::Address(ref e) => Some(e),
80+
E::Addresses(ref e) => Some(e),
4581
}
4682
}
4783
}

types/src/v22/raw_transactions/into.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use bitcoin::Address;
44

5-
use super::{DecodeScript, DecodeScriptError};
5+
use super::{DecodeScript, DecodeScriptError, DecodeScriptSegwit, DecodeScriptSegwitError};
66
use crate::model;
77

88
impl DecodeScript {
@@ -32,7 +32,43 @@ impl DecodeScript {
3232
required_signatures: self.required_signatures,
3333
addresses,
3434
p2sh,
35+
segwit: self.segwit.map(|s| s.into_model()).transpose().map_err(E::Segwit)?,
3536
p2sh_segwit: self.p2sh_segwit,
3637
})
3738
}
3839
}
40+
41+
impl DecodeScriptSegwit {
42+
/// Converts version specific type to a version nonspecific, more strongly typed type.
43+
pub fn into_model(self) -> Result<model::DecodeScriptSegwit, DecodeScriptSegwitError> {
44+
use DecodeScriptSegwitError as E;
45+
46+
let address = match self.address {
47+
Some(addr) => Some(addr.parse::<Address<_>>().map_err(E::Address)?),
48+
None => None,
49+
};
50+
// Convert `Option<Vec<String>>` to `Vec<Address<NetworkUnchecked>>`
51+
let addresses = match self.addresses {
52+
Some(addrs) => addrs
53+
.into_iter()
54+
.map(|s| s.parse::<Address<_>>())
55+
.collect::<Result<_, _>>()
56+
.map_err(E::Addresses)?,
57+
None => vec![],
58+
};
59+
60+
let required_signatures = self.required_signatures;
61+
let p2sh_segwit = self.p2sh_segwit;
62+
63+
Ok(model::DecodeScriptSegwit {
64+
asm: self.asm,
65+
hex: self.hex,
66+
descriptor: None,
67+
type_: self.type_,
68+
address,
69+
required_signatures,
70+
addresses,
71+
p2sh_segwit,
72+
})
73+
}
74+
}

types/src/v22/raw_transactions/mod.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
mod error;
88
mod into;
99

10+
use bitcoin::address::{Address, NetworkUnchecked};
11+
use bitcoin::ScriptBuf;
1012
use serde::{Deserialize, Serialize};
1113

12-
pub use self::error::DecodeScriptError;
14+
pub use self::error::{DecodeScriptError, DecodeScriptSegwitError};
1315

1416
/// Result of JSON-RPC method `decodescript`.
1517
///
@@ -25,7 +27,7 @@ pub use self::error::DecodeScriptError;
2527
pub struct DecodeScript {
2628
/// Script public key.
2729
pub asm: String,
28-
/// The output type.
30+
/// The output type
2931
#[serde(rename = "type")]
3032
pub type_: String,
3133
/// Bitcoin address (only if a well-defined address exists). v22 and later only.
@@ -37,22 +39,22 @@ pub struct DecodeScript {
3739
pub addresses: Option<Vec<String>>,
3840
/// Address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).
3941
pub p2sh: Option<String>,
40-
/// Segwit data (see `DecodeScriptSegwit` for explanation).
42+
/// Result of a witness output script wrapping this redeem script (not returned for types that should not be wrapped).
4143
pub segwit: Option<DecodeScriptSegwit>,
4244
/// Address of the P2SH script wrapping this witness redeem script
4345
#[serde(rename = "p2sh-segwit")]
44-
pub p2sh_segwit: Option<String>,
46+
pub p2sh_segwit: Option<Address<NetworkUnchecked>>,
4547
}
4648

4749
/// `segwit` item returned as part of `decodescript`.
4850
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
4951
#[serde(deny_unknown_fields)]
5052
pub struct DecodeScriptSegwit {
51-
/// Script public key.
53+
/// Disassembly of the script.
5254
pub asm: String,
53-
/// Hex encoded public key.
54-
pub hex: String,
55-
/// The output type.
55+
/// The raw output script bytes, hex-encoded.
56+
pub hex: ScriptBuf,
57+
/// The output type (e.g. nonstandard, anchor, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_scripthash, witness_v0_keyhash, witness_v1_taproot, witness_unknown).
5658
#[serde(rename = "type")]
5759
pub type_: String,
5860
/// Bitcoin address (only if a well-defined address exists). v22 and later only.
@@ -62,7 +64,7 @@ pub struct DecodeScriptSegwit {
6264
pub required_signatures: Option<u64>,
6365
/// List of bitcoin addresses.
6466
pub addresses: Option<Vec<String>>,
65-
/// Address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).
67+
/// Address of the P2SH script wrapping this witness redeem script.
6668
#[serde(rename = "p2sh-segwit")]
67-
pub p2sh_segtwit: Option<String>,
69+
pub p2sh_segwit: Option<Address<NetworkUnchecked>>,
6870
}

0 commit comments

Comments
 (0)