bitcoind_request/command/
get_block_stats.rs

1/*
2getblockstats hash_or_height ( stats )
3
4Compute per block statistics for a given window. All amounts are in satoshis.
5It won't work for some heights with pruning.
6
7Arguments:
81. hash_or_height    (string or numeric, required) The block hash or height of the target block
92. stats             (json array, optional, default=all values) Values to plot (see result below)
10     [
11       "height",     (string) Selected statistic
12       "time",       (string) Selected statistic
13       ...
14     ]
15
16Result:
17{                              (json object)
18  "avgfee" : n,                (numeric) Average fee in the block
19  "avgfeerate" : n,            (numeric) Average feerate (in satoshis per virtual byte)
20  "avgtxsize" : n,             (numeric) Average transaction size
21  "blockhash" : "hex",         (string) The block hash (to check for potential reorgs)
22  "feerate_percentiles" : [    (json array) Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)
23    n,                         (numeric) The 10th percentile feerate
24    n,                         (numeric) The 25th percentile feerate
25    n,                         (numeric) The 50th percentile feerate
26    n,                         (numeric) The 75th percentile feerate
27    n                          (numeric) The 90th percentile feerate
28  ],
29  "height" : n,                (numeric) The height of the block
30  "ins" : n,                   (numeric) The number of inputs (excluding coinbase)
31  "maxfee" : n,                (numeric) Maximum fee in the block
32  "maxfeerate" : n,            (numeric) Maximum feerate (in satoshis per virtual byte)
33  "maxtxsize" : n,             (numeric) Maximum transaction size
34  "medianfee" : n,             (numeric) Truncated median fee in the block
35  "mediantime" : n,            (numeric) The block median time past
36  "mediantxsize" : n,          (numeric) Truncated median transaction size
37  "minfee" : n,                (numeric) Minimum fee in the block
38  "minfeerate" : n,            (numeric) Minimum feerate (in satoshis per virtual byte)
39  "mintxsize" : n,             (numeric) Minimum transaction size
40  "outs" : n,                  (numeric) The number of outputs
41  "subsidy" : n,               (numeric) The block subsidy
42  "swtotal_size" : n,          (numeric) Total size of all segwit transactions
43  "swtotal_weight" : n,        (numeric) Total weight of all segwit transactions
44  "swtxs" : n,                 (numeric) The number of segwit transactions
45  "time" : n,                  (numeric) The block time
46  "total_out" : n,             (numeric) Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])
47  "total_size" : n,            (numeric) Total size of all non-coinbase transactions
48  "total_weight" : n,          (numeric) Total weight of all non-coinbase transactions
49  "totalfee" : n,              (numeric) The fee total
50  "txs" : n,                   (numeric) The number of transactions (including coinbase)
51  "utxo_increase" : n,         (numeric) The increase/decrease in the number of unspent outputs
52  "utxo_size_inc" : n          (numeric) The increase/decrease in size for the utxo index (not discounting op_return and similar)
53}
54
55Examples:
56> bitcoin-cli getblockstats '"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]'
57> bitcoin-cli getblockstats 1000 '["minfeerate","avgfeerate"]'
58> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblockstats", "params": ["00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", ["minfeerate","avgfeerate"]]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
59> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblockstats", "params": [1000, ["minfeerate","avgfeerate"]]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
60 */
61use serde::{Deserialize, Serialize};
62use serde_json::value::to_raw_value;
63use std::fmt;
64
65use crate::{client::Client, command::CallableCommand, Blockhash};
66
67use super::request::request;
68
69type BlockHeight = u64;
70pub enum TargetBlockArgument {
71    Hash(Blockhash),
72    Height(BlockHeight),
73}
74// TODO: Fill in all of these:
75//     https://bitcoincore.org/en/doc/0.21.0/rpc/blockchain/getblockstats/
76#[derive(Serialize, Deserialize, Debug)]
77pub enum StatsArgumentChoices {
78    AvgFee,
79    AvgTxSize,
80    Blockhash,
81    FeeRatePercentiles,
82    Height,
83    Ins,
84    MaxFee,
85    MaxFeeRate,
86    MaxTxSize,
87    MedianFee,
88    MedianTime,
89    MedianTxSize,
90    MinFee,
91    MinFeeRate,
92    MinTxSize,
93    Outs,
94    Subsidy,
95    SwTotalSize,
96    SwTotalWeight,
97    SwTxs,
98    Time,
99    TotalOut,
100    TotalSize,
101    TotalWeight,
102    TotalFee,
103    Txs,
104    UtxoIncrease,
105    UtxoSizeInc,
106}
107impl fmt::Display for StatsArgumentChoices {
108    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109        match self {
110            StatsArgumentChoices::AvgFee => write!(f, "avgfee"),
111            StatsArgumentChoices::AvgTxSize => write!(f, "avgtxsize"),
112            StatsArgumentChoices::Blockhash => write!(f, "blockhash"),
113            StatsArgumentChoices::FeeRatePercentiles => write!(f, "feerate_percentiles"),
114            StatsArgumentChoices::Height => write!(f, "height"),
115            StatsArgumentChoices::Ins => write!(f, "ins"),
116            StatsArgumentChoices::MaxFee => write!(f, "maxfee"),
117            StatsArgumentChoices::MaxFeeRate => write!(f, "maxfeerate"),
118            StatsArgumentChoices::MaxTxSize => write!(f, "maxtxsize"),
119            StatsArgumentChoices::MedianFee => write!(f, "medianfee"),
120            StatsArgumentChoices::MedianTime => write!(f, "mediantime"),
121            StatsArgumentChoices::MedianTxSize => write!(f, "mediantxsize"),
122            StatsArgumentChoices::MinFee => write!(f, "minfee"),
123            StatsArgumentChoices::MinFeeRate => write!(f, "minfeerate"),
124            StatsArgumentChoices::MinTxSize => write!(f, "mintxsize"),
125            StatsArgumentChoices::Outs => write!(f, "outs"),
126            StatsArgumentChoices::Subsidy => write!(f, "subsidy"),
127            StatsArgumentChoices::SwTotalSize => write!(f, "swtotal_size"),
128            StatsArgumentChoices::SwTotalWeight => write!(f, "swtotal_weight"),
129            StatsArgumentChoices::SwTxs => write!(f, "swtxs"),
130            StatsArgumentChoices::Time => write!(f, "time"),
131            StatsArgumentChoices::TotalOut => write!(f, "total_out"),
132            StatsArgumentChoices::TotalSize => write!(f, "total_size"),
133            StatsArgumentChoices::TotalWeight => write!(f, "total_weight"),
134            StatsArgumentChoices::TotalFee => write!(f, "totalfee"),
135            StatsArgumentChoices::Txs => write!(f, "txs"),
136            StatsArgumentChoices::UtxoIncrease => write!(f, "utxo_increase"),
137            StatsArgumentChoices::UtxoSizeInc => write!(f, "utxo_size_inc"),
138        }
139    }
140}
141pub struct GetBlockStatsCommand {
142    target_block: TargetBlockArgument,
143    stats: Vec<StatsArgumentChoices>,
144}
145
146impl GetBlockStatsCommand {
147    pub fn new(target_block: TargetBlockArgument) -> Self {
148        GetBlockStatsCommand {
149            target_block,
150            stats: vec![],
151        }
152    }
153    pub fn add_selective_stats(
154        &mut self,
155        stats_argument_choices: Vec<StatsArgumentChoices>,
156    ) -> &Self {
157        self.stats = stats_argument_choices;
158        self
159    }
160}
161#[derive(Serialize, Deserialize, Debug)]
162pub struct GetBlockStatsCommandWithSelectiveStatsResponse {
163    pub avgfee: Option<u64>,                   // Average fee in the block
164    pub avgfeerate: Option<u64>,               // Average feerate (in satoshis per virtual byte)
165    pub avgtxsize: Option<u64>,                // Average transaction size
166    pub blockhash: Option<String>, // "hex" The block hash (to check for potential reorgs)
167    pub feerate_percentiles: Option<[u64; 5]>, //  Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)
168    //  index 0,                         (numeric) The 10th percentile feerate
169    //  index 1                        (numeric) The 25th percentile feerate
170    //  index 2                         (numeric) The 50th percentile feerate
171    //  index 3,                         (numeric) The 75th percentile feerate
172    //  index 4                         (numeric) The 90th percentile feerate
173    pub height: Option<u64>,         // The height of the block
174    pub ins: Option<u64>,            // The number of inputs (excluding coinbase)
175    pub maxfee: Option<u64>,         // Maximum fee in the block
176    pub maxfeerate: Option<u64>,     // Maximum feerate (in satoshis per virtual byte)
177    pub maxtxsize: Option<u64>,      // Maximum transaction size
178    pub medianfee: Option<u64>,      //Truncated median fee in the block
179    pub mediantime: Option<u64>,     // The block median time past
180    pub mediantxsize: Option<u64>,   // Truncated median transaction size
181    pub minfee: Option<u64>,         // Minimum fee in the block
182    pub minfeerate: Option<u64>,     // Minimum feerate (in satoshis per virtual byte)
183    pub mintxsize: Option<u64>,      // Minimum transaction size
184    pub outs: Option<u64>,           // The number of outputs
185    pub subsidy: Option<u64>,        // The block subsidy
186    pub swtotal_size: Option<u64>,   // Total size of all segwit transactions
187    pub swtotal_weight: Option<u64>, // Total weight of all segwit transactions
188    pub swtxs: Option<u64>,          // The number of segwit transactions
189    pub time: Option<u64>,           // The block time
190    pub total_out: Option<u64>, // Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])
191    pub total_size: Option<u64>, // Total size of all non-coinbase transactions
192    pub total_weight: Option<u64>, // Total weight of all non-coinbase transactions
193    pub totalfee: Option<u64>,  // The fee total
194    pub txs: Option<u64>,       // The number of transactions (including coinbase)
195    pub utxo_increase: Option<u64>, // The increase/decrease in the number of unspent outputs
196    pub utxo_size_inc: Option<u64>, // The increase/decrease in size for the utxo index (not discounting op_return and similar)
197}
198
199#[derive(Serialize, Deserialize, Debug)]
200pub struct GetBlockStatsCommandWithAllStatsResponse {
201    pub avgfee: u64,                   // Average fee in the block
202    pub avgfeerate: u64,               // Average feerate (in satoshis per virtual byte)
203    pub avgtxsize: u64,                // Average transaction size
204    pub blockhash: String,             // "hex" The block hash (to check for potential reorgs)
205    pub feerate_percentiles: [u64; 5], //  Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)
206    //  index 0,                         (numeric) The 10th percentile feerate
207    //  index 1                        (numeric) The 25th percentile feerate
208    //  index 2                         (numeric) The 50th percentile feerate
209    //  index 3,                         (numeric) The 75th percentile feerate
210    //  index 4                         (numeric) The 90th percentile feerate
211    pub height: u64,         // The height of the block
212    pub ins: u64,            // The number of inputs (excluding coinbase)
213    pub maxfee: u64,         // Maximum fee in the block
214    pub maxfeerate: u64,     // Maximum feerate (in satoshis per virtual byte)
215    pub maxtxsize: u64,      // Maximum transaction size
216    pub medianfee: u64,      //Truncated median fee in the block
217    pub mediantime: u64,     // The block median time past
218    pub mediantxsize: u64,   // Truncated median transaction size
219    pub minfee: u64,         // Minimum fee in the block
220    pub minfeerate: u64,     // Minimum feerate (in satoshis per virtual byte)
221    pub mintxsize: u64,      // Minimum transaction size
222    pub outs: u64,           // The number of outputs
223    pub subsidy: u64,        // The block subsidy
224    pub swtotal_size: u64,   // Total size of all segwit transactions
225    pub swtotal_weight: u64, // Total weight of all segwit transactions
226    pub swtxs: u64,          // The number of segwit transactions
227    pub time: u64,           // The block time
228    pub total_out: u64, // Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])
229    pub total_size: u64, // Total size of all non-coinbase transactions
230    pub total_weight: u64, // Total weight of all non-coinbase transactions
231    pub totalfee: u64,  // The fee total
232    pub txs: u64,       // The number of transactions (including coinbase)
233    pub utxo_increase: i64, // The increase/decrease in the number of unspent outputs
234    pub utxo_size_inc: i64, // The increase/decrease in size for the utxo index (not discounting op_return and similar)
235}
236
237#[derive(Serialize, Deserialize, Debug)]
238#[serde(rename_all = "camelCase")]
239#[serde(untagged)]
240pub enum GetBlockStatsCommandResponse {
241    SelectiveStats(GetBlockStatsCommandWithSelectiveStatsResponse),
242    AllStats(GetBlockStatsCommandWithAllStatsResponse),
243}
244
245impl CallableCommand for GetBlockStatsCommand {
246    type Response = GetBlockStatsCommandResponse;
247    fn call(&self, client: &Client) -> Result<Self::Response, jsonrpc::Error> {
248        let target_block = &self.target_block;
249        let hash_or_height_arg_raw_value = match target_block {
250            TargetBlockArgument::Hash(hash) => to_raw_value(&hash).unwrap(),
251            TargetBlockArgument::Height(height) => to_raw_value(&height).unwrap(),
252        };
253
254        // TODO: Add stats param!
255        let stats_arg: Vec<String> = self.stats.iter().map(|stat| stat.to_string()).collect();
256        let stats_arg_raw_value = to_raw_value(&stats_arg).unwrap();
257        let command = "getblockstats";
258        let params = vec![hash_or_height_arg_raw_value, stats_arg_raw_value];
259        let r = request(client, command, params);
260        let response: GetBlockStatsCommandResponse = if stats_arg.is_empty() {
261            GetBlockStatsCommandResponse::AllStats(r.result()?)
262        } else {
263            GetBlockStatsCommandResponse::SelectiveStats(r.result()?)
264        };
265        Ok(response)
266    }
267}