core, integration-test: migrate usage of Guava Stopwatch to java.time API

This commit is contained in:
Andreas Schildbach 2023-03-01 13:01:02 +01:00
parent 64dbdd9fcd
commit 2eb7278d04
9 changed files with 58 additions and 38 deletions

View File

@ -18,6 +18,8 @@ package org.bitcoinj.base.internal;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
@ -91,6 +93,20 @@ public class TimeUtils {
return currentTimeMillis() / 1000; 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. * Formats a given date+time value to an ISO 8601 string.
* @param dateTime value to format, as a Date * @param dateTime value to format, as a Date

View File

@ -18,7 +18,6 @@
package org.bitcoinj.core; package org.bitcoinj.core;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
@ -69,6 +68,7 @@ import java.net.InetSocketAddress;
import java.net.NoRouteToHostException; import java.net.NoRouteToHostException;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -1062,7 +1062,7 @@ public class PeerGroup implements TransactionBroadcaster {
checkState(!lock.isHeldByCurrentThread()); checkState(!lock.isHeldByCurrentThread());
int maxPeersToDiscoverCount = this.vMaxPeersToDiscoverCount; int maxPeersToDiscoverCount = this.vMaxPeersToDiscoverCount;
long peerDiscoveryTimeoutMillis = this.vPeerDiscoveryTimeoutMillis; long peerDiscoveryTimeoutMillis = this.vPeerDiscoveryTimeoutMillis;
final Stopwatch watch = Stopwatch.createStarted(); Instant start = TimeUtils.currentTime();
final List<PeerAddress> addressList = new LinkedList<>(); final List<PeerAddress> addressList = new LinkedList<>();
for (PeerDiscovery peerDiscovery : peerDiscoverers /* COW */) { for (PeerDiscovery peerDiscovery : peerDiscoverers /* COW */) {
List<InetSocketAddress> addresses; List<InetSocketAddress> addresses;
@ -1084,9 +1084,8 @@ public class PeerGroup implements TransactionBroadcaster {
registration.executor.execute(() -> registration.listener.onPeersDiscovered(peersDiscoveredSet)); registration.executor.execute(() -> registration.listener.onPeersDiscovered(peersDiscoveredSet));
} }
} }
watch.stop(); log.info("Peer discovery took {} ms and returned {} items from {} discoverers",
log.info("Peer discovery took {} and returned {} items from {} discoverers", watch, addressList.size(), TimeUtils.elapsedTime(start).toMillis(), addressList.size(), peerDiscoverers.size());
peerDiscoverers.size());
return addressList.size(); return addressList.size();
} }
@ -1169,7 +1168,7 @@ public class PeerGroup implements TransactionBroadcaster {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try { try {
log.info("Stopping ..."); log.info("Stopping ...");
Stopwatch watch = Stopwatch.createStarted(); Instant start = TimeUtils.currentTime();
// The log output this creates can be useful. // The log output this creates can be useful.
setDownloadPeer(null); setDownloadPeer(null);
// Blocking close of all sockets. // Blocking close of all sockets.
@ -1179,7 +1178,7 @@ public class PeerGroup implements TransactionBroadcaster {
peerDiscovery.shutdown(); peerDiscovery.shutdown();
} }
vRunning = false; vRunning = false;
log.info("Stopped, took {}.", watch); log.info("Stopped, took {} ms.", TimeUtils.elapsedTime(start).toMillis());
} catch (Throwable e) { } catch (Throwable e) {
log.error("Exception when shutting down", e); // The executor swallows exceptions :( log.error("Exception when shutting down", e); // The executor swallows exceptions :(
} }
@ -1191,11 +1190,11 @@ public class PeerGroup implements TransactionBroadcaster {
/** Does a blocking stop */ /** Does a blocking stop */
public void stop() { public void stop() {
try { try {
Stopwatch watch = Stopwatch.createStarted(); Instant start = TimeUtils.currentTime();
stopAsync(); stopAsync();
log.info("Awaiting PeerGroup shutdown ..."); log.info("Awaiting PeerGroup shutdown ...");
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
log.info("... took {}", watch); log.info("... took {} ms", TimeUtils.elapsedTime(start).toMillis());
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -17,8 +17,8 @@
package org.bitcoinj.crypto; package org.bitcoinj.crypto;
import com.google.common.base.Stopwatch;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.wallet.Protos; import org.bitcoinj.wallet.Protos;
import org.bitcoinj.wallet.Protos.ScryptParameters; import org.bitcoinj.wallet.Protos.ScryptParameters;
import org.bitcoinj.wallet.Protos.Wallet.EncryptionType; import org.bitcoinj.wallet.Protos.Wallet.EncryptionType;
@ -34,6 +34,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; 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."); 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); byte[] keyBytes = SCrypt.generate(passwordBytes, salt, (int) scryptParameters.getN(), scryptParameters.getR(), scryptParameters.getP(), KEY_LENGTH);
watch.stop(); log.info("Deriving key took {} ms for {}.", TimeUtils.elapsedTime(start).toMillis(), scryptParametersString());
log.info("Deriving key took {} for {}.", watch, scryptParametersString());
return new KeyParameter(keyBytes); return new KeyParameter(keyBytes);
} catch (Exception e) { } catch (Exception e) {
throw new KeyCrypterException("Could not generate key from password and salt.", e); throw new KeyCrypterException("Could not generate key from password and salt.", e);

View File

@ -17,9 +17,9 @@
package org.bitcoinj.crypto; package org.bitcoinj.crypto;
import com.google.common.base.Stopwatch;
import org.bitcoinj.base.Sha256Hash; import org.bitcoinj.base.Sha256Hash;
import org.bitcoinj.base.internal.PlatformUtils; import org.bitcoinj.base.internal.PlatformUtils;
import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.base.utils.StreamUtils; import org.bitcoinj.base.utils.StreamUtils;
import org.bitcoinj.base.internal.InternalUtils; import org.bitcoinj.base.internal.InternalUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -32,6 +32,7 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -136,10 +137,9 @@ public class MnemonicCode {
String pass = InternalUtils.SPACE_JOINER.join(words); String pass = InternalUtils.SPACE_JOINER.join(words);
String salt = "mnemonic" + passphrase; String salt = "mnemonic" + passphrase;
final Stopwatch watch = Stopwatch.createStarted(); Instant start = TimeUtils.currentTime();
byte[] seed = PBKDF2SHA512.derive(pass, salt, PBKDF2_ROUNDS, 64); byte[] seed = PBKDF2SHA512.derive(pass, salt, PBKDF2_ROUNDS, 64);
watch.stop(); log.info("PBKDF2 took {} ms", TimeUtils.elapsedTime(start).toMillis());
log.info("PBKDF2 took {}", watch);
return seed; return seed;
} }

View File

@ -17,8 +17,8 @@
package org.bitcoinj.params; package org.bitcoinj.params;
import com.google.common.base.Stopwatch;
import org.bitcoinj.base.BitcoinNetwork; import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.base.utils.ByteUtils; import org.bitcoinj.base.utils.ByteUtils;
import org.bitcoinj.core.BitcoinSerializer; import org.bitcoinj.core.BitcoinSerializer;
import org.bitcoinj.core.Block; import org.bitcoinj.core.Block;
@ -36,6 +36,10 @@ import org.slf4j.LoggerFactory;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.math.BigInteger; 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 java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkState; 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 // 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. // two weeks after the initial block chain download.
final Stopwatch watch = Stopwatch.createStarted(); Instant start = TimeUtils.currentTime();
Sha256Hash hash = prev.getHash(); Sha256Hash hash = prev.getHash();
StoredBlock cursor = null; StoredBlock cursor = null;
final int interval = this.getInterval(); final int interval = this.getInterval();
@ -189,9 +193,9 @@ public abstract class BitcoinNetworkParams extends NetworkParameters {
} }
checkState(cursor != null && isDifficultyTransitionPoint(cursor.getHeight() - 1), checkState(cursor != null && isDifficultyTransitionPoint(cursor.getHeight() - 1),
"Didn't arrive at a transition point."); "Didn't arrive at a transition point.");
watch.stop(); Duration elapsed = TimeUtils.elapsedTime(start);
if (watch.elapsed(TimeUnit.MILLISECONDS) > 50) if (elapsed.toMillis() > 50)
log.info("Difficulty transition traversal took {}", watch); log.info("Difficulty transition traversal took {} ms", elapsed.toMillis());
Block blockIntervalAgo = cursor.getHeader(); Block blockIntervalAgo = cursor.getHeader();
int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds()); int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds());

View File

@ -17,7 +17,6 @@
package org.bitcoinj.wallet; package org.bitcoinj.wallet;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Stopwatch;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import org.bitcoinj.base.Network; import org.bitcoinj.base.Network;
import org.bitcoinj.base.ScriptType; import org.bitcoinj.base.ScriptType;
@ -50,6 +49,7 @@ import org.slf4j.LoggerFactory;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList; 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", log.info("{} keys needed for {} = {} issued + {} lookahead size + {} lookahead threshold - {} num children",
limit, parent.getPathAsString(), issued, lookaheadSize, lookaheadThreshold, numChildren); limit, parent.getPathAsString(), issued, lookaheadSize, lookaheadThreshold, numChildren);
final Stopwatch watch = Stopwatch.createStarted(); Instant start = TimeUtils.currentTime();
List<DeterministicKey> result = HDKeyDerivation.generate(parent, numChildren) List<DeterministicKey> result = HDKeyDerivation.generate(parent, numChildren)
.limit(limit) .limit(limit)
.map(DeterministicKey::dropPrivateBytes) .map(DeterministicKey::dropPrivateBytes)
.collect(StreamUtils.toUnmodifiableList()); .collect(StreamUtils.toUnmodifiableList());
watch.stop(); log.info("Took {} ms", TimeUtils.elapsedTime(start).toMillis());
log.info("Took {}", watch);
return result; return result;
} }

View File

@ -17,7 +17,6 @@
package org.bitcoinj.wallet; package org.bitcoinj.wallet;
import com.google.common.base.Stopwatch;
import org.bitcoinj.base.internal.TimeUtils; import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.utils.ContextPropagatingThreadFactory; import org.bitcoinj.utils.ContextPropagatingThreadFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -27,6 +26,9 @@ import javax.annotation.Nonnull;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.sql.Time;
import java.time.Duration;
import java.time.Instant;
import java.util.Date; import java.util.Date;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
@ -130,7 +132,7 @@ public class WalletFiles {
} }
private void saveNowInternal() throws IOException { private void saveNowInternal() throws IOException {
final Stopwatch watch = Stopwatch.createStarted(); Instant start = TimeUtils.currentTime();
File directory = file.getAbsoluteFile().getParentFile(); File directory = file.getAbsoluteFile().getParentFile();
if (!directory.exists()) { if (!directory.exists()) {
throw new FileNotFoundException(directory.getPath() + " (wallet directory not found)"); throw new FileNotFoundException(directory.getPath() + " (wallet directory not found)");
@ -142,8 +144,7 @@ public class WalletFiles {
wallet.saveToFile(temp, file); wallet.saveToFile(temp, file);
if (listener != null) if (listener != null)
listener.onAfterAutoSave(file); listener.onAfterAutoSave(file);
watch.stop(); log.info("Save completed in {} ms", TimeUtils.elapsedTime(start).toMillis());
log.info("Save completed in {}", watch);
} }
/** Queues up a save in the background. Useful for not very important wallet changes. */ /** Queues up a save in the background. Useful for not very important wallet changes. */

View File

@ -17,7 +17,6 @@
package org.bitcoinj.store; package org.bitcoinj.store;
import com.google.common.base.Stopwatch;
import org.bitcoinj.base.BitcoinNetwork; import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.ScriptType; import org.bitcoinj.base.ScriptType;
import org.bitcoinj.base.Address; import org.bitcoinj.base.Address;
@ -37,6 +36,8 @@ import org.junit.Test;
import java.io.File; import java.io.File;
import java.math.BigInteger; import java.math.BigInteger;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -146,7 +147,7 @@ public class SPVBlockStoreTest {
final int ITERATIONS = 100000; final int ITERATIONS = 100000;
final long THRESHOLD_MS = 2000; final long THRESHOLD_MS = 2000;
SPVBlockStore store = new SPVBlockStore(TESTNET, blockStoreFile); SPVBlockStore store = new SPVBlockStore(TESTNET, blockStoreFile);
Stopwatch watch = Stopwatch.createStarted(); Instant start = TimeUtils.currentTime();
for (int i = 0; i < ITERATIONS; i++) { for (int i = 0; i < ITERATIONS; i++) {
// Using i as the nonce so that the block hashes are different. // 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, 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.put(b);
store.setChainHead(b); store.setChainHead(b);
} }
assertTrue("took " + watch + " for " + ITERATIONS + " iterations", Duration elapsed = TimeUtils.elapsedTime(start);
watch.elapsed(TimeUnit.MILLISECONDS) < THRESHOLD_MS); assertTrue("took " + elapsed.toMillis() + " ms for " + ITERATIONS + " iterations",
elapsed.toMillis() < THRESHOLD_MS);
store.close(); store.close();
} }

View File

@ -17,7 +17,6 @@
package org.bitcoinj.core; package org.bitcoinj.core;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.bitcoinj.base.Address; import org.bitcoinj.base.Address;
import org.bitcoinj.base.Coin; import org.bitcoinj.base.Coin;
@ -48,6 +47,7 @@ import java.net.BindException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -532,7 +532,7 @@ public class PeerGroupTest extends TestWithPeerGroup {
peerGroup.addConnectedEventListener(Threading.SAME_THREAD, (peer, peerCount) -> peerConnectedFuture.complete(null)); peerGroup.addConnectedEventListener(Threading.SAME_THREAD, (peer, peerCount) -> peerConnectedFuture.complete(null));
peerGroup.addDisconnectedEventListener(Threading.SAME_THREAD, (peer, peerCount) -> peerDisconnectedFuture.complete(null)); peerGroup.addDisconnectedEventListener(Threading.SAME_THREAD, (peer, peerCount) -> peerDisconnectedFuture.complete(null));
// connect to peer but don't do handshake // 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); connectPeerWithoutVersionExchange(0);
// wait for disconnect (plus a bit more, in case test server is overloaded) // wait for disconnect (plus a bit more, in case test server is overloaded)
try { try {
@ -541,9 +541,8 @@ public class PeerGroupTest extends TestWithPeerGroup {
// the checks below suffice for this case too // the checks below suffice for this case too
} }
// check things after disconnect // check things after disconnect
watch.stop();
assertFalse(peerConnectedFuture.isDone()); // should never have connected 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 assertTrue(peerDisconnectedFuture.isDone()); // but should disconnect eventually
} }