Skip to content

Commit 40b6314

Browse files
committed
Merge #370: Add ScriptPubKey model
478a53c Use ScriptPubKey model in GetDescriptorActivity (Jamil Lambert, PhD) 267c004 Add ScriptPubKey model, error and into_model (Jamil Lambert, PhD) Pull request description: `ScriptPubKey` has fields that can be modelled with rust-bitcoin types. And says in it's own rustdoc ```The `mtype::ScriptPubkey` mirrors this design```, but there is none. Two RPCs use this type, `getdescriptoractivity` and `gettxout`. The `GetTxOut` model extracts individual fields from the `ScriptPubKey` to create a `TxOut` and `Address`, and does not model a `ScriptPubKey` type. - Add the `ScriptPubKey` model, error and into function. - Use the model in `GetDescriptorActivity`. ACKs for top commit: tcharding: ACK 478a53c Tree-SHA512: b466b99f4d609778b3d98e520610b186b65bee7c2fdfee11b37536e778bafe713a3b2686ac29cec8e8747fb6732a68fbc455f40aa07ab4fcb5ceb6522d896e83
2 parents 3a9c52a + 478a53c commit 40b6314

File tree

5 files changed

+105
-6
lines changed

5 files changed

+105
-6
lines changed

types/src/lib.rs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ use bitcoin::hex::{self, FromHex as _};
4242
use bitcoin::{Amount, FeeRate, ScriptBuf, Witness};
4343
use serde::{Deserialize, Serialize};
4444

45+
use crate::error::write_err;
46+
4547
/// Converts an `i64` numeric type to a `u32`.
4648
///
4749
/// The Bitcoin Core JSONRPC API has fields marked as 'numeric'. It is not obvious what Rust
@@ -191,7 +193,7 @@ pub struct ScriptPubkey {
191193
pub hex: String,
192194
/// Number of required signatures - deprecated in Core v22.
193195
///
194-
/// Only returned before in versions prior to 22 or for version 22 onwards if
196+
/// Only returned in versions prior to 22 or for version 22 onwards if
195197
/// config option `-deprecatedrpc=addresses` is passed.
196198
#[serde(rename = "reqSigs")]
197199
pub required_signatures: Option<i64>,
@@ -202,11 +204,45 @@ pub struct ScriptPubkey {
202204
pub address: Option<String>,
203205
/// Array of bitcoin addresses - deprecated in Core v22.
204206
///
205-
/// Only returned before in versions prior to 22 or for version 22 onwards if
207+
/// Only returned in versions prior to 22 or for version 22 onwards if
206208
/// config option `-deprecatedrpc=addresses` is passed.
207209
pub addresses: Option<Vec<String>>,
208210
}
209211

212+
/// Error when converting a `ScriptPubkey` type into the model type.
213+
#[derive(Debug)]
214+
pub enum ScriptPubkeyError {
215+
/// Conversion of the `hex` field failed.
216+
Hex(hex::HexToBytesError),
217+
/// Conversion of the `address` field failed.
218+
Address(address::ParseError),
219+
/// Conversion of the `addresses` field failed.
220+
Addresses(address::ParseError),
221+
}
222+
223+
impl fmt::Display for ScriptPubkeyError {
224+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225+
use ScriptPubkeyError::*;
226+
match *self {
227+
Hex(ref e) => write_err!(f, "conversion of the `hex` field failed"; e),
228+
Address(ref e) => write_err!(f, "conversion of the `address` field failed"; e),
229+
Addresses(ref e) => write_err!(f, "conversion of the `addresses` field failed"; e),
230+
}
231+
}
232+
}
233+
234+
#[cfg(feature = "std")]
235+
impl std::error::Error for ScriptPubkeyError {
236+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
237+
use ScriptPubkeyError::*;
238+
match *self {
239+
Hex(ref e) => Some(e),
240+
Address(ref e) => Some(e),
241+
Addresses(ref e) => Some(e),
242+
}
243+
}
244+
}
245+
210246
impl ScriptPubkey {
211247
fn script_buf(&self) -> Result<ScriptBuf, hex::HexToBytesError> {
212248
ScriptBuf::from_hex(&self.hex)
@@ -215,6 +251,32 @@ impl ScriptPubkey {
215251
fn address(&self) -> Option<Result<Address<NetworkUnchecked>, address::ParseError>> {
216252
self.address.as_ref().map(|addr| addr.parse::<Address<_>>())
217253
}
254+
255+
/// Converts version specific type to a version nonspecific, more strongly typed type.
256+
pub fn into_model(self) -> Result<model::ScriptPubkey, ScriptPubkeyError> {
257+
use ScriptPubkeyError as E;
258+
259+
let script_pubkey = ScriptBuf::from_hex(&self.hex).map_err(E::Hex)?;
260+
261+
let address =
262+
self.address.map(|s| s.parse::<Address<_>>().map_err(E::Address)).transpose()?;
263+
264+
let addresses = self
265+
.addresses
266+
.map(|v| {
267+
v.into_iter()
268+
.map(|s| s.parse::<Address<_>>().map_err(E::Addresses))
269+
.collect::<Result<Vec<_>, _>>()
270+
})
271+
.transpose()?;
272+
273+
Ok(model::ScriptPubkey {
274+
script_pubkey,
275+
required_signatures: self.required_signatures,
276+
address,
277+
addresses,
278+
})
279+
}
218280
}
219281

220282
/// Data returned by Core for a script signature.

types/src/model/blockchain.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use bitcoin::{
1515
};
1616
use serde::{Deserialize, Serialize};
1717

18-
use crate::ScriptPubkey;
18+
use super::ScriptPubkey;
1919

2020
/// Models the result of JSON-RPC method `dumptxoutset`.
2121
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]

types/src/model/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ mod util;
1818
mod wallet;
1919
mod zmq;
2020

21+
use bitcoin::address::NetworkUnchecked;
22+
use bitcoin::{Address, ScriptBuf};
23+
use serde::{Deserialize, Serialize};
24+
2125
#[doc(inline)]
2226
pub use self::{
2327
blockchain::{
@@ -65,3 +69,26 @@ pub use self::{
6569
UnloadWallet, WalletCreateFundedPsbt, WalletDisplayAddress, WalletProcessPsbt,
6670
},
6771
};
72+
73+
/// Models the data returned by Core for a scriptPubKey.
74+
///
75+
/// This is used by methods in the blockchain section and in the raw transaction section (i.e raw
76+
/// transaction and psbt methods).
77+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
78+
#[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))]
79+
pub struct ScriptPubkey {
80+
/// The script_pubkey parsed from hex.
81+
pub script_pubkey: ScriptBuf,
82+
/// Number of required signatures - deprecated in Core v22.
83+
///
84+
/// Only returned in versions prior to 22 or for version 22 onwards if
85+
/// config option `-deprecatedrpc=addresses` is passed.
86+
pub required_signatures: Option<i64>,
87+
/// Bitcoin address (only if a well-defined address exists).
88+
pub address: Option<Address<NetworkUnchecked>>,
89+
/// Array of bitcoin addresses - deprecated in Core v22.
90+
///
91+
/// Only returned in versions prior to 22 or for version 22 onwards if
92+
/// config option `-deprecatedrpc=addresses` is passed.
93+
pub addresses: Option<Vec<Address<NetworkUnchecked>>>,
94+
}

types/src/v29/blockchain/error.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use bitcoin::hex::HexToBytesError;
88
use bitcoin::{address, amount, hex, network};
99

1010
use crate::error::write_err;
11-
use crate::NumericError;
11+
use crate::{NumericError, ScriptPubkeyError};
1212

1313
/// Error when converting a `GetBlockVerboseOne` type into the model type.
1414
#[derive(Debug)]
@@ -295,6 +295,10 @@ pub enum GetDescriptorActivityError {
295295
/// We wrap the inner error to provide context. This might not be strictly necessary
296296
/// if the inner errors are distinct enough, but can be helpful.
297297
ActivityEntry(Box<GetDescriptorActivityError>), // Use Box to avoid recursive type size issues
298+
/// Conversion of the `prevout_spk` field failed.
299+
PrevoutSpk(ScriptPubkeyError),
300+
/// Conversion of the `output_spk` field failed.
301+
OutputSpk(ScriptPubkeyError),
298302
}
299303

300304
impl fmt::Display for GetDescriptorActivityError {
@@ -308,6 +312,8 @@ impl fmt::Display for GetDescriptorActivityError {
308312
Script(ref e) => write_err!(f, "conversion of the script `hex` field failed"; e),
309313
Address(ref e) => write_err!(f, "conversion of the `address` field failed"; e),
310314
ActivityEntry(ref e) => write_err!(f, "conversion of an activity entry failed"; e),
315+
PrevoutSpk(ref e) => write_err!(f, "conversion of the `prevout_spk` field failed"; e),
316+
OutputSpk(ref e) => write_err!(f, "conversion of the `output_spk` field failed"; e),
311317
}
312318
}
313319
}
@@ -324,6 +330,8 @@ impl std::error::Error for GetDescriptorActivityError {
324330
Script(ref e) => Some(e),
325331
Address(ref e) => Some(e),
326332
ActivityEntry(ref e) => Some(&**e), // Deref the Box to get the inner error
333+
PrevoutSpk(ref e) => Some(e),
334+
OutputSpk(ref e) => Some(e),
327335
}
328336
}
329337
}

types/src/v29/blockchain/into.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ impl GetDescriptorActivity {
225225
spend.height.map(|h| crate::to_u32(h, "height")).transpose()?;
226226
let spend_txid = Txid::from_str(&spend.spend_txid).map_err(E::Hash)?;
227227
let prevout_txid = Txid::from_str(&spend.prevout_txid).map_err(E::Hash)?;
228+
let prevout_spk = spend.prevout_spk.into_model().map_err(E::PrevoutSpk)?;
228229

229230
Ok(model::ActivityEntry::Spend(model::SpendActivity {
230231
amount,
@@ -234,7 +235,7 @@ impl GetDescriptorActivity {
234235
spend_vout: spend.spend_vout,
235236
prevout_txid,
236237
prevout_vout: spend.prevout_vout,
237-
prevout_spk: spend.prevout_spk,
238+
prevout_spk,
238239
}))
239240
}
240241
ActivityEntry::Receive(receive) => {
@@ -247,14 +248,15 @@ impl GetDescriptorActivity {
247248
let height =
248249
receive.height.map(|h| crate::to_u32(h, "height")).transpose()?; // Uses From<NumericError>
249250
let txid = Txid::from_str(&receive.txid).map_err(E::Hash)?;
251+
let output_spk = receive.output_spk.into_model().map_err(E::OutputSpk)?;
250252

251253
Ok(model::ActivityEntry::Receive(model::ReceiveActivity {
252254
amount,
253255
block_hash,
254256
height,
255257
txid,
256258
vout: receive.vout,
257-
output_spk: receive.output_spk,
259+
output_spk,
258260
}))
259261
}
260262
}

0 commit comments

Comments
 (0)