use WalletContext scheduler for walletpassphrase callback

This commit is contained in:
Matthew Zipkin committed 2025-07-03 06:26:13 -04:00
1 parent a92e8b10a5
commit 8a1765795f
2 files changed
+14 -11

No files matched your search

+10 -8
View File
@@ -3,6 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <rpc/util.h> #include <rpc/util.h>
#include <scheduler.h>
#include <wallet/context.h>
#include <wallet/rpc/util.h> #include <wallet/rpc/util.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
@@ -88,24 +90,24 @@ RPCHelpMan walletpassphrase()
relock_time = pwallet->nRelockTime; relock_time = pwallet->nRelockTime;
} }
// rpcRunLater must be called without cs_wallet held otherwise a deadlock // Get wallet scheduler to queue up the relock callback in the future.
// can occur. The deadlock would happen when RPCRunLater removes the // Scheduled events don't get destructed until they are executed,
// previous timer (and waits for the callback to finish if already running) // and they are executed in series in a single scheduler thread so
// and the callback locks cs_wallet. // no cs_wallet lock is needed.
AssertLockNotHeld(wallet->cs_wallet); WalletContext& context = EnsureWalletContext(request.context);
// Keep a weak pointer to the wallet so that it is possible to unload the // Keep a weak pointer to the wallet so that it is possible to unload the
// wallet before the following callback is called. If a valid shared pointer // wallet before the following callback is called. If a valid shared pointer
// is acquired in the callback then the wallet is still loaded. // is acquired in the callback then the wallet is still loaded.
std::weak_ptr<CWallet> weak_wallet = wallet; std::weak_ptr<CWallet> weak_wallet = wallet;
pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] { context.scheduler->scheduleFromNow([weak_wallet, relock_time] {
if (auto shared_wallet = weak_wallet.lock()) { if (auto shared_wallet = weak_wallet.lock()) {
LOCK2(shared_wallet->m_relock_mutex, shared_wallet->cs_wallet); LOCK2(shared_wallet->m_relock_mutex, shared_wallet->cs_wallet);
// Skip if this is not the most recent rpcRunLater callback. // Skip if this is not the most recent relock callback.
if (shared_wallet->nRelockTime != relock_time) return; if (shared_wallet->nRelockTime != relock_time) return;
shared_wallet->Lock(); shared_wallet->Lock();
shared_wallet->nRelockTime = 0; shared_wallet->nRelockTime = 0;
} }
}, nSleepTime); }, std::chrono::seconds(nSleepTime));
return UniValue::VNULL; return UniValue::VNULL;
}, },
+4 -3
View File
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the wallet keypool and interaction with wallet encryption/locking.""" """Test the wallet keypool and interaction with wallet encryption/locking."""
import time
from decimal import Decimal from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
@@ -127,8 +126,10 @@ class KeyPoolTest(BitcoinTestFramework):
nodes[0].keypoolrefill(3) nodes[0].keypoolrefill(3)
# test walletpassphrase timeout # test walletpassphrase timeout
time.sleep(1.1) # CScheduler relies on condition_variable::wait_until() which does not
assert_equal(nodes[0].getwalletinfo()["unlocked_until"], 0) # guarantee accurate timing. We'll wait up to 5 seconds to execute a 1
# second scheduled event.
nodes[0].wait_until(lambda: nodes[0].getwalletinfo()["unlocked_until"] == 0, timeout=5)
# drain the keypool # drain the keypool
for _ in range(3): for _ in range(3):