|
| 1 | +use crate::helpers::{NumberOrRange, RpcArgs}; |
1 | 2 | use alloy::providers::RootProvider; |
2 | 3 | use clap::Args; |
3 | | -use console::{Emoji, style}; |
4 | | -use eyre::Context; |
5 | | -use indicatif::{HumanBytes, HumanDuration, ProgressBar, ProgressStyle}; |
| 4 | +use console::Emoji; |
| 5 | +use eyre::{Context, ContextCompat}; |
| 6 | +use indicatif::{HumanBytes, HumanDuration, MultiProgress, ProgressBar, ProgressStyle}; |
6 | 7 | use sbv::{primitives::types::Network, utils::rpc::ProviderExt}; |
7 | 8 | use std::{ |
8 | | - path::PathBuf, |
| 9 | + collections::HashMap, |
| 10 | + path::{Path, PathBuf}, |
| 11 | + sync::LazyLock, |
9 | 12 | time::{Duration, Instant}, |
10 | 13 | }; |
11 | 14 |
|
12 | | -use crate::helpers::{NumberOrRange, RpcArgs}; |
| 15 | +const INFO_ICON: Emoji = Emoji(" 🔗 ", " [+] "); |
| 16 | +const ERR_ICON: Emoji = Emoji(" ❌ ", " [x] "); |
| 17 | +const COMPLETED_ICON: Emoji = Emoji(" ✅ ", " [v] "); |
| 18 | +const SAD_ICON: Emoji = Emoji(" ⚠️ ", " :( "); |
| 19 | +const SPARKLE_ICON: Emoji = Emoji(" ✨ ", " :) "); |
13 | 20 |
|
14 | 21 | #[derive(Debug, Args)] |
15 | 22 | pub struct DumpWitnessCommand { |
@@ -44,104 +51,137 @@ impl DumpWitnessCommand { |
44 | 51 |
|
45 | 52 | let provider = self.rpc_args.into_provider(); |
46 | 53 |
|
47 | | - dump_range( |
| 54 | + let ok = dump_range( |
48 | 55 | provider, |
49 | 56 | self.block.into(), |
50 | 57 | self.out_dir, |
51 | 58 | #[cfg(not(feature = "scroll"))] |
52 | 59 | self.ancestors, |
53 | 60 | ) |
54 | | - .await?; |
| 61 | + .await; |
55 | 62 |
|
56 | | - println!( |
57 | | - "{} Done in {}", |
58 | | - Emoji("✨ ", ":-)"), |
59 | | - HumanDuration(started.elapsed()) |
60 | | - ); |
| 63 | + let elapsed = HumanDuration(started.elapsed()); |
| 64 | + if ok { |
| 65 | + println!("{SPARKLE_ICON} Done in {elapsed}"); |
| 66 | + } else { |
| 67 | + println!("{SAD_ICON} Completed with errors in {elapsed}",); |
| 68 | + } |
61 | 69 |
|
62 | 70 | Ok(()) |
63 | 71 | } |
64 | 72 | } |
65 | 73 |
|
| 74 | +static PB_STYLE: LazyLock<ProgressStyle> = |
| 75 | + LazyLock::new(|| ProgressStyle::with_template("{prefix}{msg} {spinner}").expect("infallible")); |
| 76 | + |
66 | 77 | async fn dump_range( |
67 | 78 | provider: RootProvider<Network>, |
68 | 79 | range: std::ops::Range<u64>, |
69 | 80 | out_dir: PathBuf, |
70 | 81 | #[cfg(not(feature = "scroll"))] ancestors: usize, |
71 | | -) -> eyre::Result<()> { |
| 82 | +) -> bool { |
72 | 83 | let mut set = tokio::task::JoinSet::new(); |
73 | 84 |
|
| 85 | + let multi_progress_bar = MultiProgress::new(); |
| 86 | + |
| 87 | + let mut ok = true; |
| 88 | + let mut pb_map = HashMap::new(); |
| 89 | + |
74 | 90 | for block in range { |
75 | 91 | let provider = provider.clone(); |
76 | 92 | let out_dir = out_dir.clone(); |
77 | | - set.spawn(async move { |
78 | | - if let Err(e) = dump( |
79 | | - provider, |
80 | | - block, |
81 | | - out_dir.as_path(), |
82 | | - #[cfg(not(feature = "scroll"))] |
83 | | - ancestors, |
84 | | - ) |
85 | | - .await |
86 | | - { |
87 | | - eprintln!("Error dumping witness for block {block}: {e}"); |
88 | | - } |
89 | | - }); |
| 93 | + let progress_bar = multi_progress_bar.add(ProgressBar::new_spinner()); |
| 94 | + let handle = { |
| 95 | + let progress_bar = progress_bar.clone(); |
| 96 | + set.spawn(async move { |
| 97 | + dump( |
| 98 | + provider, |
| 99 | + block, |
| 100 | + out_dir.as_path(), |
| 101 | + #[cfg(not(feature = "scroll"))] |
| 102 | + ancestors, |
| 103 | + progress_bar, |
| 104 | + ) |
| 105 | + .await |
| 106 | + }) |
| 107 | + }; |
| 108 | + pb_map.insert(handle.id(), progress_bar); |
90 | 109 | } |
91 | 110 |
|
92 | | - while let Some(result) = set.join_next().await { |
93 | | - if let Err(e) = result { |
94 | | - eprintln!("Dump task panicked: {e}"); |
| 111 | + while let Some(result) = set.join_next_with_id().await { |
| 112 | + match result { |
| 113 | + Err(e) => { |
| 114 | + let pb = pb_map.remove(&e.id()).expect("progress bar exists"); |
| 115 | + pb.set_prefix(format!("{ERR_ICON}")); |
| 116 | + pb.finish_with_message(format!("Dump task failed: {e}")); |
| 117 | + ok = false; |
| 118 | + } |
| 119 | + Ok((_, false)) => { |
| 120 | + ok = false; |
| 121 | + } |
| 122 | + _ => { /* ok */ } |
95 | 123 | } |
96 | 124 | } |
97 | | - |
98 | | - Ok(()) |
| 125 | + ok |
99 | 126 | } |
100 | 127 |
|
101 | 128 | async fn dump( |
102 | 129 | provider: RootProvider<Network>, |
103 | 130 | block: u64, |
104 | | - out_dir: &std::path::Path, |
| 131 | + out_dir: &Path, |
105 | 132 | #[cfg(not(feature = "scroll"))] ancestors: usize, |
106 | | -) -> eyre::Result<()> { |
107 | | - let pb = ProgressBar::new_spinner(); |
108 | | - pb.set_style(ProgressStyle::with_template("{prefix}{msg} {spinner}")?); |
109 | | - pb.set_prefix(format!( |
110 | | - "{} {}", |
111 | | - style("[1/2]").bold().dim(), |
112 | | - Emoji("🔗 ", "") |
113 | | - )); |
114 | | - pb.enable_steady_tick(Duration::from_millis(100)); |
| 133 | + pb: ProgressBar, |
| 134 | +) -> bool { |
| 135 | + pb.set_style(PB_STYLE.clone()); |
| 136 | + pb.set_prefix(format!("{INFO_ICON}")); |
115 | 137 | pb.set_message(format!("Dumping witness for block {block}")); |
| 138 | + pb.enable_steady_tick(Duration::from_millis(100)); |
116 | 139 |
|
| 140 | + match dump_inner( |
| 141 | + provider, |
| 142 | + block, |
| 143 | + out_dir, |
| 144 | + #[cfg(not(feature = "scroll"))] |
| 145 | + ancestors, |
| 146 | + ) |
| 147 | + .await |
| 148 | + { |
| 149 | + Ok((path, size)) => { |
| 150 | + pb.set_prefix(format!("{COMPLETED_ICON}")); |
| 151 | + pb.finish_with_message(format!("Witness: {size} saved to {p}", p = path.display())); |
| 152 | + true |
| 153 | + } |
| 154 | + Err(e) => { |
| 155 | + pb.set_prefix(format!("{ERR_ICON}")); |
| 156 | + pb.finish_with_message(format!("Failed to dump witness for block {block}: {e}")); |
| 157 | + false |
| 158 | + } |
| 159 | + } |
| 160 | +} |
| 161 | + |
| 162 | +async fn dump_inner( |
| 163 | + provider: RootProvider<Network>, |
| 164 | + block: u64, |
| 165 | + out_dir: &Path, |
| 166 | + #[cfg(not(feature = "scroll"))] ancestors: usize, |
| 167 | +) -> eyre::Result<(PathBuf, HumanBytes)> { |
117 | 168 | #[cfg(not(feature = "scroll"))] |
118 | 169 | let witness = provider |
119 | 170 | .dump_block_witness(block) |
120 | 171 | .ancestors(ancestors) |
121 | 172 | .send() |
122 | | - .await |
123 | | - .context("dump ethereum block witness")?; |
| 173 | + .await? |
| 174 | + .context("block not found")?; |
124 | 175 | #[cfg(feature = "scroll")] |
125 | 176 | let witness = provider |
126 | 177 | .dump_block_witness(block) |
127 | 178 | .send() |
128 | | - .await |
129 | | - .context("dump scroll block witness")?; |
130 | | - |
131 | | - pb.finish_with_message(format!("Dumped witness for block {block}")); |
132 | | - println!(); |
| 179 | + .await? |
| 180 | + .context("block not found")?; |
133 | 181 |
|
134 | | - let json = serde_json::to_string_pretty(&witness).context("serialize witness")?; |
| 182 | + let json = serde_json::to_string_pretty(&witness)?; |
135 | 183 | let path = out_dir.join(format!("{block}.json")); |
136 | | - std::fs::write(&path, json).context("write json file")?; |
137 | | - let size = HumanBytes(std::fs::metadata(&path)?.len()); |
138 | | - println!( |
139 | | - "{} {}JSON witness({}) saved to {}", |
140 | | - style("[2/2]").bold().dim(), |
141 | | - Emoji("📃 ", ""), |
142 | | - size, |
143 | | - path.display() |
144 | | - ); |
145 | | - |
146 | | - Ok(()) |
| 184 | + tokio::fs::write(&path, json).await?; |
| 185 | + let size = HumanBytes(tokio::fs::metadata(&path).await?.len()); |
| 186 | + Ok((path, size)) |
147 | 187 | } |
0 commit comments