From a32a612630150297d89b70d66eafc6fdcafdb5e5 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Mon, 2 May 2011 12:14:35 +0000 Subject: [PATCH] Add DNS based discovery. Patch from John Sample. Resolves issue 18. Rename IRCPeers to PrintPeers, add DNS, add timing measurements. --- src/com/google/bitcoin/core/DnsDiscovery.java | 106 ++++++++++++++++++ .../{IRCPeers.java => PrintPeers.java} | 51 ++++++--- 2 files changed, 143 insertions(+), 14 deletions(-) create mode 100644 src/com/google/bitcoin/core/DnsDiscovery.java rename src/com/google/bitcoin/examples/{IRCPeers.java => PrintPeers.java} (58%) diff --git a/src/com/google/bitcoin/core/DnsDiscovery.java b/src/com/google/bitcoin/core/DnsDiscovery.java new file mode 100644 index 00000000..7fe76a92 --- /dev/null +++ b/src/com/google/bitcoin/core/DnsDiscovery.java @@ -0,0 +1,106 @@ +/** + * Copyright 2011 John Sample + * + * 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. + */ + +package com.google.bitcoin.core; + +import java.net.*; +import java.util.HashSet; +import java.util.Set; +import java.util.Vector; + +/** + * Supports peer discovery through DNS.

+ * + * This class does not support the testnet as currently there are no DNS servers providing testnet hosts. + * If this class is being used for testnet you must specify the hostnames to use.

+ * + * Failure to resolve individual host names will not cause an Exception to be thrown. + * However, if all hosts passed fail to resolve a PeerDiscoveryException will be thrown during getPeers(). + */ +public class DnsDiscovery implements PeerDiscovery { + private String[] hostNames; + private NetworkParameters netParams; + private static final String[] defaultHosts = new String[] {"bitseed.xf2.org","bitseed.bitcoin.org.uk"}; + + /** + * Supports finding peers through DNS A records. Community run DNS entry points will be used. + * + * @param netParams Network parameters to be used for port information. + */ + public DnsDiscovery(NetworkParameters netParams) + { + this(getDefaultHostNames(), netParams); + } + + /** + * Supports finding peers through DNS A records. + * + * @param hostNames Host names to be examined for seed addresses. + * @param netParams Network parameters to be used for port information. + */ + public DnsDiscovery(String[] hostNames, NetworkParameters netParams) + { + this.hostNames = hostNames; + this.netParams = netParams; + } + + public InetSocketAddress[] getPeers() throws PeerDiscoveryException { + Set addresses = new HashSet(); + + /* + * Keep track of how many lookups failed vs. succeeded. + * We'll throw an exception only if all the lookups fail. + * We don't want to throw an exception if only one of many lookups fails. + */ + int failedLookups = 0; + + for (String hostName : hostNames) { + try { + InetAddress[] hostAddresses = InetAddress.getAllByName(hostName); + + for (InetAddress inetAddress : hostAddresses) { + // DNS isn't going to provide us with the port. + // Grab the port from the specified NetworkParameters. + InetSocketAddress socketAddress = new InetSocketAddress(inetAddress, netParams.port); + + // Only add the new address if it's not already in the combined list. + if (!addresses.contains(socketAddress)) { + addresses.add(socketAddress); + } + } + } catch (Exception e) { + failedLookups++; + Utils.LOG("DNS lookup for " + hostName + " failed."); + + if (failedLookups == hostNames.length) { + // All the lookups failed. + // Throw the discovery exception and include the last inner exception. + throw new PeerDiscoveryException("DNS resolution for all hosts failed.", e); + } + } + } + return addresses.toArray(new InetSocketAddress[]{}); + } + + /** + * Returns the well known discovery host names on the production network. + */ + public static String[] getDefaultHostNames() + { + return defaultHosts; + } + +} diff --git a/src/com/google/bitcoin/examples/IRCPeers.java b/src/com/google/bitcoin/examples/PrintPeers.java similarity index 58% rename from src/com/google/bitcoin/examples/IRCPeers.java rename to src/com/google/bitcoin/examples/PrintPeers.java index 66687d63..b04dd450 100644 --- a/src/com/google/bitcoin/examples/IRCPeers.java +++ b/src/com/google/bitcoin/examples/PrintPeers.java @@ -23,24 +23,47 @@ import com.google.bitcoin.core.*; /** * Prints a list of IP addresses connected to the rendezvous point on the LFnet IRC channel. */ -public class IRCPeers { - public static void main(String[] args) throws PeerDiscoveryException { - IrcDiscovery d = new IrcDiscovery("#bitcoin") { - @Override - protected void onIRCReceive(String message) { - System.out.println(message); - } +public class PrintPeers { + private static void printElapsed(long start) { + long now = System.currentTimeMillis(); + System.out.println(String.format("Took %.2f seconds", (now - start) / 1000.0)); + } - @Override - protected void onIRCSend(String message) { - System.out.println(message); - } - }; - - InetSocketAddress[] addresses = d.getPeers(); + private static void printPeers(InetSocketAddress[] addresses) { for (InetSocketAddress address : addresses) { String hostAddress = address.getAddress().getHostAddress(); System.out.println(String.format("%s:%d", hostAddress.toString(), address.getPort())); } } + + private static void printIRC() throws PeerDiscoveryException { + long start = System.currentTimeMillis(); + IrcDiscovery d = new IrcDiscovery("#bitcoin") { + @Override + protected void onIRCReceive(String message) { + System.out.println("<- " + message); + } + + @Override + protected void onIRCSend(String message) { + System.out.println("-> " + message); + } + }; + printPeers(d.getPeers()); + printElapsed(start); + } + + private static void printDNS() throws PeerDiscoveryException { + long start = System.currentTimeMillis(); + DnsDiscovery dns = new DnsDiscovery(NetworkParameters.prodNet()); + printPeers(dns.getPeers()); + printElapsed(start); + } + + public static void main(String[] args) throws PeerDiscoveryException { + System.out.println("=== IRC ==="); + printIRC(); + System.out.println("=== DNS ==="); + printDNS(); + } }