diff --git a/examples/src/main/java/org/bitcoinj/examples/FetchBlock.java b/examples/src/main/java/org/bitcoinj/examples/FetchBlock.java index 5008add8166..7ebfa3e4e3b 100644 --- a/examples/src/main/java/org/bitcoinj/examples/FetchBlock.java +++ b/examples/src/main/java/org/bitcoinj/examples/FetchBlock.java @@ -30,8 +30,10 @@ import org.bitcoinj.store.BlockStore; import org.bitcoinj.store.MemoryBlockStore; import org.bitcoinj.utils.BriefLogFormatter; +import org.jspecify.annotations.Nullable; import picocli.CommandLine; +import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.Future; @@ -44,7 +46,7 @@ @CommandLine.Command(usageHelpAutoWidth = true, sortOptions = false) public class FetchBlock implements Callable { @CommandLine.Parameters(index = "0", description = "The hash of the block to download.") - private String blockHashParam; + private @Nullable String blockHashParam; @CommandLine.Option(names = "--localhost", description = "Connect to the localhost node. Default: ${DEFAULT-VALUE}") private boolean localhost = true; @CommandLine.Option(names = "--help", usageHelp = true, description = "Displays program options.") @@ -59,6 +61,7 @@ public static void main(String[] args) throws Exception { @Override public Integer call() throws Exception { // Connect to testnet and find a peer + Objects.requireNonNull(blockHashParam); System.out.println("Connecting to node"); final Network network = BitcoinNetwork.TESTNET; final NetworkParameters params = NetworkParameters.of(network); diff --git a/examples/src/main/java/org/bitcoinj/examples/ForwardingService.java b/examples/src/main/java/org/bitcoinj/examples/ForwardingService.java index eba04019ac2..d9d9cd881bf 100644 --- a/examples/src/main/java/org/bitcoinj/examples/ForwardingService.java +++ b/examples/src/main/java/org/bitcoinj/examples/ForwardingService.java @@ -29,6 +29,7 @@ import org.bitcoinj.wallet.SendRequest; import org.bitcoinj.wallet.Wallet; import org.bitcoinj.wallet.listeners.WalletCoinsReceivedEventListener; +import org.jspecify.annotations.Nullable; import java.io.Closeable; import java.io.File; @@ -46,7 +47,7 @@ public class ForwardingService implements Closeable { static final int MAX_CONNECTIONS = 4; private final BitcoinNetwork network; private final Address forwardingAddress; - private volatile WalletAppKit kit; + private volatile @Nullable WalletAppKit kit; /* We need to save the listener object (created by a method reference) so we can remove it later */ private final WalletCoinsReceivedEventListener coinsReceivedListener = this::coinForwardingListener; diff --git a/examples/src/main/java/org/bitcoinj/examples/GenerateLowSTests.java b/examples/src/main/java/org/bitcoinj/examples/GenerateLowSTests.java index d0a10ecb501..a8e9ebae207 100644 --- a/examples/src/main/java/org/bitcoinj/examples/GenerateLowSTests.java +++ b/examples/src/main/java/org/bitcoinj/examples/GenerateLowSTests.java @@ -38,6 +38,7 @@ import org.bitcoinj.signers.TransactionSigner.ProposedTransaction; import org.bitcoinj.wallet.KeyBag; import org.bitcoinj.wallet.RedeemData; +import org.jspecify.annotations.Nullable; import java.io.IOException; import java.math.BigInteger; @@ -75,6 +76,7 @@ public ECKey findKeyFromPubKey(byte[] pubKey) { } @Override + @Nullable public RedeemData findRedeemDataFromScriptHash(byte[] scriptHash) { return null; } diff --git a/examples/src/main/java/org/bitcoinj/examples/PrintPeers.java b/examples/src/main/java/org/bitcoinj/examples/PrintPeers.java index e0b398669b1..d5e81a0d853 100644 --- a/examples/src/main/java/org/bitcoinj/examples/PrintPeers.java +++ b/examples/src/main/java/org/bitcoinj/examples/PrintPeers.java @@ -18,6 +18,7 @@ import org.bitcoinj.base.BitcoinNetwork; import org.bitcoinj.base.Network; +import org.bitcoinj.core.Context; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Peer; import org.bitcoinj.core.PeerAddress; @@ -33,55 +34,55 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; /** * Prints a list of IP addresses obtained from DNS. */ public class PrintPeers { - private static List dnsPeers; + record PeerDnsResult(List dnsPeers, Duration duration) {} - private static void printElapsed(long start) { - long now = System.currentTimeMillis(); - System.out.println(String.format("Took %.2f seconds", (now - start) / 1000.0)); - } - - private static void printPeers(List addresses) { - for (InetSocketAddress address : addresses) { + private static void printPeers(PeerDnsResult result) { + for (InetSocketAddress address : result.dnsPeers()) { String hostAddress = address.getAddress().getHostAddress(); System.out.println(String.format("%s:%d", hostAddress, address.getPort())); } + System.out.println(String.format("DNS query took %.2f seconds", result.duration().toMillis() / 1000.0)); } - private static void printDNS(Network network) throws PeerDiscoveryException { + private static PeerDnsResult getPeers(Network network) throws PeerDiscoveryException { long start = System.currentTimeMillis(); DnsDiscovery dns = new DnsDiscovery(network); - dnsPeers = dns.getPeers(0, Duration.ofSeconds(10)); - printPeers(dnsPeers); - printElapsed(start); + List dnsPeers = dns.getPeers(0, Duration.ofSeconds(10)); + var duration = Duration.ofMillis(System.currentTimeMillis() - start); + return new PeerDnsResult(dnsPeers, duration); } public static void main(String[] args) throws Exception { - BriefLogFormatter.init(); + BriefLogFormatter.init(Level.WARNING); + Context.propagate(new Context()); final Network network = BitcoinNetwork.MAINNET; final NetworkParameters params = NetworkParameters.of(network); System.out.println("=== DNS ==="); - printDNS(network); + PeerDnsResult result = getPeers(network); + printPeers(result); System.out.println("=== Version/chain heights ==="); ArrayList addrs = new ArrayList<>(); - for (InetSocketAddress peer : dnsPeers) addrs.add(peer.getAddress()); + for (InetSocketAddress peer : result.dnsPeers()) addrs.add(peer.getAddress()); System.out.println("Scanning " + addrs.size() + " peers:"); final Object lock = new Object(); final long[] bestHeight = new long[1]; - List> futures = new ArrayList<>(); + List> futures = new ArrayList<>(); NioClientManager clientManager = new NioClientManager(); + clientManager.start().join(); for (final InetAddress addr : addrs) { InetSocketAddress address = new InetSocketAddress(addr, params.getPort()); final Peer peer = new Peer(params, new VersionMessage(params, 0), PeerAddress.simple(address), null); - final CompletableFuture future = new CompletableFuture<>(); + final CompletableFuture future = new CompletableFuture<>(); // Once the connection has completed version handshaking ... peer.addConnectedEventListener((p, peerCount) -> { // Check the chain height it claims to have. @@ -100,18 +101,22 @@ public static void main(String[] args) throws Exception { } } // Now finish the future and close the connection - future.complete(null); + future.complete(true); peer.close(); }); peer.addDisconnectedEventListener((p, peerCount) -> { - if (!future.isDone()) + if (!future.isDone()) { System.out.println("Failed to talk to " + addr); - future.complete(null); + future.complete(false); + } }); clientManager.openConnection(address, peer); futures.add(future); } // Wait for every tried connection to finish. CompletableFuture.allOf(futures.toArray( new CompletableFuture[0])).join(); + int successful = futures.stream().mapToInt(f -> f.join() ? 1 : 0).sum(); + int total = futures.size(); + System.out.printf("Successfully talked to %d of %d nodes.\n", successful, total); } } diff --git a/examples/src/main/java/org/bitcoinj/examples/package-info.java b/examples/src/main/java/org/bitcoinj/examples/package-info.java new file mode 100644 index 00000000000..3145fa3ef45 --- /dev/null +++ b/examples/src/main/java/org/bitcoinj/examples/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright by the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * bitcoinj example applications including {@link org.bitcoinj.examples.ForwardingService} which + * is featured in the Getting Started tutorial. + * @see Getting Started + */ +@NullMarked +package org.bitcoinj.examples; + +import org.jspecify.annotations.NullMarked;