Skip to content

Commit 1920b38

Browse files
committed
Add detailed sync progress
1 parent 122d94d commit 1920b38

File tree

9 files changed

+186
-61
lines changed

9 files changed

+186
-61
lines changed

client/src/bin/spaced.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use spaces_client::{
88
rpc::{AsyncChainState, RpcServerImpl, WalletLoadRequest, WalletManager},
99
source::{BitcoinBlockSource, BitcoinRpc},
1010
store,
11-
sync::Spaced,
11+
spaces::Spaced,
1212
wallets::RpcWallet,
1313
};
1414
use store::LiveSnapshot;

client/src/cbf.rs

+39-6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use spaces_wallet::bitcoin::ScriptBuf;
1313
use spaces_wallet::SpacesWallet;
1414
use crate::client::{BlockSource, BlockchainInfo};
1515
use crate::source::BitcoinBlockSource;
16+
use crate::wallets::WalletProgressUpdate;
1617

1718
pub struct CompactFilterSync {
1819
graph: IndexedTxGraph<ConfirmationBlockTime, KeychainTxOutIndex<KeychainKind>>,
@@ -24,6 +25,8 @@ pub struct CompactFilterSync {
2425
queued_blocks: BTreeMap<u32, BlockHash>,
2526
queued_filters: VecDeque<u32>,
2627
filters_tip: u32,
28+
block_matches: u32,
29+
total_filters: u32,
2730
wait: Option<Instant>,
2831
state: SyncState,
2932
}
@@ -36,7 +39,7 @@ enum SyncState {
3639
WaitForBlocks,
3740
ProcessBlocks,
3841
ApplyUpdate,
39-
Synced
42+
Synced,
4043
}
4144

4245
impl CompactFilterSync {
@@ -56,6 +59,8 @@ impl CompactFilterSync {
5659
queued_blocks: BTreeMap::new(),
5760
queued_filters: Default::default(),
5861
filters_tip: 0,
62+
block_matches: 0,
63+
total_filters: 0,
5964
wait: None,
6065
state: SyncState::SyncChecks,
6166
};
@@ -101,6 +106,7 @@ impl CompactFilterSync {
101106
&mut self,
102107
wallet: &mut SpacesWallet,
103108
source: &BitcoinBlockSource,
109+
progress: &mut WalletProgressUpdate,
104110
) -> anyhow::Result<()> {
105111
if self.wait.is_some_and(|w| w.elapsed() < Duration::from_secs(10)) {
106112
return Ok(());
@@ -112,11 +118,16 @@ impl CompactFilterSync {
112118
let info = source.get_blockchain_info()?;
113119
if info.headers != info.blocks {
114120
info!("Source still syncing, retrying...");
121+
*progress = WalletProgressUpdate::Syncing;
115122
self.wait = Some(Instant::now());
116123
return Ok(());
117124
}
118125
if info.filters != info.filter_headers {
119126
info!("Filters syncing, retrying...");
127+
*progress = WalletProgressUpdate::CbfFilterSync {
128+
total: info.filter_headers.unwrap_or(0),
129+
completed: info.filters.unwrap_or(0),
130+
};
120131
self.wait = Some(Instant::now());
121132
return Ok(());
122133
}
@@ -156,14 +167,15 @@ impl CompactFilterSync {
156167
self.queued_filters.push_back(height);
157168
}
158169
self.filters_tip = end;
170+
self.total_filters = self.queued_filters.len() as u32;
159171
self.state = SyncState::ProcessFilters;
160172
}
161173
SyncState::ProcessFilters => {
162174
let height = match self.queued_filters.pop_front() {
163175
None => {
164176
self.state = SyncState::QueueBlocks;
165-
return Ok(())
166-
},
177+
return Ok(());
178+
}
167179
Some(f) => f,
168180
};
169181
let idx_filter = source.get_block_filter_by_height(height)?;
@@ -173,10 +185,15 @@ impl CompactFilterSync {
173185
if filter.match_any(&idx_filter.hash, self.scripts.iter().map(|s| s.as_bytes()))? {
174186
self.queued_blocks.insert(height, idx_filter.hash);
175187
self.load_more_scripts(wallet);
188+
self.block_matches += 1;
176189
info!("wallet({}) processed block filter {} - match found", wallet.name(), height);
177190
} else {
178191
info!("wallet({}) processed block filter {} - no match", wallet.name(), height);
179192
}
193+
*progress = WalletProgressUpdate::CbfProcessFilters {
194+
total: self.total_filters,
195+
completed: self.total_filters - self.queued_filters.len() as u32,
196+
};
180197
}
181198
SyncState::QueueBlocks => {
182199
if !self.queued_blocks.is_empty() {
@@ -195,6 +212,16 @@ impl CompactFilterSync {
195212

196213
if status.pending > 0 {
197214
info!("wallet({}): waiting for {} pending blocks", wallet.name(), status.pending);
215+
216+
// The client has a global state for pending blocks in the queue
217+
// so we cap it just in case other things are queuing blocks
218+
// at the same time
219+
let pending = std::cmp::min(status.pending, self.block_matches);
220+
*progress = WalletProgressUpdate::CbfDownloadMatchingBlocks {
221+
total: self.block_matches,
222+
completed: self.block_matches - pending,
223+
};
224+
198225
self.wait = Some(Instant::now());
199226
return Ok(());
200227
}
@@ -209,7 +236,8 @@ impl CompactFilterSync {
209236
SyncState::ProcessBlocks => {
210237
let (height, hash) = match self.queued_blocks.pop_first() {
211238
None => {
212-
self.state = SyncState::Synced;
239+
*progress = WalletProgressUpdate::CbfApplyUpdate;
240+
self.state = SyncState::ApplyUpdate;
213241
return Ok(());
214242
}
215243
Some(f) => f,
@@ -219,7 +247,10 @@ impl CompactFilterSync {
219247
.ok_or(anyhow!("block {} {} not found", height, hash))?;
220248
self.chain_changeset.insert(height, Some(hash));
221249
let _ = self.graph.apply_block_relevant(&block, height);
222-
self.state = SyncState::ApplyUpdate;
250+
*progress = WalletProgressUpdate::CbfProcessMatchingBlocks {
251+
total: self.block_matches,
252+
completed: self.block_matches - self.queued_blocks.len() as u32 ,
253+
};
223254
}
224255
SyncState::ApplyUpdate => {
225256
info!("wallet({}): updating wallet tip to {}", wallet.name(), self.filters_tip);
@@ -233,8 +264,10 @@ impl CompactFilterSync {
233264
wallet.insert_checkpoint(filters_anchor)?;
234265
info!("wallet({}): compact filter sync portion complete at {}", wallet.name(), self.filters_tip);
235266
self.state = SyncState::Synced;
267+
// Only CBF portion is done
268+
*progress = WalletProgressUpdate::Syncing
236269
}
237-
SyncState::Synced => {}
270+
SyncState::Synced => {},
238271
}
239272
Ok(())
240273
}

client/src/config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use toml::Value;
2222
use crate::{
2323
source::{BitcoinRpc, BitcoinRpcAuth},
2424
store::{LiveStore, Store},
25-
sync::Spaced,
25+
spaces::Spaced,
2626
};
2727

2828
const RPC_OPTIONS: &str = "RPC Server Options";

client/src/format.rs

+43-7
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ use spaces_wallet::{
1414
BidEventDetails, BidoutEventDetails, OpenEventDetails, SendEventDetails,
1515
TransferEventDetails, TxEventKind,
1616
},
17-
Balance, DoubleUtxo, WalletInfo, WalletOutput,
17+
Balance, DoubleUtxo, WalletOutput,
1818
};
1919
use tabled::{Table, Tabled};
2020

2121
use crate::{
2222
rpc::ServerInfo,
2323
wallets::{ListSpacesResponse, TxInfo, TxResponse, WalletResponse},
2424
};
25+
use crate::wallets::{WalletInfoWithProgress, WalletProgressUpdate};
2526

2627
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, ValueEnum, Serialize, Deserialize)]
2728
#[serde(rename_all = "lowercase")]
@@ -163,27 +164,62 @@ pub fn print_server_info(info: ServerInfo, format: Format) {
163164
}
164165
}
165166

166-
pub fn print_wallet_info(info: WalletInfo, format: Format) {
167+
pub fn print_wallet_info(prog: WalletInfoWithProgress, format: Format) {
167168
match format {
168169
Format::Text => {
169-
println!("WALLET: {}", info.label);
170-
println!(" Tip {}\n Birthday {}", info.tip, info.start_block);
170+
println!("WALLET: {}", prog.info.label);
171+
println!(" Tip {}\n Birthday {}", prog.info.tip, prog.info.start_block);
171172

172173
println!(" Public descriptors");
173-
for desc in info.descriptors {
174+
for desc in prog.info.descriptors {
174175
println!(" {}", desc.descriptor);
175176
}
177+
178+
// Print sync status
179+
println!(" Sync Status:");
180+
match prog.status {
181+
WalletProgressUpdate::SourceSync { total, completed } => {
182+
println!(" Source Syncing: {}/{} ({:.1}%)", completed, total,
183+
(completed as f64 / total as f64) * 100.0);
184+
}
185+
WalletProgressUpdate::CbfFilterSync { total, completed } => {
186+
println!(" Filters Syncing: {}/{} ({:.1}%)", completed, total,
187+
(completed as f64 / total as f64) * 100.0);
188+
}
189+
WalletProgressUpdate::CbfProcessFilters { total, completed } => {
190+
println!(" Processing Filters: {}/{} ({:.1}%)", completed, total,
191+
(completed as f64 / total as f64) * 100.0);
192+
}
193+
WalletProgressUpdate::CbfDownloadMatchingBlocks { total, completed } => {
194+
println!(" Downloading Matching Blocks: {}/{} ({:.1}%)", completed, total,
195+
(completed as f64 / total as f64) * 100.0);
196+
}
197+
WalletProgressUpdate::CbfProcessMatchingBlocks { total, completed } => {
198+
println!(" Processing Matching Blocks: {}/{} ({:.1}%)", completed, total,
199+
(completed as f64 / total as f64) * 100.0);
200+
}
201+
WalletProgressUpdate::Syncing => {
202+
println!(" Syncing: In progress ({:.1}%):", prog.info.progress * 100.0);
203+
}
204+
WalletProgressUpdate::CbfApplyUpdate => {
205+
println!(" Applying compact filters update");
206+
}
207+
WalletProgressUpdate::Complete => {
208+
println!(" Complete");
209+
}
210+
}
211+
176212
println!();
177213
}
178214
Format::Json => {
179-
println!("{}", serde_json::to_string_pretty(&info).unwrap());
215+
println!("{}", serde_json::to_string_pretty(&prog.info).unwrap());
180216
}
181217
}
182218
}
183219

184220
fn ascii_table<I, T>(iter: I) -> String
185221
where
186-
I: IntoIterator<Item = T>,
222+
I: IntoIterator<Item=T>,
187223
T: Tabled,
188224
{
189225
Table::new(iter)

client/src/lib.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub mod format;
1616
pub mod rpc;
1717
pub mod source;
1818
pub mod store;
19-
pub mod sync;
19+
pub mod spaces;
2020
pub mod wallets;
2121
mod cbf;
2222

@@ -60,3 +60,15 @@ where
6060
Vec::<u8>::deserialize(deserializer)
6161
}
6262
}
63+
64+
pub fn calc_progress(start_block: u32, tip: u32, chain_tip: u32) -> f32 {
65+
if chain_tip <= start_block || tip < start_block {
66+
0.0
67+
} else if tip >= chain_tip {
68+
1.0
69+
} else {
70+
let blocks_synced = tip - start_block;
71+
let blocks_to_sync = chain_tip - start_block;
72+
blocks_synced as f32 / blocks_to_sync as f32
73+
}
74+
}

client/src/rpc.rs

+17-21
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,19 @@ use spaces_protocol::{
4040
use spaces_wallet::{
4141
bdk_wallet as bdk, bdk_wallet::template::Bip86, bitcoin::hashes::Hash as BitcoinHash,
4242
export::WalletExport, nostr::NostrEvent, Balance, DoubleUtxo, Listing, SpacesWallet,
43-
WalletConfig, WalletDescriptors, WalletInfo, WalletOutput,
43+
WalletConfig, WalletDescriptors, WalletOutput,
4444
};
4545
use tokio::{
4646
select,
4747
sync::{broadcast, mpsc, oneshot, RwLock},
4848
task::JoinSet,
4949
};
5050

51-
use crate::{
52-
checker::TxChecker,
53-
client::{BlockMeta, TxEntry},
54-
config::ExtendedNetwork,
55-
deserialize_base64, serialize_base64,
56-
source::BitcoinRpc,
57-
store::{ChainState, LiveSnapshot, RolloutEntry, Sha256},
58-
sync::{COMMIT_BLOCK_INTERVAL, ROOT_ANCHORS_COUNT},
59-
wallets::{
60-
AddressKind, ListSpacesResponse, RpcWallet, TxInfo, TxResponse, WalletCommand,
61-
WalletResponse,
62-
},
63-
};
51+
use crate::{calc_progress, checker::TxChecker, client::{BlockMeta, TxEntry}, config::ExtendedNetwork, deserialize_base64, serialize_base64, source::BitcoinRpc, store::{ChainState, LiveSnapshot, RolloutEntry, Sha256}, spaces::{COMMIT_BLOCK_INTERVAL, ROOT_ANCHORS_COUNT}, wallets::{
52+
AddressKind, ListSpacesResponse, RpcWallet, TxInfo, TxResponse, WalletCommand,
53+
WalletResponse,
54+
}};
55+
use crate::wallets::WalletInfoWithProgress;
6456

6557
pub(crate) type Responder<T> = oneshot::Sender<T>;
6658

@@ -229,7 +221,7 @@ pub trait Rpc {
229221
) -> Result<NostrEvent, ErrorObjectOwned>;
230222

231223
#[method(name = "walletgetinfo")]
232-
async fn wallet_get_info(&self, name: &str) -> Result<WalletInfo, ErrorObjectOwned>;
224+
async fn wallet_get_info(&self, name: &str) -> Result<WalletInfoWithProgress, ErrorObjectOwned>;
233225

234226
#[method(name = "walletexport")]
235227
async fn wallet_export(&self, name: &str) -> Result<WalletExport, ErrorObjectOwned>;
@@ -870,7 +862,7 @@ impl RpcServer for RpcServerImpl {
870862
.map_err(|error| ErrorObjectOwned::owned(-1, error.to_string(), None::<String>))
871863
}
872864

873-
async fn wallet_get_info(&self, wallet: &str) -> Result<WalletInfo, ErrorObjectOwned> {
865+
async fn wallet_get_info(&self, wallet: &str) -> Result<WalletInfoWithProgress, ErrorObjectOwned> {
874866
self.wallet(&wallet)
875867
.await?
876868
.send_get_info()
@@ -1584,17 +1576,21 @@ async fn get_server_info(client: &reqwest::Client, rpc: &BitcoinRpc, tip: ChainA
15841576
.await
15851577
.map_err(|e| anyhow!("Could not retrieve blockchain info ({})", e))?;
15861578

1579+
let start_block = if info.chain == "main" {
1580+
871_222
1581+
} else if info.chain.starts_with("test") {
1582+
50_000
1583+
} else {
1584+
0
1585+
};
1586+
15871587
Ok(ServerInfo {
15881588
network: info.chain,
15891589
tip,
15901590
chain: ChainInfo {
15911591
blocks: info.blocks,
15921592
headers: info.headers,
15931593
},
1594-
progress: if info.headers != 0 && info.headers >= tip.height {
1595-
tip.height as f32 / info.headers as f32
1596-
} else {
1597-
0.0
1598-
},
1594+
progress: calc_progress(start_block, tip.height, info.headers),
15991595
})
16001596
}
File renamed without changes.

0 commit comments

Comments
 (0)