Reset Orchard wallet state when rescanning from below NU5 activation.
This commit is contained in:
parent
b78c2732ab
commit
6b8ecc8a31
|
@ -30,6 +30,15 @@ OrchardWalletPtr* orchard_wallet_new();
|
|||
*/
|
||||
void orchard_wallet_free(OrchardWalletPtr* wallet);
|
||||
|
||||
/**
|
||||
* Reset the state of the wallet to be suitable for rescan from the NU5 activation
|
||||
* height. This removes all witness and spentness information from the wallet. The
|
||||
* keystore is unmodified and decrypted note, nullifier, and conflict data are left
|
||||
* in place with the expectation that they will be overwritten and/or updated in
|
||||
* the rescan process.
|
||||
*/
|
||||
bool orchard_wallet_reset(OrchardWalletPtr* wallet);
|
||||
|
||||
/**
|
||||
* Adds a checkpoint to the wallet's note commitment tree to enable
|
||||
* a future rewind.
|
||||
|
|
|
@ -487,6 +487,12 @@ pub extern "C" fn orchard_wallet_free(wallet: *mut Wallet) {
|
|||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_wallet_reset(wallet: *mut Wallet) {
|
||||
let wallet = unsafe { wallet.as_mut() }.expect("Wallet pointer may not be null");
|
||||
wallet.reset();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_wallet_checkpoint(wallet: *mut Wallet, block_height: u32) -> bool {
|
||||
let wallet = unsafe { wallet.as_mut() }.expect("Wallet pointer may not be null");
|
||||
|
|
|
@ -66,19 +66,27 @@ public:
|
|||
OrchardWallet& operator=(const OrchardWallet&) = delete;
|
||||
|
||||
/**
|
||||
* Checkpoint the note commitment tree. This returns `false` and leaves the
|
||||
* note commitment tree unmodified if the block height does not match the
|
||||
* last block height scanned for transactions. This must be called exactly
|
||||
* once per block.
|
||||
* Reset the state of the wallet to be suitable for rescan from the NU5 activation
|
||||
* height. This removes all witness and spentness information from the wallet. The
|
||||
* keystore is unmodified and decrypted note, nullifier, and conflict data are left
|
||||
* in place with the expectation that they will be overwritten and/or updated in the
|
||||
* rescan process.
|
||||
*/
|
||||
bool Reset() {
|
||||
return orchard_wallet_reset(inner.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkpoint the note commitment tree. This returns `false` and leaves the note
|
||||
* commitment tree unmodified if the block height does not match the last block
|
||||
* height scanned for transactions. This must be called exactly once per block.
|
||||
*/
|
||||
bool CheckpointNoteCommitmentTree(int nBlockHeight) {
|
||||
return orchard_wallet_checkpoint(inner.get(), (uint32_t) nBlockHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind to the most recent checkpoint, and mark as unspent any notes
|
||||
* previously identified as having been spent by transactions in the
|
||||
* latest block.
|
||||
* Return whether the orchard note commitment tree contains any checkpoints.
|
||||
*/
|
||||
bool IsCheckpointed() const {
|
||||
return orchard_wallet_is_checkpointed(inner.get());
|
||||
|
|
|
@ -3937,11 +3937,11 @@ int CWallet::ScanForWalletTransactions(
|
|||
// checkpoints. Note data will be restored by the calls to AddToWalletIfInvolvingMe,
|
||||
// and then the call to `ChainTipAdded` that later occurs for each block will restore
|
||||
// the witness data that is being removed in the rewind here.
|
||||
auto nu5_height = chainParams.GetConsensus().GetActivationHeight(Consensus::UPGRADE_NU5);
|
||||
bool performOrchardWalletUpdates{false};
|
||||
if (orchardWallet.IsCheckpointed()) {
|
||||
// We have a checkpoint, so attempt to rewind the Orchard wallet at most as
|
||||
// far as the NU5 activation block.
|
||||
auto nu5_height = chainParams.GetConsensus().GetActivationHeight(Consensus::UPGRADE_NU5);
|
||||
// If there's no activation height, we shouldn't have a checkpoint already,
|
||||
// and this is a programming error.
|
||||
assert(nu5_height.has_value());
|
||||
|
@ -3962,6 +3962,11 @@ int CWallet::ScanForWalletTransactions(
|
|||
throw std::runtime_error("CWallet::ScanForWalletTransactions(): Orchard wallet is out of sync. Please restart your node with -rescan.");
|
||||
}
|
||||
}
|
||||
} else if (isInitScan && pindex->nHeight < nu5_height) {
|
||||
// If it's the initial scan and we're starting below the nu5 activation
|
||||
// height, we're effectively rescanning from genesis and so it's safe
|
||||
// to update the note commitment tree as we progress.
|
||||
performOrchardWalletUpdates = true;
|
||||
}
|
||||
|
||||
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
|
||||
|
@ -6015,8 +6020,17 @@ bool CWallet::InitLoadWallet(const CChainParams& params, bool clearWitnessCaches
|
|||
return UIError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
|
||||
}
|
||||
|
||||
// If a rescan would begin at a point before NU5 activation height, reset
|
||||
// the Orchard wallet state to empty.
|
||||
if (pindexRescan->nHeight <= Params().GetConsensus().GetActivationHeight(Consensus::UPGRADE_NU5)) {
|
||||
walletInstance->orchardWallet.Reset();
|
||||
}
|
||||
|
||||
uiInterface.InitMessage(_("Rescanning..."));
|
||||
LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight);
|
||||
LogPrintf(
|
||||
"Rescanning last %i blocks (from block %i)...\n",
|
||||
chainActive.Height() - pindexRescan->nHeight,
|
||||
pindexRescan->nHeight);
|
||||
nStart = GetTimeMillis();
|
||||
walletInstance->ScanForWalletTransactions(pindexRescan, true, true);
|
||||
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
|
||||
|
|
Loading…
Reference in New Issue