From 2eb7278d048d2d6cdb3ff5b3dada95ebf21e7f86 Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Wed, 1 Mar 2023 13:01:02 +0100 Subject: [PATCH] core, integration-test: migrate usage of Guava `Stopwatch` to `java.time` API --- .../org/bitcoinj/base/internal/TimeUtils.java | 16 ++++++++++++++++ .../main/java/org/bitcoinj/core/PeerGroup.java | 17 ++++++++--------- .../org/bitcoinj/crypto/KeyCrypterScrypt.java | 8 ++++---- .../java/org/bitcoinj/crypto/MnemonicCode.java | 8 ++++---- .../bitcoinj/params/BitcoinNetworkParams.java | 14 +++++++++----- .../bitcoinj/wallet/DeterministicKeyChain.java | 7 +++---- .../java/org/bitcoinj/wallet/WalletFiles.java | 9 +++++---- .../org/bitcoinj/store/SPVBlockStoreTest.java | 10 ++++++---- .../java/org/bitcoinj/core/PeerGroupTest.java | 7 +++---- 9 files changed, 58 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/base/internal/TimeUtils.java b/core/src/main/java/org/bitcoinj/base/internal/TimeUtils.java index 1e18e6736..3c7fcaa2b 100644 --- a/core/src/main/java/org/bitcoinj/base/internal/TimeUtils.java +++ b/core/src/main/java/org/bitcoinj/base/internal/TimeUtils.java @@ -18,6 +18,8 @@ package org.bitcoinj.base.internal; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.Instant; import java.util.Date; import java.util.Locale; import java.util.TimeZone; @@ -91,6 +93,20 @@ public class TimeUtils { return currentTimeMillis() / 1000; } + /** + * Returns the current time as an Instant, or a mocked out equivalent. + */ + public static Instant currentTime() { + return Instant.ofEpochMilli(currentTimeMillis()); + } + + /** + * Returns elapsed time between given start and current time as a Duration. + */ + public static Duration elapsedTime(Instant start) { + return Duration.between(start, currentTime()); + } + /** * Formats a given date+time value to an ISO 8601 string. * @param dateTime value to format, as a Date diff --git a/core/src/main/java/org/bitcoinj/core/PeerGroup.java b/core/src/main/java/org/bitcoinj/core/PeerGroup.java index 44ed15f18..6b3ce919d 100644 --- a/core/src/main/java/org/bitcoinj/core/PeerGroup.java +++ b/core/src/main/java/org/bitcoinj/core/PeerGroup.java @@ -18,7 +18,6 @@ package org.bitcoinj.core; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Stopwatch; import com.google.common.base.Throwables; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; @@ -69,6 +68,7 @@ import java.net.InetSocketAddress; import java.net.NoRouteToHostException; import java.net.Socket; import java.net.SocketAddress; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -1062,7 +1062,7 @@ public class PeerGroup implements TransactionBroadcaster { checkState(!lock.isHeldByCurrentThread()); int maxPeersToDiscoverCount = this.vMaxPeersToDiscoverCount; long peerDiscoveryTimeoutMillis = this.vPeerDiscoveryTimeoutMillis; - final Stopwatch watch = Stopwatch.createStarted(); + Instant start = TimeUtils.currentTime(); final List addressList = new LinkedList<>(); for (PeerDiscovery peerDiscovery : peerDiscoverers /* COW */) { List addresses; @@ -1084,9 +1084,8 @@ public class PeerGroup implements TransactionBroadcaster { registration.executor.execute(() -> registration.listener.onPeersDiscovered(peersDiscoveredSet)); } } - watch.stop(); - log.info("Peer discovery took {} and returned {} items from {} discoverers", watch, addressList.size(), - peerDiscoverers.size()); + log.info("Peer discovery took {} ms and returned {} items from {} discoverers", + TimeUtils.elapsedTime(start).toMillis(), addressList.size(), peerDiscoverers.size()); return addressList.size(); } @@ -1169,7 +1168,7 @@ public class PeerGroup implements TransactionBroadcaster { CompletableFuture future = CompletableFuture.runAsync(() -> { try { log.info("Stopping ..."); - Stopwatch watch = Stopwatch.createStarted(); + Instant start = TimeUtils.currentTime(); // The log output this creates can be useful. setDownloadPeer(null); // Blocking close of all sockets. @@ -1179,7 +1178,7 @@ public class PeerGroup implements TransactionBroadcaster { peerDiscovery.shutdown(); } vRunning = false; - log.info("Stopped, took {}.", watch); + log.info("Stopped, took {} ms.", TimeUtils.elapsedTime(start).toMillis()); } catch (Throwable e) { log.error("Exception when shutting down", e); // The executor swallows exceptions :( } @@ -1191,11 +1190,11 @@ public class PeerGroup implements TransactionBroadcaster { /** Does a blocking stop */ public void stop() { try { - Stopwatch watch = Stopwatch.createStarted(); + Instant start = TimeUtils.currentTime(); stopAsync(); log.info("Awaiting PeerGroup shutdown ..."); executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); - log.info("... took {}", watch); + log.info("... took {} ms", TimeUtils.elapsedTime(start).toMillis()); } catch (InterruptedException e) { throw new RuntimeException(e); } diff --git a/core/src/main/java/org/bitcoinj/crypto/KeyCrypterScrypt.java b/core/src/main/java/org/bitcoinj/crypto/KeyCrypterScrypt.java index 79f372fb2..3369c8f96 100644 --- a/core/src/main/java/org/bitcoinj/crypto/KeyCrypterScrypt.java +++ b/core/src/main/java/org/bitcoinj/crypto/KeyCrypterScrypt.java @@ -17,8 +17,8 @@ package org.bitcoinj.crypto; -import com.google.common.base.Stopwatch; import com.google.protobuf.ByteString; +import org.bitcoinj.base.internal.TimeUtils; import org.bitcoinj.wallet.Protos; import org.bitcoinj.wallet.Protos.ScryptParameters; import org.bitcoinj.wallet.Protos.Wallet.EncryptionType; @@ -34,6 +34,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.SecureRandom; +import java.time.Instant; import java.util.Arrays; import java.util.Objects; @@ -151,10 +152,9 @@ public class KeyCrypterScrypt implements KeyCrypter { log.warn("You are using a ScryptParameters with no salt. Your encryption may be vulnerable to a dictionary attack."); } - final Stopwatch watch = Stopwatch.createStarted(); + Instant start = TimeUtils.currentTime(); byte[] keyBytes = SCrypt.generate(passwordBytes, salt, (int) scryptParameters.getN(), scryptParameters.getR(), scryptParameters.getP(), KEY_LENGTH); - watch.stop(); - log.info("Deriving key took {} for {}.", watch, scryptParametersString()); + log.info("Deriving key took {} ms for {}.", TimeUtils.elapsedTime(start).toMillis(), scryptParametersString()); return new KeyParameter(keyBytes); } catch (Exception e) { throw new KeyCrypterException("Could not generate key from password and salt.", e); diff --git a/core/src/main/java/org/bitcoinj/crypto/MnemonicCode.java b/core/src/main/java/org/bitcoinj/crypto/MnemonicCode.java index 8c38079b2..8b258050d 100644 --- a/core/src/main/java/org/bitcoinj/crypto/MnemonicCode.java +++ b/core/src/main/java/org/bitcoinj/crypto/MnemonicCode.java @@ -17,9 +17,9 @@ package org.bitcoinj.crypto; -import com.google.common.base.Stopwatch; import org.bitcoinj.base.Sha256Hash; import org.bitcoinj.base.internal.PlatformUtils; +import org.bitcoinj.base.internal.TimeUtils; import org.bitcoinj.base.utils.StreamUtils; import org.bitcoinj.base.internal.InternalUtils; import org.slf4j.Logger; @@ -32,6 +32,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; +import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -136,10 +137,9 @@ public class MnemonicCode { String pass = InternalUtils.SPACE_JOINER.join(words); String salt = "mnemonic" + passphrase; - final Stopwatch watch = Stopwatch.createStarted(); + Instant start = TimeUtils.currentTime(); byte[] seed = PBKDF2SHA512.derive(pass, salt, PBKDF2_ROUNDS, 64); - watch.stop(); - log.info("PBKDF2 took {}", watch); + log.info("PBKDF2 took {} ms", TimeUtils.elapsedTime(start).toMillis()); return seed; } diff --git a/core/src/main/java/org/bitcoinj/params/BitcoinNetworkParams.java b/core/src/main/java/org/bitcoinj/params/BitcoinNetworkParams.java index b599ff301..c324d901c 100644 --- a/core/src/main/java/org/bitcoinj/params/BitcoinNetworkParams.java +++ b/core/src/main/java/org/bitcoinj/params/BitcoinNetworkParams.java @@ -17,8 +17,8 @@ package org.bitcoinj.params; -import com.google.common.base.Stopwatch; import org.bitcoinj.base.BitcoinNetwork; +import org.bitcoinj.base.internal.TimeUtils; import org.bitcoinj.base.utils.ByteUtils; import org.bitcoinj.core.BitcoinSerializer; import org.bitcoinj.core.Block; @@ -36,6 +36,10 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.math.BigInteger; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalUnit; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkState; @@ -174,7 +178,7 @@ public abstract class BitcoinNetworkParams extends NetworkParameters { // We need to find a block far back in the chain. It's OK that this is expensive because it only occurs every // two weeks after the initial block chain download. - final Stopwatch watch = Stopwatch.createStarted(); + Instant start = TimeUtils.currentTime(); Sha256Hash hash = prev.getHash(); StoredBlock cursor = null; final int interval = this.getInterval(); @@ -189,9 +193,9 @@ public abstract class BitcoinNetworkParams extends NetworkParameters { } checkState(cursor != null && isDifficultyTransitionPoint(cursor.getHeight() - 1), "Didn't arrive at a transition point."); - watch.stop(); - if (watch.elapsed(TimeUnit.MILLISECONDS) > 50) - log.info("Difficulty transition traversal took {}", watch); + Duration elapsed = TimeUtils.elapsedTime(start); + if (elapsed.toMillis() > 50) + log.info("Difficulty transition traversal took {} ms", elapsed.toMillis()); Block blockIntervalAgo = cursor.getHeader(); int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds()); diff --git a/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java b/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java index ea48e2e1b..d6e70c98c 100644 --- a/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java +++ b/core/src/main/java/org/bitcoinj/wallet/DeterministicKeyChain.java @@ -17,7 +17,6 @@ package org.bitcoinj.wallet; import com.google.common.base.MoreObjects; -import com.google.common.base.Stopwatch; import com.google.protobuf.ByteString; import org.bitcoinj.base.Network; import org.bitcoinj.base.ScriptType; @@ -50,6 +49,7 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.math.BigInteger; import java.security.SecureRandom; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; @@ -1242,13 +1242,12 @@ public class DeterministicKeyChain implements EncryptableKeyChain { log.info("{} keys needed for {} = {} issued + {} lookahead size + {} lookahead threshold - {} num children", limit, parent.getPathAsString(), issued, lookaheadSize, lookaheadThreshold, numChildren); - final Stopwatch watch = Stopwatch.createStarted(); + Instant start = TimeUtils.currentTime(); List result = HDKeyDerivation.generate(parent, numChildren) .limit(limit) .map(DeterministicKey::dropPrivateBytes) .collect(StreamUtils.toUnmodifiableList()); - watch.stop(); - log.info("Took {}", watch); + log.info("Took {} ms", TimeUtils.elapsedTime(start).toMillis()); return result; } diff --git a/core/src/main/java/org/bitcoinj/wallet/WalletFiles.java b/core/src/main/java/org/bitcoinj/wallet/WalletFiles.java index 8c4b98685..d1652505d 100644 --- a/core/src/main/java/org/bitcoinj/wallet/WalletFiles.java +++ b/core/src/main/java/org/bitcoinj/wallet/WalletFiles.java @@ -17,7 +17,6 @@ package org.bitcoinj.wallet; -import com.google.common.base.Stopwatch; import org.bitcoinj.base.internal.TimeUtils; import org.bitcoinj.utils.ContextPropagatingThreadFactory; import org.slf4j.Logger; @@ -27,6 +26,9 @@ import javax.annotation.Nonnull; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.sql.Time; +import java.time.Duration; +import java.time.Instant; import java.util.Date; import java.util.concurrent.Callable; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -130,7 +132,7 @@ public class WalletFiles { } private void saveNowInternal() throws IOException { - final Stopwatch watch = Stopwatch.createStarted(); + Instant start = TimeUtils.currentTime(); File directory = file.getAbsoluteFile().getParentFile(); if (!directory.exists()) { throw new FileNotFoundException(directory.getPath() + " (wallet directory not found)"); @@ -142,8 +144,7 @@ public class WalletFiles { wallet.saveToFile(temp, file); if (listener != null) listener.onAfterAutoSave(file); - watch.stop(); - log.info("Save completed in {}", watch); + log.info("Save completed in {} ms", TimeUtils.elapsedTime(start).toMillis()); } /** Queues up a save in the background. Useful for not very important wallet changes. */ diff --git a/core/src/test/java/org/bitcoinj/store/SPVBlockStoreTest.java b/core/src/test/java/org/bitcoinj/store/SPVBlockStoreTest.java index 83f71bd5f..e2470f877 100644 --- a/core/src/test/java/org/bitcoinj/store/SPVBlockStoreTest.java +++ b/core/src/test/java/org/bitcoinj/store/SPVBlockStoreTest.java @@ -17,7 +17,6 @@ package org.bitcoinj.store; -import com.google.common.base.Stopwatch; import org.bitcoinj.base.BitcoinNetwork; import org.bitcoinj.base.ScriptType; import org.bitcoinj.base.Address; @@ -37,6 +36,8 @@ import org.junit.Test; import java.io.File; import java.math.BigInteger; +import java.time.Duration; +import java.time.Instant; import java.util.Collections; import java.util.concurrent.TimeUnit; @@ -146,7 +147,7 @@ public class SPVBlockStoreTest { final int ITERATIONS = 100000; final long THRESHOLD_MS = 2000; SPVBlockStore store = new SPVBlockStore(TESTNET, blockStoreFile); - Stopwatch watch = Stopwatch.createStarted(); + Instant start = TimeUtils.currentTime(); for (int i = 0; i < ITERATIONS; i++) { // Using i as the nonce so that the block hashes are different. Block block = new Block(TESTNET, 0, Sha256Hash.ZERO_HASH, Sha256Hash.ZERO_HASH, 0, 0, i, @@ -155,8 +156,9 @@ public class SPVBlockStoreTest { store.put(b); store.setChainHead(b); } - assertTrue("took " + watch + " for " + ITERATIONS + " iterations", - watch.elapsed(TimeUnit.MILLISECONDS) < THRESHOLD_MS); + Duration elapsed = TimeUtils.elapsedTime(start); + assertTrue("took " + elapsed.toMillis() + " ms for " + ITERATIONS + " iterations", + elapsed.toMillis() < THRESHOLD_MS); store.close(); } diff --git a/integration-test/src/test/java/org/bitcoinj/core/PeerGroupTest.java b/integration-test/src/test/java/org/bitcoinj/core/PeerGroupTest.java index 577d8a689..903dbd215 100644 --- a/integration-test/src/test/java/org/bitcoinj/core/PeerGroupTest.java +++ b/integration-test/src/test/java/org/bitcoinj/core/PeerGroupTest.java @@ -17,7 +17,6 @@ package org.bitcoinj.core; -import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; import org.bitcoinj.base.Address; import org.bitcoinj.base.Coin; @@ -48,6 +47,7 @@ import java.net.BindException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -532,7 +532,7 @@ public class PeerGroupTest extends TestWithPeerGroup { peerGroup.addConnectedEventListener(Threading.SAME_THREAD, (peer, peerCount) -> peerConnectedFuture.complete(null)); peerGroup.addDisconnectedEventListener(Threading.SAME_THREAD, (peer, peerCount) -> peerDisconnectedFuture.complete(null)); // connect to peer but don't do handshake - final Stopwatch watch = Stopwatch.createStarted(); // before connection so we don't get elapsed < timeout + Instant start = TimeUtils.currentTime(); // before connection so we don't get elapsed < timeout connectPeerWithoutVersionExchange(0); // wait for disconnect (plus a bit more, in case test server is overloaded) try { @@ -541,9 +541,8 @@ public class PeerGroupTest extends TestWithPeerGroup { // the checks below suffice for this case too } // check things after disconnect - watch.stop(); assertFalse(peerConnectedFuture.isDone()); // should never have connected - assertTrue(watch.elapsed(TimeUnit.MILLISECONDS) >= timeout); // should not disconnect before timeout + assertTrue(TimeUtils.elapsedTime(start).toMillis() >= timeout); // should not disconnect before timeout assertTrue(peerDisconnectedFuture.isDone()); // but should disconnect eventually }