Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LightWalletClient.Rescan method #383

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/Nerdbank.Zcash/LightWalletClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,25 @@ public IEnumerable<ZcashAccount> GetAccounts()
return this.accountIds.Keys;
}

/// <summary>
/// Gets the minimum block height corresponding to an unspent note in the wallet.
/// </summary>
/// <returns>The minimum block height, or <see langword="null" /> if there are no unspent notes.</returns>
public uint? GetMinUnspentHeight() => LightWalletMethods.GetMinUnspentHeight(this.dbinit);

/// <summary>
/// Invalidates any scanned blocks beyond a given block.
/// </summary>
/// <param name="blockHeight">
/// The height of the last block to consider valid.
/// This must either be within 100 blocks of the current tip (<see cref="GetLatestBlockHeightAsync(CancellationToken)"/>,
/// or less than the height of the oldest unspent note in the wallet (<see cref="GetMinUnspentHeight"/>).
/// </param>
/// <remarks>
/// This method can be very slow, and should not be run concurrently with <see cref="DownloadTransactionsAsync"/>.
/// </remarks>
public void Rescan(uint blockHeight) => LightWalletMethods.Rescan(this.dbinit, blockHeight);

/// <summary>
/// Records a diversifier index as having been used by the given account.
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions src/Nerdbank.Zcash/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,12 @@ Nerdbank.Zcash.LightWalletClient.GetBalances(Nerdbank.Zcash.ZcashAccount! accoun
Nerdbank.Zcash.LightWalletClient.GetBirthdayHeights(Nerdbank.Zcash.ZcashAccount! account) -> Nerdbank.Zcash.LightWalletClient.BirthdayHeights
Nerdbank.Zcash.LightWalletClient.GetDownloadedTransactions(Nerdbank.Zcash.ZcashAccount! account, uint startingBlock = 0) -> System.Collections.Generic.List<Nerdbank.Zcash.Transaction!>!
Nerdbank.Zcash.LightWalletClient.GetLatestBlockHeightAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask<uint>
Nerdbank.Zcash.LightWalletClient.GetMinUnspentHeight() -> uint?
Nerdbank.Zcash.LightWalletClient.GetUnshieldedBalances(Nerdbank.Zcash.ZcashAccount! account) -> System.Collections.Generic.IReadOnlyList<(Nerdbank.Zcash.TransparentAddress! Address, decimal Balance)>!
Nerdbank.Zcash.LightWalletClient.LastDownloadHeight.get -> uint?
Nerdbank.Zcash.LightWalletClient.LightWalletClient(System.Uri! serverUrl, Nerdbank.Zcash.ZcashNetwork network, string! dataFile) -> void
Nerdbank.Zcash.LightWalletClient.Network.get -> Nerdbank.Zcash.ZcashNetwork
Nerdbank.Zcash.LightWalletClient.Rescan(uint blockHeight) -> void
Nerdbank.Zcash.LightWalletClient.SendAsync(Nerdbank.Zcash.ZcashAccount! account, System.Collections.Generic.IReadOnlyCollection<Nerdbank.Zcash.Transaction.LineItem>! payments, System.IProgress<Nerdbank.Zcash.LightWalletClient.SendProgress!>? progress, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<System.ReadOnlyMemory<Nerdbank.Zcash.TxId>>!
Nerdbank.Zcash.LightWalletClient.SendProgress
Nerdbank.Zcash.LightWalletClient.SendProgress.Deconstruct(out uint Id, out bool IsSendInProgress, out uint Progress, out uint Total, out string? LastError, out string? LastTransactionId) -> void
Expand Down
73 changes: 73 additions & 0 deletions src/Nerdbank.Zcash/RustBindings/LightWallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,12 @@ public static extern uint uniffi_nerdbank_zcash_rust_fn_func_get_block_height(
ref RustCallStatus _uniffi_out_err
);

[DllImport("nerdbank_zcash_rust")]
public static extern RustBuffer uniffi_nerdbank_zcash_rust_fn_func_get_min_unspent_height(
RustBuffer @config,
ref RustCallStatus _uniffi_out_err
);

[DllImport("nerdbank_zcash_rust")]
public static extern RustBuffer uniffi_nerdbank_zcash_rust_fn_func_get_sync_height(
RustBuffer @config,
Expand Down Expand Up @@ -668,6 +674,13 @@ public static extern void uniffi_nerdbank_zcash_rust_fn_func_init(
ref RustCallStatus _uniffi_out_err
);

[DllImport("nerdbank_zcash_rust")]
public static extern void uniffi_nerdbank_zcash_rust_fn_func_rescan(
RustBuffer @config,
uint @blockHeight,
ref RustCallStatus _uniffi_out_err
);

[DllImport("nerdbank_zcash_rust")]
public static extern RustBuffer uniffi_nerdbank_zcash_rust_fn_func_send(
RustBuffer @config,
Expand Down Expand Up @@ -994,6 +1007,9 @@ ref RustCallStatus _uniffi_out_err
[DllImport("nerdbank_zcash_rust")]
public static extern ushort uniffi_nerdbank_zcash_rust_checksum_func_get_block_height();

[DllImport("nerdbank_zcash_rust")]
public static extern ushort uniffi_nerdbank_zcash_rust_checksum_func_get_min_unspent_height();

[DllImport("nerdbank_zcash_rust")]
public static extern ushort uniffi_nerdbank_zcash_rust_checksum_func_get_sync_height();

Expand All @@ -1012,6 +1028,9 @@ ref RustCallStatus _uniffi_out_err
[DllImport("nerdbank_zcash_rust")]
public static extern ushort uniffi_nerdbank_zcash_rust_checksum_func_init();

[DllImport("nerdbank_zcash_rust")]
public static extern ushort uniffi_nerdbank_zcash_rust_checksum_func_rescan();

[DllImport("nerdbank_zcash_rust")]
public static extern ushort uniffi_nerdbank_zcash_rust_checksum_func_send();

Expand Down Expand Up @@ -1124,6 +1143,16 @@ static void uniffiCheckApiChecksums()
);
}
}
{
var checksum =
_UniFFILib.uniffi_nerdbank_zcash_rust_checksum_func_get_min_unspent_height();
if (checksum != 51541)
{
throw new UniffiContractChecksumException(
$"uniffi.LightWallet: uniffi bindings expected function `uniffi_nerdbank_zcash_rust_checksum_func_get_min_unspent_height` checksum `51541`, library returned `{checksum}`"
);
}
}
{
var checksum = _UniFFILib.uniffi_nerdbank_zcash_rust_checksum_func_get_sync_height();
if (checksum != 61447)
Expand Down Expand Up @@ -1180,6 +1209,15 @@ static void uniffiCheckApiChecksums()
);
}
}
{
var checksum = _UniFFILib.uniffi_nerdbank_zcash_rust_checksum_func_rescan();
if (checksum != 9888)
{
throw new UniffiContractChecksumException(
$"uniffi.LightWallet: uniffi bindings expected function `uniffi_nerdbank_zcash_rust_checksum_func_rescan` checksum `9888`, library returned `{checksum}`"
);
}
}
{
var checksum = _UniFFILib.uniffi_nerdbank_zcash_rust_checksum_func_send();
if (checksum != 5940)
Expand Down Expand Up @@ -3180,6 +3218,21 @@ ref _status
);
}

/// <exception cref="LightWalletException"></exception>
public static uint? GetMinUnspentHeight(DbInit @config)
{
return FfiConverterOptionalUInt32.INSTANCE.Lift(
_UniffiHelpers.RustCallWithError(
FfiConverterTypeLightWalletException.INSTANCE,
(ref RustCallStatus _status) =>
_UniFFILib.uniffi_nerdbank_zcash_rust_fn_func_get_min_unspent_height(
FfiConverterTypeDbInit.INSTANCE.Lower(@config),
ref _status
)
)
);
}

/// <exception cref="LightWalletException"></exception>
public static uint? GetSyncHeight(DbInit @config)
{
Expand Down Expand Up @@ -3288,6 +3341,26 @@ ref _status
);
}

/// <summary>
/// Resets the database to consider the specified block height to be the tip, such that the next
/// scan begins from there.
/// The block height must be either within 100 of the current tip (get_block_height)
/// so no greater than the oldest unspent input (get_min_unspent_height).
/// </summary>
/// <exception cref="LightWalletException"></exception>
public static void Rescan(DbInit @config, uint @blockHeight)
{
_UniffiHelpers.RustCallWithError(
FfiConverterTypeLightWalletException.INSTANCE,
(ref RustCallStatus _status) =>
_UniFFILib.uniffi_nerdbank_zcash_rust_fn_func_rescan(
FfiConverterTypeDbInit.INSTANCE.Lower(@config),
FfiConverterUInt32.INSTANCE.Lower(@blockHeight),
ref _status
)
);
}

/// <exception cref="LightWalletException"></exception>
public static List<SendTransactionResult> Send(
DbInit @config,
Expand Down
10 changes: 10 additions & 0 deletions src/nerdbank-zcash-rust/src/ffi.udl
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ namespace LightWallet {
[Throws=LightWalletError]
u32? get_birthday_height(DbInit config);

[Throws=LightWalletError]
u32? get_min_unspent_height(DbInit config);

[Throws=LightWalletError]
u32 get_block_height(string uri, CancellationSource? cancellation);

Expand Down Expand Up @@ -162,4 +165,11 @@ namespace LightWallet {

[Throws=LightWalletError]
string add_diversifier(DbInit config, u32 account_id, bytes diversifier_index);

/// Resets the database to consider the specified block height to be the tip, such that the next
/// scan begins from there.
/// The block height must be either within 100 of the current tip (get_block_height)
/// so no greater than the oldest unspent input (get_min_unspent_height).
[Throws=LightWalletError]
void rescan(DbInit config, u32 block_height);
};
21 changes: 19 additions & 2 deletions src/nerdbank-zcash-rust/src/interop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ use secrecy::SecretVec;
use tokio::runtime::Runtime;
use tokio_util::sync::CancellationToken;
use zcash_client_backend::{
data_api::{Account, AccountPurpose, WalletRead},
data_api::{Account, AccountPurpose, WalletRead, WalletWrite},
encoding::AddressCodec,
keys::{Era, UnifiedSpendingKey},
};
use zcash_client_sqlite::error::SqliteClientError;
use zcash_keys::keys::UnifiedFullViewingKey;
use zcash_primitives::{consensus::Network, legacy::TransparentAddress, zip32::DiversifierIndex};
use zcash_primitives::{
consensus::{BlockHeight, Network},
legacy::TransparentAddress,
zip32::DiversifierIndex,
};

use crate::{
analysis::{BirthdayHeights, UserBalances},
Expand Down Expand Up @@ -577,6 +581,19 @@ pub fn shield(
})
}

pub fn rescan(config: DbInit, block_height: u32) -> Result<(), LightWalletError> {
let network = config.network.into();
let mut db = Db::init(config.data_file, network)?;
db.data
.truncate_to_height(BlockHeight::from_u32(block_height))?;
Ok(())
}

pub fn get_min_unspent_height(config: DbInit) -> Result<Option<u32>, LightWalletError> {
let db = Db::load(config.data_file, config.network.into())?;
Ok(db.data.get_min_unspent_height()?.map(|h| h.into()))
}

#[cfg(test)]
mod tests {
use crate::test_constants::setup_test;
Expand Down
10 changes: 5 additions & 5 deletions src/nerdbank-zcash-rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ mod test_constants;
use analysis::{BirthdayHeights, UserBalances};
use interop::{
add_account, add_diversifier, cancel, disconnect_server, get_accounts, get_birthday_height,
get_birthday_heights, get_block_height, get_sync_height, get_transactions,
get_unshielded_utxos, get_user_balances, import_account_ufvk, init, send, shield,
simulate_send, sync, AccountInfo, CancellationSource, ChainType, DbInit, LightWalletError,
Pool, SendDetails, SendTransactionResult, SyncUpdate, SyncUpdateData, Transaction,
TransactionNote, TransactionSendDetail, TransparentNote,
get_birthday_heights, get_block_height, get_min_unspent_height, get_sync_height,
get_transactions, get_unshielded_utxos, get_user_balances, import_account_ufvk, init, rescan,
send, shield, simulate_send, sync, AccountInfo, CancellationSource, ChainType, DbInit,
LightWalletError, Pool, SendDetails, SendTransactionResult, SyncUpdate, SyncUpdateData,
Transaction, TransactionNote, TransactionSendDetail, TransparentNote,
};