main.rs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{fmt, sync::Arc};
  19. use log::info;
  20. use smol::stream::StreamExt;
  21. use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
  22. use url::Url;
  23. use darkfi::{
  24. async_daemonize,
  25. blockchain::BlockInfo,
  26. cli_desc,
  27. rpc::{client::RpcClient, jsonrpc::JsonRequest, util::JsonValue},
  28. util::encoding::base64,
  29. Result,
  30. };
  31. use darkfi_serial::{deserialize, serialize};
  32. const CONFIG_FILE: &str = "blockchain_storage_metrics_config.toml";
  33. const CONFIG_FILE_CONTENTS: &str = include_str!("../blockchain_storage_metrics_config.toml");
  34. #[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
  35. #[serde(default)]
  36. #[structopt(name = "blockchain-storage-metrics", about = cli_desc!())]
  37. struct Args {
  38. #[structopt(short, long)]
  39. /// Configuration file to use
  40. config: Option<String>,
  41. #[structopt(short, long, default_value = "tcp://127.0.0.1:8340")]
  42. /// darkfid JSON-RPC endpoint
  43. endpoint: Url,
  44. #[structopt(short, long)]
  45. /// Block height to measure until
  46. height: Option<usize>,
  47. #[structopt(short, long)]
  48. /// Set log file to output into
  49. log: Option<String>,
  50. #[structopt(short, parse(from_occurrences))]
  51. /// Increase verbosity (-vvv supported)
  52. verbose: u8,
  53. }
  54. /// Structure representing block storage metrics.
  55. /// Everything is measured in bytes.
  56. struct BlockMetrics {
  57. /// Header height
  58. height: u32,
  59. /// Header hash,
  60. hash: String,
  61. /// Header size
  62. header_size: usize,
  63. /// Transactions count
  64. txs: usize,
  65. /// Transactions size
  66. txs_size: usize,
  67. /// Block producer signature size
  68. signature_size: usize,
  69. }
  70. impl BlockMetrics {
  71. fn new(block: &BlockInfo) -> Self {
  72. let header_size = serialize(&block.header).len();
  73. let txs_size = serialize(&block.txs).len();
  74. let signature_size = serialize(&block.signature).len();
  75. Self {
  76. height: block.header.height,
  77. hash: block.hash().to_string(),
  78. header_size,
  79. txs: block.txs.len(),
  80. txs_size,
  81. signature_size,
  82. }
  83. }
  84. /// Compute Block total raw size, in bytes.
  85. fn size(&self) -> usize {
  86. self.header_size + self.txs_size + self.signature_size
  87. }
  88. }
  89. impl fmt::Display for BlockMetrics {
  90. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  91. let s = format!(
  92. "Block {} - {} metrics:\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}",
  93. self.height,
  94. self.hash,
  95. "Header size",
  96. self.header_size,
  97. "Transactions count",
  98. self.txs,
  99. "Transactions size",
  100. self.txs_size,
  101. "Block producer signature size",
  102. self.signature_size,
  103. "Raw size",
  104. self.size(),
  105. );
  106. write!(f, "{}", s)
  107. }
  108. }
  109. async_daemonize!(realmain);
  110. async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
  111. // DEV-NOTE: We store everything in memory so don't go crazy with it
  112. info!(target: "blockchain-storage-metrics", "Initializing blockchain storage metrics script...");
  113. // Initialize rpc client
  114. let rpc_client = RpcClient::new(args.endpoint, ex).await?;
  115. // Grab all blocks up to configured height
  116. let height = args.height.unwrap_or_default() + 1;
  117. let mut blocks = Vec::with_capacity(height);
  118. for h in 0..height {
  119. info!(target: "blockchain-storage-metrics", "Requesting block for height: {h}");
  120. let req = JsonRequest::new(
  121. "blockchain.get_block",
  122. JsonValue::Array(vec![JsonValue::String(h.to_string())]),
  123. );
  124. let rep = rpc_client.request(req).await?;
  125. let encoded_block = rep.get::<String>().unwrap();
  126. let bytes = base64::decode(encoded_block).unwrap();
  127. let block: BlockInfo = deserialize(&bytes)?;
  128. info!(target: "blockchain-storage-metrics", "Retrieved block: {h} - {}", block.hash());
  129. blocks.push(block);
  130. }
  131. // Stop rpc client
  132. rpc_client.stop().await;
  133. // TODO: Create a dummy in memory validator to apply each block
  134. // Measure each block storage
  135. let mut blocks_metrics = Vec::with_capacity(height);
  136. for block in &blocks {
  137. // TODO: Grab complete storage requirements from the validator
  138. let block_metrics = BlockMetrics::new(block);
  139. info!(target: "blockchain-storage-metrics", "{block_metrics}");
  140. blocks_metrics.push(block_metrics);
  141. }
  142. // Measure total storage
  143. let mut total_headers_size = 0_u64;
  144. let mut total_txs = 0_u64;
  145. let mut total_txs_size = 0_u64;
  146. let mut total_signatures_size = 0_u64;
  147. let mut total_size = 0_u64;
  148. for block_metrics in blocks_metrics {
  149. total_headers_size += block_metrics.header_size as u64;
  150. total_txs += block_metrics.txs as u64;
  151. total_txs_size += block_metrics.txs_size as u64;
  152. total_signatures_size += block_metrics.signature_size as u64;
  153. total_size += block_metrics.size() as u64;
  154. }
  155. let metrics = format!(
  156. "Total metrics:\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}",
  157. "Headers size",
  158. total_headers_size,
  159. "Transactions",
  160. total_txs,
  161. "Transactions size",
  162. total_txs_size,
  163. "Signatures size",
  164. total_signatures_size,
  165. "Raw size",
  166. total_size
  167. );
  168. info!(target: "blockchain-storage-metrics", "{metrics}");
  169. // TODO: export metrics as a csv so we can use it to visualize stuff in charts
  170. Ok(())
  171. }