Merge bitcoin/bitcoin#27405: util: Use steady clock instead of system clock to measure durations

fa83fb3161 wallet: Use steady clock to calculate number of derive iterations (MarcoFalke)
fa2c099cec wallet: Use steady clock to measure scanning duration (MarcoFalke)
fa97621804 qt: Use steady clock to throttle GUI notifications (MarcoFalke)
fa1d8044ab test: Use steady clock in index tests (MarcoFalke)
fa454dcb20 net: Use steady clock in InterruptibleRecv (MarcoFalke)

Pull request description:

  `GetTimeMillis` has multiple issues:

  * It doesn't denote the underlying clock type
  * It isn't type-safe
  * It is used incorrectly in places that should use a steady clock

  Fix all issues here.

ACKs for top commit:
  willcl-ark:
    ACK fa83fb3161
  martinus:
    Code review ACK fa83fb3161, also ran all tests. All usages of the steady_clock are just for duration measurements, so the change to a different epoch is ok.

Tree-SHA512: 5ec4fede8c7f97e2e08863c011856e8304f16ba30a68fdeb42f96a50a04961092cbe46ccf9ea6ac99ff5203c09f9e0924eb483eb38d7df0759addc85116c8a9f
This commit is contained in:
fanquake 2023-05-06 11:49:34 +01:00
commit e460c0a24a
No known key found for this signature in database
GPG Key ID: 2EEB9F5CC09526C1
8 changed files with 37 additions and 35 deletions

View File

@ -36,8 +36,8 @@ static Proxy nameProxy GUARDED_BY(g_proxyinfo_mutex);
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
bool fNameLookup = DEFAULT_NAME_LOOKUP;
// Need ample time for negotiation for very slow proxies such as Tor (milliseconds)
int g_socks5_recv_timeout = 20 * 1000;
// Need ample time for negotiation for very slow proxies such as Tor
std::chrono::milliseconds g_socks5_recv_timeout = 20s;
static std::atomic<bool> interruptSocks5Recv(false);
std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup)
@ -296,7 +296,7 @@ enum class IntrRecvError {
*
* @param data The buffer where the read bytes should be stored.
* @param len The number of bytes to read into the specified buffer.
* @param timeout The total timeout in milliseconds for this read.
* @param timeout The total timeout for this read.
* @param sock The socket (has to be in non-blocking mode) from which to read bytes.
*
* @returns An IntrRecvError indicating the resulting status of this read.
@ -306,10 +306,10 @@ enum class IntrRecvError {
* @see This function can be interrupted by calling InterruptSocks5(bool).
* Sockets can be made non-blocking with Sock::SetNonBlocking().
*/
static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const Sock& sock)
static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, std::chrono::milliseconds timeout, const Sock& sock)
{
int64_t curTime = GetTimeMillis();
int64_t endTime = curTime + timeout;
auto curTime{Now<SteadyMilliseconds>()};
const auto endTime{curTime + timeout};
while (len > 0 && curTime < endTime) {
ssize_t ret = sock.Recv(data, len, 0); // Optimistically try the recv first
if (ret > 0) {
@ -333,7 +333,7 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
}
if (interruptSocks5Recv)
return IntrRecvError::Interrupted;
curTime = GetTimeMillis();
curTime = Now<SteadyMilliseconds>();
}
return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout;
}

View File

@ -28,8 +28,8 @@
#include <QThread>
#include <QTimer>
static int64_t nLastHeaderTipUpdateNotification = 0;
static int64_t nLastBlockTipUpdateNotification = 0;
static SteadyClock::time_point g_last_header_tip_update_notification{};
static SteadyClock::time_point g_last_block_tip_update_notification{};
ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) :
QObject(parent),
@ -222,9 +222,9 @@ void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockT
// Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
const bool throttle = (sync_state != SynchronizationState::POST_INIT && synctype == SyncType::BLOCK_SYNC) || sync_state == SynchronizationState::INIT_REINDEX;
const int64_t now = throttle ? GetTimeMillis() : 0;
int64_t& nLastUpdateNotification = synctype != SyncType::BLOCK_SYNC ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
if (throttle && now < nLastUpdateNotification + count_milliseconds(MODEL_UPDATE_DELAY)) {
const auto now{throttle ? SteadyClock::now() : SteadyClock::time_point{}};
auto& nLastUpdateNotification = synctype != SyncType::BLOCK_SYNC ? g_last_header_tip_update_notification : g_last_block_tip_update_notification;
if (throttle && now < nLastUpdateNotification + MODEL_UPDATE_DELAY) {
return;
}

View File

@ -141,10 +141,10 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
BOOST_REQUIRE(filter_index.Start());
// Allow filter index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
int64_t time_start = GetTimeMillis();
constexpr auto timeout{10s};
const auto time_start{SteadyClock::now()};
while (!filter_index.BlockUntilSyncedToCurrentChain()) {
BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
BOOST_REQUIRE(time_start + timeout > SteadyClock::now());
UninterruptibleSleep(std::chrono::milliseconds{100});
}

View File

@ -14,11 +14,11 @@
#include <string>
#include <vector>
namespace {
int default_socks5_recv_timeout;
};
extern std::chrono::milliseconds g_socks5_recv_timeout;
extern int g_socks5_recv_timeout;
namespace {
decltype(g_socks5_recv_timeout) default_socks5_recv_timeout;
};
void initialize_socks5()
{
@ -35,7 +35,7 @@ FUZZ_TARGET_INIT(socks5, initialize_socks5)
InterruptSocks5(fuzzed_data_provider.ConsumeBool());
// Set FUZZED_SOCKET_FAKE_LATENCY=1 to exercise recv timeout code paths. This
// will slow down fuzzing.
g_socks5_recv_timeout = (fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) ? 1 : default_socks5_recv_timeout;
g_socks5_recv_timeout = (fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) ? 1ms : default_socks5_recv_timeout;
FuzzedSock fuzzed_sock = ConsumeSock(fuzzed_data_provider);
// This Socks5(...) fuzzing harness would have caught CVE-2017-18350 within
// a few seconds of fuzzing.

View File

@ -32,10 +32,10 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
BOOST_REQUIRE(txindex.Start());
// Allow tx index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
int64_t time_start = GetTimeMillis();
constexpr auto timeout{10s};
const auto time_start{SteadyClock::now()};
while (!txindex.BlockUntilSyncedToCurrentChain()) {
BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
BOOST_REQUIRE(time_start + timeout > SteadyClock::now());
UninterruptibleSleep(std::chrono::milliseconds{100});
}

View File

@ -122,7 +122,7 @@ static RPCHelpMan getwalletinfo()
obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
if (pwallet->IsScanning()) {
UniValue scanning(UniValue::VOBJ);
scanning.pushKV("duration", pwallet->ScanningDuration() / 1000);
scanning.pushKV("duration", Ticks<std::chrono::seconds>(pwallet->ScanningDuration()));
scanning.pushKV("progress", pwallet->ScanningProgress());
obj.pushKV("scanning", scanning);
} else {

View File

@ -559,13 +559,14 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
return false;
if (Unlock(_vMasterKey))
{
int64_t nStartTime = GetTimeMillis();
constexpr MillisecondsDouble target{100};
auto start{SteadyClock::now()};
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations = static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))));
pMasterKey.second.nDeriveIterations = static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * target / (SteadyClock::now() - start));
nStartTime = GetTimeMillis();
start = SteadyClock::now();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2;
pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * target / (SteadyClock::now() - start))) / 2;
if (pMasterKey.second.nDeriveIterations < 25000)
pMasterKey.second.nDeriveIterations = 25000;
@ -762,13 +763,14 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
GetStrongRandBytes(kMasterKey.vchSalt);
CCrypter crypter;
int64_t nStartTime = GetTimeMillis();
constexpr MillisecondsDouble target{100};
auto start{SteadyClock::now()};
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations = static_cast<unsigned int>(2500000 / ((double)(GetTimeMillis() - nStartTime)));
kMasterKey.nDeriveIterations = static_cast<unsigned int>(25000 * target / (SteadyClock::now() - start));
nStartTime = GetTimeMillis();
start = SteadyClock::now();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + static_cast<unsigned int>(kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2;
kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + static_cast<unsigned int>(kMasterKey.nDeriveIterations * target / (SteadyClock::now() - start))) / 2;
if (kMasterKey.nDeriveIterations < 25000)
kMasterKey.nDeriveIterations = 25000;

View File

@ -284,7 +284,7 @@ private:
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
std::atomic<bool> m_attaching_chain{false};
std::atomic<bool> m_scanning_with_passphrase{false};
std::atomic<int64_t> m_scanning_start{0};
std::atomic<SteadyClock::time_point> m_scanning_start{SteadyClock::time_point{}};
std::atomic<double> m_scanning_progress{0};
friend class WalletRescanReserver;
@ -505,7 +505,7 @@ public:
bool IsAbortingRescan() const { return fAbortRescan; }
bool IsScanning() const { return fScanningWallet; }
bool IsScanningWithPassphrase() const { return m_scanning_with_passphrase; }
int64_t ScanningDuration() const { return fScanningWallet ? GetTimeMillis() - m_scanning_start : 0; }
SteadyClock::duration ScanningDuration() const { return fScanningWallet ? SteadyClock::now() - m_scanning_start.load() : SteadyClock::duration{}; }
double ScanningProgress() const { return fScanningWallet ? (double) m_scanning_progress : 0; }
//! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo
@ -1014,7 +1014,7 @@ public:
return false;
}
m_wallet.m_scanning_with_passphrase.exchange(with_passphrase);
m_wallet.m_scanning_start = GetTimeMillis();
m_wallet.m_scanning_start = SteadyClock::now();
m_wallet.m_scanning_progress = 0;
m_could_reserve = true;
return true;