Use boost::asio::deadline_timer for walletpassphrase timeout
New method in bitcoinrpc: RunLater, that uses a map of deadline
timers to run a function later.
Behavior of walletpassphrase is changed; before, calling
walletpassphrase again before the lock timeout passed
would result in: Error: Wallet is already unlocked.
You would have to call lockwallet before walletpassphrase.
Now: the last walletpassphrase with correct password
wins, and overrides any previous timeout.
Fixes issue# 1961 which was caused by spawning too many threads.
Test plan:
Start with encrypted wallet, password 'foo'
NOTE:
python -c 'import time; print("%d"%time.time())'
... will tell you current unix timestamp.
Try:
walletpassphrase foo 600
getinfo
EXPECT: unlocked_until is about 10 minutes in the future
walletpassphrase foo 1
sleep 2
sendtoaddress mun74Bvba3B1PF2YkrF4NsgcJwHXXh12LF 11
EXPECT: Error: Please enter the wallet passphrase with walletpassphrase first.
walletpassphrase foo 600
walletpassphrase foo 0
getinfo
EXPECT: wallet is locked (unlocked_until is 0)
walletpassphrase foo 10
walletpassphrase foo 600
getinfo
EXPECT: wallet is unlocked until 10 minutes in future
walletpassphrase foo 60
walletpassphrase bar 600
EXPECT: Error, incorrect passphrase
getinfo
EXPECT: wallet still scheduled to lock 60 seconds from first (successful) walletpassphrase
This commit is contained in:
@@ -84,7 +84,7 @@ Value getinfo(const Array& params, bool fHelp)
|
||||
obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
|
||||
obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
|
||||
if (pwalletMain->IsCrypted())
|
||||
obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
|
||||
obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
|
||||
obj.push_back(Pair("errors", GetWarnings("statusbar")));
|
||||
return obj;
|
||||
}
|
||||
@@ -1256,56 +1256,11 @@ Value keypoolrefill(const Array& params, bool fHelp)
|
||||
}
|
||||
|
||||
|
||||
void ThreadTopUpKeyPool(void* parg)
|
||||
static void LockWallet(CWallet* pWallet)
|
||||
{
|
||||
// Make this thread recognisable as the key-topping-up thread
|
||||
RenameThread("bitcoin-key-top");
|
||||
|
||||
pwalletMain->TopUpKeyPool();
|
||||
}
|
||||
|
||||
void ThreadCleanWalletPassphrase(void* parg)
|
||||
{
|
||||
// Make this thread recognisable as the wallet relocking thread
|
||||
RenameThread("bitcoin-lock-wa");
|
||||
|
||||
int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
|
||||
|
||||
ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
|
||||
|
||||
if (nWalletUnlockTime == 0)
|
||||
{
|
||||
nWalletUnlockTime = nMyWakeTime;
|
||||
|
||||
do
|
||||
{
|
||||
if (nWalletUnlockTime==0)
|
||||
break;
|
||||
int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
|
||||
if (nToSleep <= 0)
|
||||
break;
|
||||
|
||||
LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
|
||||
MilliSleep(nToSleep);
|
||||
ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
|
||||
|
||||
} while(1);
|
||||
|
||||
if (nWalletUnlockTime)
|
||||
{
|
||||
nWalletUnlockTime = 0;
|
||||
pwalletMain->Lock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nWalletUnlockTime < nMyWakeTime)
|
||||
nWalletUnlockTime = nMyWakeTime;
|
||||
}
|
||||
|
||||
LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
|
||||
|
||||
delete (int64*)parg;
|
||||
LOCK(cs_nWalletUnlockTime);
|
||||
nWalletUnlockTime = 0;
|
||||
pWallet->Lock();
|
||||
}
|
||||
|
||||
Value walletpassphrase(const Array& params, bool fHelp)
|
||||
@@ -1319,9 +1274,6 @@ Value walletpassphrase(const Array& params, bool fHelp)
|
||||
if (!pwalletMain->IsCrypted())
|
||||
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
|
||||
|
||||
if (!pwalletMain->IsLocked())
|
||||
throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked.");
|
||||
|
||||
// Note that the walletpassphrase is stored in params[0] which is not mlock()ed
|
||||
SecureString strWalletPass;
|
||||
strWalletPass.reserve(100);
|
||||
@@ -1339,9 +1291,12 @@ Value walletpassphrase(const Array& params, bool fHelp)
|
||||
"walletpassphrase <passphrase> <timeout>\n"
|
||||
"Stores the wallet decryption key in memory for <timeout> seconds.");
|
||||
|
||||
NewThread(ThreadTopUpKeyPool, NULL);
|
||||
int64* pnSleepTime = new int64(params[1].get_int64());
|
||||
NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
|
||||
pwalletMain->TopUpKeyPool();
|
||||
|
||||
int64 nSleepTime = params[1].get_int64();
|
||||
LOCK(cs_nWalletUnlockTime);
|
||||
nWalletUnlockTime = GetTime() + nSleepTime;
|
||||
RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime);
|
||||
|
||||
return Value::null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user