Skip to content

Commit

Permalink
feat(zcoin): allow ARRR to sync using a start date (#1922)
Browse files Browse the repository at this point in the history
Improve ARRR synchronization based on a user-selected date. This feature will enable users to specify a specific date as the starting point for synchronization as a substitute for the checkpoint block from config or syncing from the first block.

---------

Signed-off-by: borngraced <[email protected]>
  • Loading branch information
borngraced authored Sep 7, 2023
1 parent 96a53ce commit 1538564
Show file tree
Hide file tree
Showing 15 changed files with 521 additions and 109 deletions.
9 changes: 7 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ jobs:
uses: ./.github/actions/cargo-cache

- name: Test
run: cargo test --test 'mm2_tests_main' --no-fail-fast
run: |
wget -O - https://raw.githubusercontent.com/KomodoPlatform/komodo/master/zcutil/fetch-params-alt.sh | bash
cargo test --test 'mm2_tests_main' --no-fail-fast
# https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits
# https://github.com/KomodoPlatform/atomicDEX-API/actions/runs/4419618128/jobs/7748266141#step:4:1790
Expand Down Expand Up @@ -161,7 +163,10 @@ jobs:
uses: ./.github/actions/cargo-cache

- name: Test
run: cargo test --test 'mm2_tests_main' --no-fail-fast
run: |
Invoke-WebRequest -Uri https://github.com/KomodoPlatform/komodo/raw/d456be35acd1f8584e1e4f971aea27bd0644d5c5/zcutil/wget64.exe -OutFile \wget64.exe
Invoke-WebRequest -Uri https://raw.githubusercontent.com/KomodoPlatform/komodo/master/zcutil/fetch-params-alt.bat -OutFile \cmd.bat && \cmd.bat
cargo test --test 'mm2_tests_main' --no-fail-fast
docker-tests:
timeout-minutes: 90
Expand Down
2 changes: 1 addition & 1 deletion mm2src/coins/utxo/utxo_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod utxo_conf_builder;
pub use utxo_arc_builder::{MergeUtxoArcOps, UtxoArcBuilder};
pub use utxo_coin_builder::{UtxoCoinBuildError, UtxoCoinBuildResult, UtxoCoinBuilder, UtxoCoinBuilderCommonOps,
UtxoFieldsWithGlobalHDBuilder, UtxoFieldsWithHardwareWalletBuilder,
UtxoFieldsWithIguanaSecretBuilder};
UtxoFieldsWithIguanaSecretBuilder, DAY_IN_SECONDS};
pub use utxo_conf_builder::{UtxoConfBuilder, UtxoConfError, UtxoConfResult};

#[cfg(test)]
Expand Down
44 changes: 43 additions & 1 deletion mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use common::custom_futures::repeatable::{Ready, Retry};
use common::executor::{abortable_queue::AbortableQueue, AbortSettings, AbortableSystem, AbortedError, SpawnAbortable,
Timer};
use common::log::{error, info, LogOnError};
use common::small_rng;
use common::{now_sec, small_rng};
use crypto::{Bip32DerPathError, CryptoCtx, CryptoCtxError, GlobalHDAccountArc, HwWalletType, StandardHDPathError,
StandardHDPathToCoin};
use derive_more::Display;
Expand Down Expand Up @@ -44,6 +44,9 @@ cfg_native! {
use std::path::{Path, PathBuf};
}

/// Number of seconds in a day (24 hours * 60 * 60)
pub const DAY_IN_SECONDS: u64 = 86400;

pub type UtxoCoinBuildResult<T> = Result<T, MmError<UtxoCoinBuildError>>;

#[derive(Debug, Display)]
Expand Down Expand Up @@ -85,6 +88,7 @@ pub enum UtxoCoinBuildError {
Internal(String),
#[display(fmt = "SPV params verificaiton failed. Error: {_0}")]
SPVError(SPVError),
ErrorCalculatingStartingHeight(String),
}

impl From<UtxoConfError> for UtxoCoinBuildError {
Expand Down Expand Up @@ -685,6 +689,44 @@ pub trait UtxoCoinBuilderCommonOps {

(None, None)
}

/// Calculates the starting block height based on a given date and the current block height.
///
/// # Arguments
/// * `date`: The date in seconds representing the desired starting date.
/// * `current_block_height`: The current block height at the time of calculation.
///
fn calculate_starting_height_from_date(
&self,
date_s: u64,
current_block_height: u64,
) -> UtxoCoinBuildResult<Option<u64>> {
let avg_blocktime = self.conf()["avg_blocktime"]
.as_u64()
.ok_or_else(|| format!("avg_blocktime not specified in {} coin config", self.ticker()))
.map_to_mm(UtxoCoinBuildError::ErrorCalculatingStartingHeight)?;
let blocks_per_day = DAY_IN_SECONDS / avg_blocktime;
let current_time_s = now_sec();

if current_time_s < date_s {
return MmError::err(UtxoCoinBuildError::ErrorCalculatingStartingHeight(format!(
"{} sync date must be earlier then current date",
self.ticker()
)));
};

let secs_since_date = current_time_s - date_s;
let days_since_date = (secs_since_date / DAY_IN_SECONDS) - 1;
let blocks_to_sync = (days_since_date * blocks_per_day) + blocks_per_day;

if current_block_height < blocks_to_sync {
return Ok(None);
}

let block_to_sync_from = current_block_height - blocks_to_sync;

Ok(Some(block_to_sync_from))
}
}

/// Attempts to parse native daemon conf file and return rpcport, rpcuser and rpcpassword
Expand Down
57 changes: 35 additions & 22 deletions mm2src/coins/z_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ use z_htlc::{z_p2sh_spend, z_send_dex_fee, z_send_htlc};

mod z_rpc;
use z_rpc::init_light_client;
pub use z_rpc::SyncStatus;
pub use z_rpc::{FirstSyncBlock, SyncStatus};

cfg_native!(
use crate::{NumConversError, TransactionDetails, TxFeeDetails};
Expand Down Expand Up @@ -305,9 +305,15 @@ impl ZCoin {
#[inline]
pub fn consensus_params_ref(&self) -> &ZcoinConsensusParams { &self.z_fields.consensus_params }

/// Asynchronously checks the synchronization status and returns `true` if
/// the Sapling state has finished synchronizing, meaning that the block number is available.
/// Otherwise, it returns `false`.
#[inline]
pub async fn is_sapling_state_synced(&self) -> bool {
matches!(self.sync_status().await, Ok(SyncStatus::Finished { block_number: _ }))
matches!(
self.sync_status().await,
Ok(SyncStatus::Finished { block_number: _, .. })
)
}

#[inline]
Expand Down Expand Up @@ -755,14 +761,34 @@ impl AsRef<UtxoCoinFields> for ZCoin {
fn as_ref(&self) -> &UtxoCoinFields { &self.utxo_arc }
}

/// SyncStartPoint represents the starting point for synchronizing a wallet's blocks and transaction history.
/// This can be specified as a date, a block height, or starting from the earliest available data.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum SyncStartPoint {
/// Synchronize from a specific date (in Unix timestamp format).
Date(u64),
/// Synchronize from a specific block height.
Height(u64),
/// Synchronize from the earliest available data(`sapling_activation_height` from coin config).
Earliest,
}

// ZcoinRpcMode reprs available RPC modes for interacting with the Zcoin network. It includes
/// modes for both native and light client, each with their own configuration options.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(tag = "rpc", content = "rpc_data")]
pub enum ZcoinRpcMode {
#[cfg(not(target_arch = "wasm32"))]
Native,
#[serde(alias = "Electrum")]
Light {
#[serde(alias = "servers")]
electrum_servers: Vec<ElectrumRpcRequest>,
light_wallet_d_servers: Vec<String>,
/// Specifies the parameters for synchronizing the wallet from a specific block. This overrides the
/// `CheckPointBlockInfo` configuration in the coin settings.
sync_params: Option<SyncStartPoint>,
},
}

Expand Down Expand Up @@ -894,36 +920,23 @@ impl<'a> UtxoCoinBuilder for ZCoinBuilder<'a> {
);

let blocks_db = self.blocks_db().await?;
let wallet_db = WalletDbShared::new(&self, &z_spending_key)
.await
.map_err(|err| ZCoinBuildError::ZcashDBError(err.to_string()))?;

let (sync_state_connector, light_wallet_db) = match &self.z_coin_params.mode {
#[cfg(not(target_arch = "wasm32"))]
ZcoinRpcMode::Native => {
let native_client = self.native_client()?;
init_native_client(
self.ticker.into(),
native_client,
blocks_db,
wallet_db,
self.protocol_info.consensus_params.clone(),
self.z_coin_params.scan_blocks_per_iteration,
self.z_coin_params.scan_interval_ms,
)
.await?
init_native_client(&self, native_client, blocks_db, &z_spending_key).await?
},
ZcoinRpcMode::Light {
light_wallet_d_servers, ..
light_wallet_d_servers,
sync_params,
..
} => {
init_light_client(
self.ticker.into(),
&self,
light_wallet_d_servers.clone(),
blocks_db,
wallet_db,
self.protocol_info.consensus_params.clone(),
self.z_coin_params.scan_blocks_per_iteration,
self.z_coin_params.scan_interval_ms,
sync_params,
&z_spending_key,
)
.await?
},
Expand Down
11 changes: 11 additions & 0 deletions mm2src/coins/z_coin/storage/blockdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@ impl BlockDbImpl {

Ok(())
}

pub(crate) async fn get_earliest_block(&self) -> Result<u32, ZcashClientError> {
Ok(query_single_row(
&self.db.lock().unwrap(),
"SELECT MIN(height) from compactblocks",
[],
|row| row.get::<_, Option<u32>>(0),
)?
.flatten()
.unwrap_or(0))
}
}

#[cfg(not(target_arch = "wasm32"))]
Expand Down
7 changes: 4 additions & 3 deletions mm2src/coins/z_coin/storage/walletdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use mm2_err_handle::prelude::*;
use zcash_primitives::zip32::ExtendedSpendingKey;

cfg_native!(
use crate::z_coin::{ZcoinConsensusParams};
use crate::z_coin::{CheckPointBlockInfo, ZcoinConsensusParams};
use crate::z_coin::z_rpc::create_wallet_db;

use parking_lot::Mutex;
Expand Down Expand Up @@ -38,18 +38,19 @@ pub struct WalletDbShared {
impl<'a> WalletDbShared {
pub async fn new(
zcoin_builder: &ZCoinBuilder<'a>,
checkpoint_block: Option<CheckPointBlockInfo>,
z_spending_key: &ExtendedSpendingKey,
) -> MmResult<Self, WalletDbError> {
let wallet_db = create_wallet_db(
zcoin_builder
.db_dir_path
.join(format!("{}_wallet.db", zcoin_builder.ticker)),
zcoin_builder.protocol_info.consensus_params.clone(),
zcoin_builder.protocol_info.check_point_block.clone(),
checkpoint_block,
ExtendedFullViewingKey::from(z_spending_key),
)
.await
.map_err(|err| MmError::new(WalletDbError::ZcoinClientInitError(err.into_inner())))?;
.mm_err(WalletDbError::ZcoinClientInitError)?;

Ok(Self {
db: Arc::new(Mutex::new(wallet_db)),
Expand Down
12 changes: 12 additions & 0 deletions mm2src/coins/z_coin/z_coin_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json};
use zcash_client_sqlite::error::SqliteClientError;
use zcash_primitives::transaction::builder::Error as ZTxBuilderError;

/// Represents possible errors that might occur while interacting with Zcoin rpc.
#[derive(Debug, Display)]
#[non_exhaustive]
pub enum UpdateBlocksCacheErr {
Expand All @@ -27,6 +28,7 @@ pub enum UpdateBlocksCacheErr {
JsonRpcError(JsonRpcError),
GetLiveLightClientError(String),
ZcashDBError(String),
DecodeError(String),
}

#[cfg(not(target_arch = "wasm32"))]
Expand All @@ -52,13 +54,23 @@ impl From<JsonRpcError> for UpdateBlocksCacheErr {
fn from(err: JsonRpcError) -> Self { UpdateBlocksCacheErr::JsonRpcError(err) }
}

/// This enum encompasses various error scenarios that may arise
/// when configuring and activating a Zcoin, such as invalid
/// configuration settings, network connectivity issues, or other
/// initialization failures.
#[derive(Debug, Display)]
#[non_exhaustive]
pub enum ZcoinClientInitError {
ZcashDBError(String),
EmptyLightwalletdUris,
#[display(fmt = "Fail to init clients while iterating lightwalletd urls {:?}", _0)]
UrlIterFailure(Vec<UrlIterError>),
UpdateBlocksCacheErr(UpdateBlocksCacheErr),
UtxoCoinBuildError(UtxoCoinBuildError),
}

impl From<UpdateBlocksCacheErr> for ZcoinClientInitError {
fn from(err: UpdateBlocksCacheErr) -> Self { ZcoinClientInitError::UpdateBlocksCacheErr(err) }
}

#[cfg(not(target_arch = "wasm32"))]
Expand Down
Loading

0 comments on commit 1538564

Please sign in to comment.