Skip to content

Commit 66a86eb

Browse files
committed
wallet: keep track of when the passphrase is needed when rescanning
Wallet passphrases are needed to top up the keypool during a rescan. The following RPCs need the passphrase when rescanning: - `importdescriptors` - `rescanblockchain` The following RPCs use the information about whether or not the passphrase is being used to ensure that full rescans are able to take place: - `walletlock` - `encryptwallet` - `walletpassphrasechange`
1 parent 576e16e commit 66a86eb

File tree

4 files changed

+21
-3
lines changed

4 files changed

+21
-3
lines changed

src/wallet/rpc/backup.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1651,7 +1651,7 @@ RPCHelpMan importdescriptors()
16511651
}
16521652

16531653
WalletRescanReserver reserver(*pwallet);
1654-
if (!reserver.reserve()) {
1654+
if (!reserver.reserve(/*with_passphrase=*/true)) {
16551655
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
16561656
}
16571657

src/wallet/rpc/encrypt.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ RPCHelpMan walletpassphrasechange()
128128
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
129129
}
130130

131+
if (pwallet->IsScanningWithPassphrase()) {
132+
throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before changing the passphrase.");
133+
}
134+
131135
// TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
132136
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
133137
SecureString strOldWalletPass;
@@ -181,6 +185,10 @@ RPCHelpMan walletlock()
181185
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
182186
}
183187

188+
if (pwallet->IsScanningWithPassphrase()) {
189+
throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before locking the wallet.");
190+
}
191+
184192
pwallet->Lock();
185193
pwallet->nRelockTime = 0;
186194

@@ -229,6 +237,10 @@ RPCHelpMan encryptwallet()
229237
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
230238
}
231239

240+
if (pwallet->IsScanningWithPassphrase()) {
241+
throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before encrypting the wallet.");
242+
}
243+
232244
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
233245
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
234246
SecureString strWalletPass;

src/wallet/rpc/transactions.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,15 +872,17 @@ RPCHelpMan rescanblockchain()
872872
wallet.BlockUntilSyncedToCurrentChain();
873873

874874
WalletRescanReserver reserver(*pwallet);
875-
if (!reserver.reserve()) {
875+
if (!reserver.reserve(/*with_passphrase=*/true)) {
876876
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
877877
}
878878

879879
int start_height = 0;
880880
std::optional<int> stop_height;
881881
uint256 start_block;
882+
882883
{
883884
LOCK(pwallet->cs_wallet);
885+
EnsureWalletIsUnlocked(*pwallet);
884886
int tip_height = pwallet->GetLastBlockHeight();
885887

886888
if (!request.params[0].isNull()) {

src/wallet/wallet.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
243243
std::atomic<bool> fAbortRescan{false};
244244
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
245245
std::atomic<bool> m_attaching_chain{false};
246+
std::atomic<bool> m_scanning_with_passphrase{false};
246247
std::atomic<int64_t> m_scanning_start{0};
247248
std::atomic<double> m_scanning_progress{0};
248249
friend class WalletRescanReserver;
@@ -467,6 +468,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
467468
void AbortRescan() { fAbortRescan = true; }
468469
bool IsAbortingRescan() const { return fAbortRescan; }
469470
bool IsScanning() const { return fScanningWallet; }
471+
bool IsScanningWithPassphrase() const { return m_scanning_with_passphrase; }
470472
int64_t ScanningDuration() const { return fScanningWallet ? GetTimeMillis() - m_scanning_start : 0; }
471473
double ScanningProgress() const { return fScanningWallet ? (double) m_scanning_progress : 0; }
472474

@@ -960,12 +962,13 @@ class WalletRescanReserver
960962
public:
961963
explicit WalletRescanReserver(CWallet& w) : m_wallet(w) {}
962964

963-
bool reserve()
965+
bool reserve(bool with_passphrase = false)
964966
{
965967
assert(!m_could_reserve);
966968
if (m_wallet.fScanningWallet.exchange(true)) {
967969
return false;
968970
}
971+
m_wallet.m_scanning_with_passphrase.exchange(with_passphrase);
969972
m_wallet.m_scanning_start = GetTimeMillis();
970973
m_wallet.m_scanning_progress = 0;
971974
m_could_reserve = true;
@@ -985,6 +988,7 @@ class WalletRescanReserver
985988
{
986989
if (m_could_reserve) {
987990
m_wallet.fScanningWallet = false;
991+
m_wallet.m_scanning_with_passphrase = false;
988992
}
989993
}
990994
};

0 commit comments

Comments
 (0)