use std::borrow::BorrowMut;
use std::sync::atomic;
use std::time::Duration;
use futures::FutureExt;
use pepper_sync::error::SyncError;
use pepper_sync::error::SyncModeError;
use pepper_sync::wallet::SyncMode;
use crate::data::PollReport;
use crate::wallet::error::WalletError;
use super::LightClient;
use super::SyncResult;
use super::error::LightClientError;
impl LightClient {
pub async fn sync(&mut self) -> Result<(), LightClientError> {
if self.sync_mode() != SyncMode::NotRunning {
return Err(LightClientError::SyncModeError(
SyncModeError::SyncAlreadyRunning,
));
}
let client = zingo_netutils::GrpcConnector::new(self.config.get_lightwalletd_uri())
.get_client()
.await?;
let wallet_guard = self.wallet.lock().await;
let network = wallet_guard.network;
let sync_config = wallet_guard.wallet_settings.sync_config.clone();
drop(wallet_guard);
let wallet = self.wallet.clone();
let sync_mode = self.sync_mode.clone();
let sync_handle = tokio::spawn(async move {
pepper_sync::sync(client, &network, wallet, sync_mode, sync_config).await
});
self.sync_handle = Some(sync_handle);
Ok(())
}
pub async fn rescan(&mut self) -> Result<(), LightClientError> {
if self.sync_mode() != SyncMode::NotRunning {
self.stop_sync().expect("infallible in this scope");
let mut interval = tokio::time::interval(Duration::from_millis(500));
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
interval.tick().await;
while matches!(self.poll_sync(), PollReport::NotReady) {
interval.tick().await;
}
}
self.wallet.lock().await.clear_all();
self.sync().await
}
pub fn sync_mode(&self) -> SyncMode {
SyncMode::from_atomic_u8(self.sync_mode.clone())
.expect("this library does not allow setting of non-valid sync mode variants")
}
pub fn pause_sync(&self) -> Result<(), SyncModeError> {
if self.sync_mode() != SyncMode::Running {
return Err(SyncModeError::SyncNotRunning);
}
self.sync_mode
.store(SyncMode::Paused as u8, atomic::Ordering::Release);
Ok(())
}
pub fn stop_sync(&self) -> Result<(), SyncModeError> {
if self.sync_mode() == SyncMode::NotRunning {
return Err(SyncModeError::SyncNotRunning);
}
self.sync_mode
.store(SyncMode::Shutdown as u8, atomic::Ordering::Release);
Ok(())
}
pub fn resume_sync(&self) -> Result<(), SyncModeError> {
if self.sync_mode() != SyncMode::Paused {
return Err(SyncModeError::SyncNotPaused);
}
self.sync_mode
.store(SyncMode::Running as u8, atomic::Ordering::Release);
Ok(())
}
pub fn poll_sync(&mut self) -> PollReport<SyncResult, SyncError<WalletError>> {
if let Some(mut sync_handle) = self.sync_handle.take() {
if let Some(sync_result) = sync_handle.borrow_mut().now_or_never() {
self.sync_mode
.store(SyncMode::NotRunning as u8, atomic::Ordering::Release);
PollReport::Ready(sync_result.expect("task panicked"))
} else {
self.sync_handle = Some(sync_handle);
PollReport::NotReady
}
} else {
PollReport::NoHandle
}
}
pub async fn await_sync(&mut self) -> Result<SyncResult, LightClientError> {
let mut interval = tokio::time::interval(Duration::from_millis(500));
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
loop {
interval.tick().await;
match self.poll_sync() {
PollReport::NoHandle => return Err(LightClientError::SyncNotRunning),
PollReport::NotReady => (),
PollReport::Ready(result) => return result.map_err(LightClientError::SyncError),
}
}
}
pub async fn sync_and_await(&mut self) -> Result<SyncResult, LightClientError> {
self.sync().await?;
self.await_sync().await
}
pub async fn rescan_and_await(&mut self) -> Result<SyncResult, LightClientError> {
self.rescan().await?;
self.await_sync().await
}
}
#[cfg(test)]
pub mod test {
use crate::{lightclient::LightClient, wallet::disk::testing::examples};
pub(crate) async fn sync_example_wallet(
wallet_case: examples::NetworkSeedVersion,
) -> LightClient {
if let Err(e) = rustls::crypto::ring::default_provider().install_default() {
log::error!("Error installing crypto provider: {:?}", e)
};
let mut lc = wallet_case.load_example_wallet_with_client().await;
let sync_result = lc.sync_and_await().await.unwrap();
println!("{}", sync_result);
println!("{:?}", lc.do_balance().await);
lc
}
mod testnet {
use super::{examples, sync_example_wallet};
#[ignore = "live chain experiment"]
#[tokio::test]
async fn testnet_sync_mskmgdbhotbpetcjwcspgopp() {
sync_example_wallet(examples::NetworkSeedVersion::Testnet(
examples::TestnetSeedVersion::MobileShuffle(examples::MobileShuffleVersion::Latest),
))
.await;
}
#[ignore = "live chain experiment"]
#[tokio::test]
async fn testnet_sync_cbbhrwiilgbrababsshsmtpr() {
sync_example_wallet(examples::NetworkSeedVersion::Testnet(
examples::TestnetSeedVersion::ChimneyBetter(examples::ChimneyBetterVersion::Latest),
))
.await;
}
}
#[tokio::test]
#[ignore = "testnet and mainnet tests should be ignored due to increasingly large execution times"]
async fn mainnet_sync() {
sync_example_wallet(examples::NetworkSeedVersion::Mainnet(
examples::MainnetSeedVersion::HotelHumor(examples::HotelHumorVersion::Gf0aaf9347),
))
.await;
}
}