bitcoind_json_rpc_types/
lib.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Types returned by the JSON-RPC API of Bitcoin Core.
4
5/// Re-export the `rust-bitcoin` crate.
6pub extern crate bitcoin;
7
8// TODO: Consider updating https://en.bitcoin.it/wiki/API_reference_%28JSON-RPC%29 when this is complete.
9
10// JSON types, for each specific version of `bitcoind`.
11pub mod v17;
12pub mod v18;
13pub mod v19;
14pub mod v20;
15pub mod v21;
16pub mod v22;
17pub mod v23;
18pub mod v24;
19pub mod v25;
20pub mod v26;
21pub mod v27;
22pub mod v28;
23
24// JSON types that model _all_ `bitcoind` versions.
25pub mod model;
26
27use std::fmt;
28
29use bitcoin::amount::ParseAmountError;
30use bitcoin::{Amount, FeeRate};
31
32/// Converts an `i64` numeric type to a `u32`.
33///
34/// The Bitcoin Core JSONRPC API has fields marked as 'numeric'. It is not obvious what Rust
35/// type these fields should be.
36///
37/// We want the version specific JSON types to just work (TM).
38///
39/// 1. We use an `i64` because its the biggest signed integer on "common" machines.
40/// 2. We use a signed integer because Core sometimes returns -1.
41///
42/// (2) was discovered in the wild but is hard to test for.
43pub fn to_u32(value: i64, field: &str) -> Result<u32, NumericError> {
44    if value.is_negative() {
45        return Err(NumericError::Negative { value, field: field.to_owned() });
46    }
47    u32::try_from(value).map_err(|_| NumericError::Overflow { value, field: field.to_owned() })
48}
49
50/// Error converting an `i64` to a `u32`.
51///
52/// If we expect a numeric value to sanely fit inside a `u32` we use that type in the `model`
53/// module, this requires converting the `i64` returned by the JSONRPC API into a `u32`, if our
54/// expectations are not met this error will be encountered.
55#[derive(Debug)]
56pub enum NumericError {
57    /// Expected an unsigned numeric value however the value was negative.
58    Negative { field: String, value: i64 },
59    /// A value larger than `u32::MAX` was unexpectedly encountered.
60    Overflow { field: String, value: i64 },
61}
62
63impl fmt::Display for NumericError {
64    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        use NumericError::*;
66
67        match *self {
68            Negative{ ref field, value } => write!(f, "expected an unsigned numeric value however the value was negative (field name: {} value: {})", field, value),
69            Overflow { ref field, value } => write!(f, "a  value larger than `u32::MAX` was unexpectedly encountered (field name: {} Value: {})", field, value),
70        }
71    }
72}
73
74impl std::error::Error for NumericError {}
75
76/// Converts `fee_rate` in BTC/kB to `FeeRate`.
77fn btc_per_kb(btc_per_kb: f64) -> Result<Option<FeeRate>, ParseAmountError> {
78    let btc_per_byte = btc_per_kb / 1000_f64;
79    let sats_per_byte = Amount::from_btc(btc_per_byte)?;
80
81    // Virtual bytes equal bytes before segwit.
82    let rate = FeeRate::from_sat_per_vb(sats_per_byte.to_sat());
83
84    Ok(rate)
85}