bitcoin_node_query/
lib.rs

1#![allow(unused_imports)]
2use bitcoin_address::is_segwit_v0;
3use bitcoin_transaction_utils::is_transaction_hex_segwit;
4use bitcoind_request::command::{
5    get_best_block_hash::GetBestBlockHashCommand,
6    get_block::{
7        self, CoinbaseVin, DecodeRawTransactionResponse, GetBlockCommand, GetBlockCommandResponse,
8        GetBlockCommandTransactionResponse, GetBlockCommandVerbosity, Vout,
9    },
10    get_block_count::{self, GetBlockCountCommand},
11    get_block_hash::GetBlockHashCommand,
12    get_block_header::GetBlockHeaderCommand,
13    get_block_stats::{
14        GetBlockStatsCommand, GetBlockStatsCommandResponse,
15        GetBlockStatsCommandWithAllStatsResponse, GetBlockStatsCommandWithSelectiveStatsResponse,
16        StatsArgumentChoices, TargetBlockArgument,
17    },
18    get_blockchain_info::GetBlockchainInfoCommand,
19    get_chain_tips::GetChainTipsCommand,
20    get_chain_tx_stats::GetChainTxStatsCommand,
21    get_difficulty::GetDifficultyCommand,
22    get_mining_info::GetMiningInfoCommand,
23    get_network_hash_ps::GetNetworkHashPsCommand,
24    get_raw_transaction::{GetRawTransactionCommand, GetRawTransactionCommandResponse, Vin},
25    get_tx_out::GetTxOutCommand,
26    get_tx_out_set_info::GetTxOutSetInfoCommand,
27    CallableCommand,
28};
29
30use bitcoind_request::{Blockhash, BlockhashHexEncoded};
31mod client;
32
33use chrono::{DateTime, Duration, TimeZone, Timelike, Utc};
34pub use client::Client;
35use jsonrpc::simple_http::{self, SimpleHttpTransport};
36use std::{env, time::SystemTimeError};
37
38const BLOCKS_PER_DIFFICULTY_PERIOD: u64 = 2016;
39
40pub struct Seconds(pub i64);
41
42fn timestamp_is_from_more_than_24_hours_ago(timestamp: i64) -> bool {
43    let hour_window_to_calculate_for = 24;
44
45    let current_datetime = chrono::offset::Utc::now();
46    let window_to_cacluate_duration_in_seconds =
47        Duration::seconds(60 * 60 * hour_window_to_calculate_for);
48    let datetime_24_hours_ago = current_datetime - window_to_cacluate_duration_in_seconds;
49    let datetime_of_block = Utc.timestamp(timestamp as i64, 0);
50    datetime_of_block < datetime_24_hours_ago
51}
52
53fn timestamp_is_from_more_than_90_days_ago(timestamp: i64) -> bool {
54    let hour_window_to_calculate_for = 24 * 90;
55
56    let current_datetime = chrono::offset::Utc::now();
57    let window_to_cacluate_duration_in_seconds =
58        Duration::seconds(60 * 60 * hour_window_to_calculate_for);
59    let datetime_90_days_ago = current_datetime - window_to_cacluate_duration_in_seconds;
60    let datetime_of_block = Utc.timestamp(timestamp as i64, 0);
61    datetime_of_block < datetime_90_days_ago
62}
63
64pub fn get_block_height(client: &Client) -> u64 {
65    let client = &client.bitcoind_request_client;
66    let block_count = GetBlockCountCommand::new().call(client);
67    return block_count.unwrap().0;
68}
69
70pub fn get_time_since_last_block_in_seconds(client: &Client) -> i64 {
71    let client = &client.bitcoind_request_client;
72    let block_count = GetBlockCountCommand::new().call(client);
73    let arg = TargetBlockArgument::Height(block_count.unwrap().0);
74    let block_stats_response = GetBlockStatsCommand::new(arg).call(client);
75    let time_of_last_block = match block_stats_response.unwrap() {
76        GetBlockStatsCommandResponse::AllStats(response) => response.time,
77        GetBlockStatsCommandResponse::SelectiveStats(response) => response.time.unwrap(),
78    };
79    let current_datetime = chrono::offset::Utc::now();
80    let datetime_of_last_block = Utc.timestamp(time_of_last_block as i64, 0);
81    let difference = current_datetime.signed_duration_since(datetime_of_last_block);
82    difference.num_seconds()
83}
84
85pub fn get_average_block_time_for_last_2016_blocks(client: &Client) -> u64 {
86    let client = &client.bitcoind_request_client;
87    let block_height = GetBlockCountCommand::new().call(client).unwrap();
88    let block_stats_response =
89        GetBlockStatsCommand::new(TargetBlockArgument::Height(block_height.0)).call(client);
90    let time_of_most_recent_block = match block_stats_response.unwrap() {
91        GetBlockStatsCommandResponse::AllStats(response) => response.time,
92        GetBlockStatsCommandResponse::SelectiveStats(response) => response.time.unwrap(),
93    };
94
95    let block_stats_response_for_block_2016_old =
96        GetBlockStatsCommand::new(TargetBlockArgument::Height(block_height.0 - 2016)).call(client);
97    let time_of_block_2016_old = match block_stats_response_for_block_2016_old.unwrap() {
98        GetBlockStatsCommandResponse::AllStats(response) => response.time,
99        GetBlockStatsCommandResponse::SelectiveStats(response) => response.time.unwrap(),
100    };
101
102    let duration = time_of_most_recent_block - time_of_block_2016_old;
103    let average_seconds_per_block = duration / 2016 as u64;
104    average_seconds_per_block
105}
106
107pub fn get_average_block_time_for_since_last_difficulty_adjustement(client: &Client) -> u64 {
108    let bitcoind_request_client = &client.bitcoind_request_client;
109    let block_height = GetBlockCountCommand::new().call(bitcoind_request_client);
110    let block_stats_response =
111        GetBlockStatsCommand::new(TargetBlockArgument::Height(block_height.unwrap().0))
112            .call(bitcoind_request_client);
113    let time_of_most_recent_block = match block_stats_response.unwrap() {
114        GetBlockStatsCommandResponse::AllStats(response) => response.time,
115        GetBlockStatsCommandResponse::SelectiveStats(response) => response.time.unwrap(),
116    };
117
118    let block_height_of_last_difficulty_adjustment =
119        get_block_height_of_last_difficulty_adjustment(client);
120    let block_stats_response_for_last_difficulty_ajustment_block = GetBlockStatsCommand::new(
121        TargetBlockArgument::Height(block_height_of_last_difficulty_adjustment),
122    )
123    .call(bitcoind_request_client);
124    let time_of_last_difficulty_adjustment_block =
125        match block_stats_response_for_last_difficulty_ajustment_block.unwrap() {
126            GetBlockStatsCommandResponse::AllStats(response) => response.time,
127            GetBlockStatsCommandResponse::SelectiveStats(response) => response.time.unwrap(),
128        };
129
130    let blocks_since_last_retarget =
131        BLOCKS_PER_DIFFICULTY_PERIOD as f64 - get_blocks_count_until_retarget(client);
132
133    let duration = time_of_most_recent_block - time_of_last_difficulty_adjustment_block;
134    let average_seconds_per_block = duration / blocks_since_last_retarget as u64;
135    average_seconds_per_block
136}
137
138pub fn get_total_money_supply(client: &Client) -> f64 {
139    // calls to gettxoutsetinfo are erroring out due to this: https://github.com/apoelstra/rust-jsonrpc/issues/67
140    let client = &client.bitcoind_request_client;
141    let tx_out_set_info = GetTxOutSetInfoCommand::new().call(client);
142    tx_out_set_info.unwrap().total_amount
143}
144
145// gets the chain size in bytes
146pub fn get_chain_size(client: &Client) -> u64 {
147    let client = &client.bitcoind_request_client;
148    let blockchain_info = GetBlockchainInfoCommand::new().call(client);
149    blockchain_info.unwrap().size_on_disk
150}
151
152pub fn get_utxo_set_size(client: &Client) -> u64 {
153    let client = &client.bitcoind_request_client;
154    let tx_out_set_info = GetTxOutSetInfoCommand::new().call(client);
155    tx_out_set_info.unwrap().txouts
156}
157
158pub fn get_total_transactions_count(client: &Client) -> u64 {
159    let client = &client.bitcoind_request_client;
160    let chain_tx_stats = GetChainTxStatsCommand::new().call(client);
161    chain_tx_stats.unwrap().txcount
162}
163
164pub fn get_tps_for_last_30_days(client: &Client) -> f64 {
165    // This defaults to getting about 30 days worth of of data
166    let client = &client.bitcoind_request_client;
167    let chain_tx_stats = GetChainTxStatsCommand::new().call(client).unwrap();
168    let seconds_in_interval = chain_tx_stats.window_interval;
169    let transactions_count_in_window = chain_tx_stats.window_tx_count as f64;
170    let elapsed_seconds_in_window = seconds_in_interval as f64;
171    let tps = transactions_count_in_window / elapsed_seconds_in_window;
172    tps
173}
174
175// takes a long time
176pub fn get_transactions_count_over_last_30_days(client: &Client) -> u64 {
177    let client = &client.bitcoind_request_client;
178    let chain_tx_stats = GetChainTxStatsCommand::new().call(client);
179    chain_tx_stats.unwrap().window_tx_count
180}
181
182pub fn get_total_fee_for_block_at_height(client: &Client, height: u64) -> u64 {
183    let client = &client.bitcoind_request_client;
184    let block_stats = GetBlockStatsCommand::new(TargetBlockArgument::Height(height))
185        .add_selective_stats(vec![StatsArgumentChoices::TotalFee])
186        .call(client);
187    let total_fee = match block_stats.unwrap() {
188        GetBlockStatsCommandResponse::AllStats(response) => response.totalfee,
189        GetBlockStatsCommandResponse::SelectiveStats(response) => response.totalfee.unwrap(),
190    };
191    total_fee
192}
193
194fn get_subsidy_for_block_at_height(client: &Client, height: u64) -> u64 {
195    let client = &client.bitcoind_request_client;
196    let block_stats = GetBlockStatsCommand::new(TargetBlockArgument::Height(height))
197        .add_selective_stats(vec![StatsArgumentChoices::Subsidy])
198        .call(client);
199    let subsidy = match block_stats.unwrap() {
200        GetBlockStatsCommandResponse::AllStats(response) => response.subsidy,
201        GetBlockStatsCommandResponse::SelectiveStats(response) => response.subsidy.unwrap(),
202    };
203    subsidy
204}
205
206fn get_timestamp_of_block_at_height(client: &Client, height: u64) -> u64 {
207    let client = &client.bitcoind_request_client;
208    let block_stats = GetBlockStatsCommand::new(TargetBlockArgument::Height(height))
209        .add_selective_stats(vec![StatsArgumentChoices::Time])
210        .call(client);
211    let time = match block_stats.unwrap() {
212        GetBlockStatsCommandResponse::AllStats(response) => response.time,
213        GetBlockStatsCommandResponse::SelectiveStats(response) => response.time.unwrap(),
214    };
215    time
216}
217
218// takes a long time
219pub fn get_total_fee_for_24_hours(client: &Client) -> u64 {
220    let last_block_height = get_block_height(&client);
221
222    let mut total_fee = 0;
223    let mut current_block_height = last_block_height;
224    let mut next_block_timestamp = chrono::offset::Utc::now().timestamp();
225    // Calculate fee while the blocktime is within the 24 hour window.
226    while !timestamp_is_from_more_than_24_hours_ago(next_block_timestamp) {
227        // TODO: Very inneficient as we're calling "getblockstats" command twice. Could just do it
228        // once.
229        let fee = get_total_fee_for_block_at_height(client, current_block_height);
230        let time = get_timestamp_of_block_at_height(client, current_block_height);
231
232        total_fee = total_fee + fee;
233
234        current_block_height = current_block_height - 1;
235        next_block_timestamp = time as i64;
236    }
237    total_fee
238}
239
240pub fn get_difficulty(client: &Client) -> f64 {
241    let client = &client.bitcoind_request_client;
242    let difficulty = GetDifficultyCommand::new().call(client);
243    difficulty.unwrap().0
244}
245
246pub fn get_current_difficulty_epoch(client: &Client) -> u64 {
247    let block_count = get_block_height(client);
248    let epoch = (block_count / BLOCKS_PER_DIFFICULTY_PERIOD) + 1;
249    epoch
250}
251pub fn get_block_height_of_last_difficulty_adjustment(client: &Client) -> u64 {
252    let last_epoch = get_current_difficulty_epoch(client) - 1;
253    let block_height_of_last_difficulty_adjustment = last_epoch * 2016;
254    block_height_of_last_difficulty_adjustment
255}
256
257pub fn get_mempool_transactions_count(client: &Client) -> u64 {
258    let client = &client.bitcoind_request_client;
259    let mining_info = GetMiningInfoCommand::new().call(client);
260    let mempool_transaction_count = mining_info.unwrap().pooledtx;
261    mempool_transaction_count
262}
263
264pub fn get_estimated_hash_rate_per_second_for_block_since_last_difficulty_change(
265    client: &Client,
266) -> f64 {
267    let client = &client.bitcoind_request_client;
268    let hash_rate = GetNetworkHashPsCommand::new()
269        .set_n_blocks(bitcoind_request::command::get_network_hash_ps::BlocksToIncludeArg::BlocksSinceLastDifficultyChange)
270        .call(client);
271    hash_rate.unwrap().0
272}
273
274pub fn get_estimated_hash_rate_per_second_for_last_2016_blocks(client: &Client) -> f64 {
275    let client = &client.bitcoind_request_client;
276    let blocks_to_calculate = 2016;
277    let hash_rate = GetNetworkHashPsCommand::new()
278        .set_n_blocks(
279            bitcoind_request::command::get_network_hash_ps::BlocksToIncludeArg::NBlocks(
280                blocks_to_calculate,
281            ),
282        )
283        .call(client);
284    hash_rate.unwrap().0
285}
286pub fn get_estimated_hash_rate_per_second_for_last_epoch(client: &Client) -> f64 {
287    let bitcoind_request_client = &client.bitcoind_request_client;
288    let block_height_of_last_difficulty_adjustment =
289        get_block_height_of_last_difficulty_adjustment(client);
290    let hash_rate = GetNetworkHashPsCommand::new()
291        .set_n_blocks(
292            bitcoind_request::command::get_network_hash_ps::BlocksToIncludeArg::NBlocks(
293                BLOCKS_PER_DIFFICULTY_PERIOD,
294            ),
295        )
296        .set_height(
297            bitcoind_request::command::get_network_hash_ps::HeightArg::Height(
298                block_height_of_last_difficulty_adjustment,
299            ),
300        )
301        .call(bitcoind_request_client);
302    hash_rate.unwrap().0
303}
304
305pub fn get_blocks_count_until_retarget(client: &Client) -> f64 {
306    let block_count = get_block_height(client);
307    let percent_of_epoch_complete: f64 =
308        (block_count as f64 / BLOCKS_PER_DIFFICULTY_PERIOD as f64) % 1.0;
309    let percent_of_epoch_to_go: f64 = 1.0 - percent_of_epoch_complete;
310    let blocks_until_retarget = percent_of_epoch_to_go * (BLOCKS_PER_DIFFICULTY_PERIOD as f64);
311    blocks_until_retarget
312}
313
314pub fn get_estimated_seconds_until_retarget(client: &Client) -> f64 {
315    // TODO: Could we get a more accurate prediction if we use average block times in the current
316    // epoch?
317    // let average_block_time_for_current_epoch =
318    get_average_block_time_for_since_last_difficulty_adjustement(client);
319    let blocks_count_until_retarget = get_blocks_count_until_retarget(client);
320    10.0 * 60.0 * blocks_count_until_retarget
321}
322
323// takes a long time
324pub fn get_blocks_mined_over_last_24_hours_count(client: &Client) -> u64 {
325    let block_count = get_block_height(client);
326
327    // defaults to current time
328    let mut next_block_timestamp: u64 = get_timestamp_of_block_at_height(client, block_count);
329    let mut traversed_blocks_count = 0;
330    // Calculate fee while the blocktime is within the 24 hour window.
331    while !timestamp_is_from_more_than_24_hours_ago(next_block_timestamp as i64) {
332        traversed_blocks_count = traversed_blocks_count + 1;
333
334        let block_height_to_calculate = block_count - traversed_blocks_count;
335        let time = get_timestamp_of_block_at_height(client, block_height_to_calculate);
336
337        next_block_timestamp = time as u64;
338    }
339
340    traversed_blocks_count
341}
342
343// takes a long time
344pub fn get_average_fees_per_block_over_last_24_hours(client: &Client) -> u64 {
345    let block_count = get_block_height(client);
346
347    // defaults to current time
348    let mut next_block_timestamp: u64 = get_timestamp_of_block_at_height(client, block_count);
349    let mut traversed_blocks_count = 0;
350    let mut total_fee = 0;
351    // Calculate fee while the blocktime is within the 24 hour window.
352    while !timestamp_is_from_more_than_24_hours_ago(next_block_timestamp as i64) {
353        let block_fee =
354            get_total_fee_for_block_at_height(client, block_count - traversed_blocks_count);
355        traversed_blocks_count = traversed_blocks_count + 1;
356        total_fee = total_fee + block_fee;
357
358        let next_block_height_to_calculate = block_count - traversed_blocks_count;
359        let time = get_timestamp_of_block_at_height(client, next_block_height_to_calculate);
360
361        next_block_timestamp = time as u64;
362    }
363
364    total_fee / traversed_blocks_count
365}
366
367// takes a long time
368pub fn get_average_fees_per_block_over_last_2016_blocks(client: &Client) -> u64 {
369    let block_count = get_block_height(client);
370    // defaults to current time
371    let mut total_fee = 0;
372
373    for i in 0..=2016 {
374        // Calculate fee while the blocktime is within the 24 hour window.
375        let block_fee = get_total_fee_for_block_at_height(client, block_count - i);
376        total_fee = total_fee + block_fee;
377    }
378
379    total_fee / 2016
380}
381
382pub fn get_fees_as_a_percent_of_reward_for_last_24_hours(client: &Client) -> f64 {
383    let block_count = get_block_height(client);
384
385    // defaults to current time
386    let mut next_block_timestamp: u64 = get_timestamp_of_block_at_height(client, block_count);
387    let mut traversed_blocks_count = 0;
388    let mut total_fee = 0.0;
389    let mut total_subsidy = 0.0;
390    // Calculate fee while the blocktime is within the 24 hour window.
391    while !timestamp_is_from_more_than_24_hours_ago(next_block_timestamp as i64) {
392        let block_fee =
393            get_total_fee_for_block_at_height(client, block_count - traversed_blocks_count);
394        let block_subsidy =
395            get_subsidy_for_block_at_height(client, block_count - traversed_blocks_count);
396        traversed_blocks_count = traversed_blocks_count + 1;
397        total_fee = total_fee as f64 + block_fee as f64;
398        total_subsidy = total_subsidy as f64 + block_subsidy as f64;
399
400        let next_block_height_to_calculate = block_count - traversed_blocks_count;
401        let time = get_timestamp_of_block_at_height(client, next_block_height_to_calculate);
402
403        next_block_timestamp = time as u64;
404    }
405
406    total_fee / (total_subsidy + total_fee)
407}
408
409pub fn get_fees_as_a_percent_of_reward_for_last_2016_blocks(client: &Client) -> f64 {
410    let block_count = get_block_height(client);
411    // defaults to current time
412    let mut total_fee = 0.0;
413    let mut total_subsidy = 0.0;
414
415    for i in 0..=2016 {
416        // Calculate fee while the blocktime is within the 24 hour window.
417        let block_fee = get_total_fee_for_block_at_height(client, block_count - i);
418        let block_subsidy = get_subsidy_for_block_at_height(client, block_count);
419        total_fee = total_fee as f64 + block_fee as f64;
420        total_subsidy = total_subsidy as f64 + block_subsidy as f64;
421    }
422
423    total_fee / (total_subsidy + total_fee)
424}
425
426pub fn get_block_subsidy_of_most_recent_block(client: &Client) -> u64 {
427    let block_count = get_block_height(client);
428    let block_subsidy = get_subsidy_for_block_at_height(client, block_count);
429    block_subsidy
430}
431
432//// takes a long time
433struct Conf {
434    based_on_transaction_hex: bool,
435    based_on_vouts_and_vins: bool,
436    based_on_vouts: bool,
437    factor_in_change_address: bool,
438    include_coinbase_transaction: bool,
439}
440
441// NOTE: Currently is optimized or only works for basing it off the transaction hex. Because of the
442// 'continue' the vouts and vins won't even be taken into consideration. To do them also, remove
443// the 'continue' and 'break's
444// TODO: REMOVE THE break AND continue, unless you incorporate the configs above.
445// TODO: SPLIT BETWEEN segwit and segwit native. For example, this tranasaction is segwit (notic
446// the yellow tag, but not segwit native). See new address functions below.
447pub fn get_percent_of_vouts_used_segwit_over_last_24_hours(
448    client: &Client,
449) -> (f64, f64, f64, f64, f64) {
450    let is_segwit = is_segwit_v0;
451    let block_count = get_block_height(client);
452
453    // defaults to current time
454    let mut next_block_timestamp: u64 = get_timestamp_of_block_at_height(client, block_count);
455    let mut traversed_blocks_count = 0;
456    let mut vouts_count_minus_change_vout: u64 = 0;
457    let mut segwit_vouts_count: u64 = 0;
458    let mut transactions_count_not_including_coinbase: u64 = 0;
459    let mut segwit_spending_transactions_count_not_including_coinbase: u64 = 0;
460    let mut payments_from_segwit_spending_transactions_count_not_including_coinbase: u64 = 0;
461    let mut transactions_count_including_coinbase: u64 = 0;
462    let mut transactions_segwit_count_based_on_vouts_not_including_coinbase: u64 = 0;
463    let mut transactions_segwit_count_based_on_transaction_hex_not_including_coinbase: u64 = 0;
464    let mut transactions_segwit_count_based_on_vins_or_vouts_not_including_coinbase: u64 = 0;
465    // Calculate fee while the blocktime is within the 24 hour window.
466    while !timestamp_is_from_more_than_24_hours_ago(next_block_timestamp as i64) {
467        let height = block_count - traversed_blocks_count;
468        // get vouts of block at height
469        let bitcoind_request_client = &client.bitcoind_request_client;
470        // Get all transactions
471        let block_stats = GetBlockStatsCommand::new(TargetBlockArgument::Height(height))
472            .add_selective_stats(vec![StatsArgumentChoices::Blockhash])
473            .call(bitcoind_request_client);
474        let blockhash = match block_stats.unwrap() {
475            GetBlockStatsCommandResponse::AllStats(response) => response.blockhash,
476            GetBlockStatsCommandResponse::SelectiveStats(response) => response.blockhash.unwrap(),
477        };
478        let get_block_response = GetBlockCommand::new(Blockhash(blockhash))
479            .verbosity(GetBlockCommandVerbosity::BlockObjectWithTransactionInformation)
480            .call(&bitcoind_request_client);
481        let block_transactions_responses: Vec<GetBlockCommandTransactionResponse> =
482            match get_block_response.unwrap() {
483                GetBlockCommandResponse::Block(block) => block.tx,
484                _ => todo!(),
485            };
486        let transactions: Vec<DecodeRawTransactionResponse> = block_transactions_responses
487            .into_iter()
488            .map(
489                |block_transaction_response| match block_transaction_response {
490                    GetBlockCommandTransactionResponse::Raw(transaction) => transaction,
491                    GetBlockCommandTransactionResponse::Id(transaction_id) => todo!(),
492                },
493            )
494            .collect();
495        // Loop through transactions
496        for transaction in transactions.into_iter() {
497            transactions_count_including_coinbase = transactions_count_including_coinbase + 1;
498            if !transaction.is_coinbase_transaction() {
499                transactions_count_not_including_coinbase =
500                    transactions_count_not_including_coinbase + 1;
501                let transaction_hex = transaction.hex;
502                if is_transaction_hex_segwit(&transaction_hex) {
503                    transactions_segwit_count_based_on_transaction_hex_not_including_coinbase =
504                        transactions_segwit_count_based_on_transaction_hex_not_including_coinbase
505                            + 1;
506                    // TODO: This would optimize it to be much quicker if we only care about the
507                    // transaction hex
508                    // continue;
509                }
510                let vouts = transaction.vout;
511                let vins = transaction.vin;
512                let vouts_count: u64 = vouts.len() as u64;
513                let vouts_count_minus_change_vout_for_transaction = if vouts_count == 1 {
514                    vouts_count
515                } else {
516                    vouts_count - 1
517                };
518                // Factor in change address: https://transactionfee.info/charts/payments-spending-segwit/
519                if vouts_count == 1 {
520                    vouts_count_minus_change_vout = vouts_count_minus_change_vout + vouts_count;
521                } else {
522                    vouts_count_minus_change_vout = vouts_count_minus_change_vout + vouts_count - 1;
523                }
524                let mut is_segwit_transaction_based_on_vouts = false;
525                let mut is_segwit_transaction_based_on_vins = false;
526                for vout in vouts.iter() {
527                    let is_segwit = match &vout.script_pub_key.address {
528                        Some(address) => is_segwit(&address),
529                        None => false,
530                    };
531                    if is_segwit {
532                        segwit_vouts_count = segwit_vouts_count + 1;
533                        is_segwit_transaction_based_on_vouts = true;
534                        // TODO: This would optimize it to be much quicker we didn't care about
535                        // vins, UNLESS there were not  vout addresses that matched our segwith
536                        // calculation
537                        //break;
538                    } else {
539                        //println!(
540                        //    "NOT SEGWIT BASED ON VOUTS: {:#?}",
541                        //    &vout.script_pub_key.address
542                        //);
543                    }
544                }
545                // NOTE: OPTIMIZATION for certain code paths
546                //if !is_segwit_transaction_based_on_vouts {
547                let mut vins_count = 0;
548
549                for vin in vins.iter() {
550                    match &vin {
551                        get_block::Vin::Coinbase(_) => {}
552                        get_block::Vin::NonCoinbase(v) => {
553                            vins_count = vins_count + 1;
554                            let txid = &v.txid;
555                            let vout_index = v.vout;
556                            let transaction = GetRawTransactionCommand::new(txid.clone())
557                                .verbose(true)
558                                .call(bitcoind_request_client);
559
560                            let vout_address = match transaction.unwrap() {
561                                GetRawTransactionCommandResponse::SerializedHexEncodedData(_s) => {
562                                    todo!()
563                                }
564                                GetRawTransactionCommandResponse::Transaction(transaction) => {
565                                    let address = &transaction.vout[vout_index as usize]
566                                        .script_pub_key
567                                        .address;
568                                    let a = address.clone();
569                                    a
570                                }
571                            };
572
573                            let is_segwit = match &vout_address {
574                                Some(address) => is_segwit(address),
575                                None => false,
576                            };
577                            if is_segwit {
578                                is_segwit_transaction_based_on_vins = true;
579                                // break;
580                            } else {
581                                //println!("NOT SEGWIT BASED ON VINS: {:#?}", &vout_address);
582                            }
583                        }
584                    }
585                }
586
587                //}
588
589                if is_segwit_transaction_based_on_vouts {
590                    transactions_segwit_count_based_on_vouts_not_including_coinbase =
591                        transactions_segwit_count_based_on_vouts_not_including_coinbase + 1;
592                } else {
593                    //jprintln!("NOT SEGWIT: {:#?}", transaction.txid);
594                }
595
596                // https://transactionfee.info/charts/payments-spending-segwit/
597                if is_segwit_transaction_based_on_vins {
598                    segwit_spending_transactions_count_not_including_coinbase =
599                        segwit_spending_transactions_count_not_including_coinbase + 1;
600
601                    payments_from_segwit_spending_transactions_count_not_including_coinbase =
602                        payments_from_segwit_spending_transactions_count_not_including_coinbase
603                            + vouts_count_minus_change_vout_for_transaction;
604                }
605
606                if is_segwit_transaction_based_on_vouts || is_segwit_transaction_based_on_vins {
607                    transactions_segwit_count_based_on_vins_or_vouts_not_including_coinbase =
608                        transactions_segwit_count_based_on_vins_or_vouts_not_including_coinbase + 1;
609                } else {
610                    //println!("NOT SEGWIT: {:#?}", transaction.txid);
611                }
612            }
613        }
614
615        traversed_blocks_count = traversed_blocks_count + 1;
616
617        let next_block_height_to_calculate = block_count - traversed_blocks_count;
618        let time = get_timestamp_of_block_at_height(client, next_block_height_to_calculate);
619
620        next_block_timestamp = time as u64;
621    }
622
623    // % of payments that are segwit, where payments are vouts. NOTE: We factor in change address
624    // segwit_vouts_count as f64 / vouts_count_minus_change_vout as f64
625    //
626    // % of transactions that are segwit, not including coinase
627    let percent_of_transactions_with_a_segwit_vout =
628        transactions_segwit_count_based_on_vouts_not_including_coinbase as f64
629            / transactions_count_not_including_coinbase as f64;
630    let percent_of_transactions_with_a_segwit_vin_or_vout =
631        transactions_segwit_count_based_on_vins_or_vouts_not_including_coinbase as f64
632            / transactions_count_not_including_coinbase as f64;
633    let percent_based_on_transaction_hexes =
634        transactions_segwit_count_based_on_transaction_hex_not_including_coinbase as f64
635            / transactions_count_not_including_coinbase as f64;
636    // https://transactionfee.info/charts/payments-spending-segwit/
637    let percent_of_payments_spending_segwit_per_day =
638        payments_from_segwit_spending_transactions_count_not_including_coinbase as f64
639            / vouts_count_minus_change_vout as f64;
640    let percent_of_segwit_spending_transactions_per_day =
641        segwit_spending_transactions_count_not_including_coinbase as f64
642            / transactions_count_not_including_coinbase as f64;
643
644    (
645        percent_of_transactions_with_a_segwit_vout,
646        percent_of_transactions_with_a_segwit_vin_or_vout,
647        percent_based_on_transaction_hexes,
648        percent_of_payments_spending_segwit_per_day,
649        percent_of_segwit_spending_transactions_per_day,
650    )
651}