corepc_types/
lib.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Types returned by the JSON-RPC API of Bitcoin Core.
4//!
5//! Each type has rustdocs copied from Core, bugs and all. Additional docs were only added if things
6//! really didn't make sense. Only required arguments are documented. To see what optional arguments
7//! are available run `bitcoin-cli help <method>` against the version of Core you are interested in.
8
9/// Re-export the `rust-bitcoin` crate.
10pub extern crate bitcoin;
11
12extern crate alloc;
13
14// TODO: Consider updating https://en.bitcoin.it/wiki/API_reference_%28JSON-RPC%29 when this is complete.
15
16mod error;
17mod psbt;
18
19// JSON types, for each specific version of `bitcoind`.
20pub mod v17;
21pub mod v18;
22pub mod v19;
23pub mod v20;
24pub mod v21;
25pub mod v22;
26pub mod v23;
27pub mod v24;
28pub mod v25;
29pub mod v26;
30pub mod v27;
31pub mod v28;
32pub mod v29;
33
34// JSON types that model _all_ `bitcoind` versions.
35pub mod model;
36
37use core::fmt;
38
39use bitcoin::address::{self, Address, NetworkUnchecked};
40use bitcoin::amount::ParseAmountError;
41use bitcoin::hex::{self, FromHex as _};
42use bitcoin::{Amount, FeeRate, ScriptBuf, Witness};
43use serde::{Deserialize, Serialize};
44
45use crate::error::write_err;
46
47/// Converts an `i64` numeric type to a `u32`.
48///
49/// The Bitcoin Core JSONRPC API has fields marked as 'numeric'. It is not obvious what Rust
50/// type these fields should be.
51///
52/// We want the version specific JSON types to just work (TM).
53///
54/// 1. We use an `i64` because its the biggest signed integer on "common" machines.
55/// 2. We use a signed integer because Core sometimes returns -1.
56///
57/// (2) was discovered in the wild but is hard to test for.
58pub fn to_u32(value: i64, field: &str) -> Result<u32, NumericError> {
59    if value.is_negative() {
60        return Err(NumericError::Negative { value, field: field.to_owned() });
61    }
62    u32::try_from(value).map_err(|_| NumericError::Overflow { value, field: field.to_owned() })
63}
64
65/// Error converting an `i64` to a `u32`.
66///
67/// If we expect a numeric value to sanely fit inside a `u32` we use that type in the `model`
68/// module, this requires converting the `i64` returned by the JSONRPC API into a `u32`, if our
69/// expectations are not met this error will be encountered.
70#[derive(Debug)]
71pub enum NumericError {
72    /// Expected an unsigned numeric value however the value was negative.
73    Negative { field: String, value: i64 },
74    /// A value larger than `u32::MAX` was unexpectedly encountered.
75    Overflow { field: String, value: i64 },
76}
77
78impl fmt::Display for NumericError {
79    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80        use NumericError::*;
81
82        match *self {
83            Negative{ ref field, value } => write!(f, "expected an unsigned numeric value however the value was negative (field name: {} value: {})", field, value),
84            Overflow { ref field, value } => write!(f, "a value larger than `u32::MAX` was unexpectedly encountered (field name: {} Value: {})", field, value),
85        }
86    }
87}
88
89#[cfg(feature = "std")]
90impl std::error::Error for NumericError {}
91
92/// Converts `fee_rate` in BTC/kB to `FeeRate`.
93fn btc_per_kb(btc_per_kb: f64) -> Result<Option<FeeRate>, ParseAmountError> {
94    let sats_per_kb = Amount::from_btc(btc_per_kb)?;
95    let sats_per_byte = sats_per_kb.to_sat() / 1000;
96
97    // Virtual bytes equal bytes before segwit.
98    let rate = FeeRate::from_sat_per_vb(sats_per_byte);
99
100    Ok(rate)
101}
102
103// TODO: Remove this function if a new `Witness` constructor gets added.
104// https://github.com/rust-bitcoin/rust-bitcoin/issues/4350
105fn witness_from_hex_slice<T: AsRef<str>>(witness: &[T]) -> Result<Witness, hex::HexToBytesError> {
106    let bytes: Vec<Vec<u8>> =
107        witness.iter().map(|hex| Vec::from_hex(hex.as_ref())).collect::<Result<_, _>>()?;
108    Ok(Witness::from_slice(&bytes))
109}
110
111/// Gets the compact size encoded value from `slice` and moves slice past the encoding.
112///
113/// Caller to guarantee that the encoding is well formed. Well formed is defined as:
114///
115/// * Being at least long enough.
116/// * Containing a minimal encoding.
117///
118/// # Panics
119///
120/// * Panics in release mode if the `slice` does not contain a valid minimal compact size encoding.
121/// * Panics in debug mode if the encoding is not minimal (referred to as "non-canonical" in Core).
122// This is copied from the `bitcoin-internals::compact_size` module.
123pub fn compact_size_decode(slice: &mut &[u8]) -> u64 {
124    if slice.is_empty() {
125        panic!("tried to decode an empty slice");
126    }
127
128    match slice[0] {
129        0xFF => {
130            const SIZE: usize = 9;
131            if slice.len() < SIZE {
132                panic!("slice too short, expected at least 9 bytes");
133            };
134
135            let mut bytes = [0_u8; SIZE - 1];
136            bytes.copy_from_slice(&slice[1..SIZE]);
137
138            let v = u64::from_le_bytes(bytes);
139            debug_assert!(v > u32::MAX.into(), "non-minimal encoding of a u64");
140            *slice = &slice[SIZE..];
141            v
142        }
143        0xFE => {
144            const SIZE: usize = 5;
145            if slice.len() < SIZE {
146                panic!("slice too short, expected at least 5 bytes");
147            };
148
149            let mut bytes = [0_u8; SIZE - 1];
150            bytes.copy_from_slice(&slice[1..SIZE]);
151
152            let v = u32::from_le_bytes(bytes);
153            debug_assert!(v > u16::MAX.into(), "non-minimal encoding of a u32");
154            *slice = &slice[SIZE..];
155            u64::from(v)
156        }
157        0xFD => {
158            const SIZE: usize = 3;
159            if slice.len() < SIZE {
160                panic!("slice too short, expected at least 3 bytes");
161            };
162
163            let mut bytes = [0_u8; SIZE - 1];
164            bytes.copy_from_slice(&slice[1..SIZE]);
165
166            let v = u16::from_le_bytes(bytes);
167            debug_assert!(v >= 0xFD, "non-minimal encoding of a u16");
168            *slice = &slice[SIZE..];
169            u64::from(v)
170        }
171        n => {
172            *slice = &slice[1..];
173            u64::from(n)
174        }
175    }
176}
177
178/// Data returned by Core for a script pubkey.
179///
180/// This is used by methods in the blockchain section and in the raw transaction section (i.e raw
181/// transaction and psbt methods). The shape changed in Core v22 but the new shape is fully
182/// backwards compatible so we only provide it not a v0.17 specific type. The `mtype::ScriptPubkey`
183/// mirrors this design (but with concrete `rust-bitcoin` types).
184#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
185#[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))]
186pub struct ScriptPubkey {
187    /// Script assembly.
188    pub asm: String,
189    /// Inferred descriptor for the output. v23 and later only.
190    #[serde(rename = "desc")]
191    pub descriptor: Option<String>,
192    /// Script hex.
193    pub hex: String,
194    /// Number of required signatures - deprecated in Core v22.
195    ///
196    /// Only returned in versions prior to 22 or for version 22 onwards if
197    /// config option `-deprecatedrpc=addresses` is passed.
198    #[serde(rename = "reqSigs")]
199    pub required_signatures: Option<i64>,
200    /// The type, eg pubkeyhash.
201    #[serde(rename = "type")]
202    pub type_: String,
203    /// Bitcoin address (only if a well-defined address exists).
204    pub address: Option<String>,
205    /// Array of bitcoin addresses - deprecated in Core v22.
206    ///
207    /// Only returned in versions prior to 22 or for version 22 onwards if
208    /// config option `-deprecatedrpc=addresses` is passed.
209    pub addresses: Option<Vec<String>>,
210}
211
212/// Error when converting a `ScriptPubkey` type into the model type.
213#[derive(Debug)]
214pub 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
223impl 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")]
235impl 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
246impl ScriptPubkey {
247    fn script_buf(&self) -> Result<ScriptBuf, hex::HexToBytesError> {
248        ScriptBuf::from_hex(&self.hex)
249    }
250
251    fn address(&self) -> Option<Result<Address<NetworkUnchecked>, address::ParseError>> {
252        self.address.as_ref().map(|addr| addr.parse::<Address<_>>())
253    }
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    }
280}
281
282/// Data returned by Core for a script signature.
283#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
284#[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))]
285pub struct ScriptSig {
286    /// Assembly representation of the script.
287    pub asm: String,
288    /// Hex representation of the script.
289    pub hex: String,
290}
291
292impl ScriptSig {
293    pub fn script_buf(&self) -> Result<ScriptBuf, hex::HexToBytesError> {
294        ScriptBuf::from_hex(&self.hex)
295    }
296}