mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-02-24 06:47:54 +01:00
Tor: accelerate startup by using HTTP/Cartographer seeding when possible instead of DNS.
We still use TorDiscovery for networks where we don't have any Cartographer seeds. Switch to OkHTTP because the standard Java HTTP client doesn't let you customise the socket factory and thus cannot be used via Tor directly (doh).
This commit is contained in:
parent
410b29fc30
commit
06ba160361
5 changed files with 92 additions and 38 deletions
|
@ -479,6 +479,11 @@
|
|||
<artifactId>orchid</artifactId>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>2.2.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -17,15 +17,14 @@
|
|||
|
||||
package org.bitcoinj.core;
|
||||
|
||||
import org.bitcoinj.params.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.ScriptOpCodes;
|
||||
import com.google.common.base.Objects;
|
||||
import org.bitcoinj.net.discovery.*;
|
||||
import org.bitcoinj.params.*;
|
||||
import org.bitcoinj.script.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import javax.annotation.*;
|
||||
import java.io.*;
|
||||
import java.math.*;
|
||||
import java.util.*;
|
||||
|
||||
import static org.bitcoinj.core.Coin.*;
|
||||
|
@ -95,6 +94,7 @@ public abstract class NetworkParameters implements Serializable {
|
|||
|
||||
protected int[] acceptableAddressCodes;
|
||||
protected String[] dnsSeeds;
|
||||
protected HttpDiscovery.Details[] httpSeeds = new HttpDiscovery.Details[] {};
|
||||
protected Map<Integer, Sha256Hash> checkpoints = new HashMap<Integer, Sha256Hash>();
|
||||
|
||||
protected NetworkParameters() {
|
||||
|
@ -265,6 +265,11 @@ public abstract class NetworkParameters implements Serializable {
|
|||
return dnsSeeds;
|
||||
}
|
||||
|
||||
/** Returns discovery objects for seeds implementing the Cartographer protocol. See {@link org.bitcoinj.net.discovery.HttpDiscovery} for more info. */
|
||||
public HttpDiscovery.Details[] getHttpSeeds() {
|
||||
return httpSeeds;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Genesis block for this chain.</p>
|
||||
*
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.google.common.collect.*;
|
|||
import com.google.common.net.*;
|
||||
import com.google.common.primitives.*;
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.squareup.okhttp.*;
|
||||
import com.subgraph.orchid.*;
|
||||
import net.jcip.annotations.*;
|
||||
import org.bitcoinj.crypto.*;
|
||||
|
@ -304,7 +305,17 @@ public class PeerGroup implements TransactionBroadcaster {
|
|||
manager.setConnectTimeoutMillis(CONNECT_TIMEOUT_MSEC);
|
||||
PeerGroup result = new PeerGroup(context, chain, manager, torClient);
|
||||
result.setConnectTimeoutMillis(CONNECT_TIMEOUT_MSEC);
|
||||
result.addPeerDiscovery(new TorDiscovery(context.getParams(), torClient));
|
||||
|
||||
NetworkParameters params = context.getParams();
|
||||
HttpDiscovery.Details[] httpSeeds = params.getHttpSeeds();
|
||||
if (httpSeeds.length > 0) {
|
||||
// Use HTTP discovery when Tor is active and there is a Cartographer seed, for a much needed speed boost.
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
client.setSocketFactory(torClient.getSocketFactory());
|
||||
result.addPeerDiscovery(new HttpDiscovery(params, httpSeeds[0], client));
|
||||
} else {
|
||||
result.addPeerDiscovery(new TorDiscovery(params, torClient));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,22 +15,21 @@
|
|||
*/
|
||||
package org.bitcoinj.net.discovery;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import org.bitcoin.crawler.PeerSeedProtos;
|
||||
import com.google.common.annotations.*;
|
||||
import com.google.protobuf.*;
|
||||
import com.squareup.okhttp.*;
|
||||
import org.bitcoin.crawler.*;
|
||||
import org.bitcoinj.core.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import javax.annotation.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.security.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
/**
|
||||
* A class that knows how to read signed sets of seeds over HTTP, using a simple protobuf based protocol. See the
|
||||
|
@ -40,33 +39,56 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
public class HttpDiscovery implements PeerDiscovery {
|
||||
private static final int TIMEOUT_SECS = 20;
|
||||
|
||||
private final ECKey pubkey;
|
||||
private final URI uri;
|
||||
public static class Details {
|
||||
@Nullable public final ECKey pubkey;
|
||||
public final URI uri;
|
||||
|
||||
public Details(@Nullable ECKey pubkey, URI uri) {
|
||||
this.pubkey = pubkey;
|
||||
this.uri = uri;
|
||||
}
|
||||
}
|
||||
|
||||
private final Details details;
|
||||
private final NetworkParameters params;
|
||||
private final OkHttpClient client;
|
||||
|
||||
/**
|
||||
* Constructs a discovery object that will read data from the given HTTP[S] URI and, if a public key is provided,
|
||||
* will check the signature using that key.
|
||||
*/
|
||||
public HttpDiscovery(NetworkParameters params, URI uri, @Nullable ECKey pubkey) {
|
||||
checkArgument(uri.getScheme().startsWith("http"));
|
||||
this.uri = uri;
|
||||
this.pubkey = pubkey;
|
||||
this(params, new Details(pubkey, uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a discovery object that will read data from the given HTTP[S] URI and, if a public key is provided,
|
||||
* will check the signature using that key.
|
||||
*/
|
||||
public HttpDiscovery(NetworkParameters params, Details details) {
|
||||
this(params, details, new OkHttpClient());
|
||||
}
|
||||
|
||||
public HttpDiscovery(NetworkParameters params, Details details, OkHttpClient client) {
|
||||
checkArgument(details.uri.getScheme().startsWith("http"));
|
||||
this.details = details;
|
||||
this.params = params;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException {
|
||||
try {
|
||||
HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection();
|
||||
conn.setReadTimeout(TIMEOUT_SECS * 1000);
|
||||
conn.setConnectTimeout(TIMEOUT_SECS * 1000);
|
||||
conn.setRequestProperty("User-Agent", "bitcoinj " + VersionMessage.BITCOINJ_VERSION);
|
||||
InputStream stream = conn.getInputStream();
|
||||
Response response = client.newCall(new Request.Builder().url(details.uri.toURL()).build()).execute();
|
||||
if (!response.isSuccessful())
|
||||
throw new PeerDiscoveryException("HTTP request failed: " + response.code() + " " + response.message());
|
||||
InputStream stream = response.body().byteStream();
|
||||
GZIPInputStream zip = new GZIPInputStream(stream);
|
||||
PeerSeedProtos.SignedPeerSeeds proto = PeerSeedProtos.SignedPeerSeeds.parseDelimitedFrom(zip);
|
||||
stream.close();
|
||||
return protoToAddrs(proto);
|
||||
} catch (PeerDiscoveryException e1) {
|
||||
throw e1;
|
||||
} catch (Exception e) {
|
||||
throw new PeerDiscoveryException(e);
|
||||
}
|
||||
|
@ -74,11 +96,11 @@ public class HttpDiscovery implements PeerDiscovery {
|
|||
|
||||
@VisibleForTesting
|
||||
public InetSocketAddress[] protoToAddrs(PeerSeedProtos.SignedPeerSeeds proto) throws PeerDiscoveryException, InvalidProtocolBufferException, SignatureException {
|
||||
if (pubkey != null) {
|
||||
if (!Arrays.equals(proto.getPubkey().toByteArray(), pubkey.getPubKey()))
|
||||
if (details.pubkey != null) {
|
||||
if (!Arrays.equals(proto.getPubkey().toByteArray(), details.pubkey.getPubKey()))
|
||||
throw new PeerDiscoveryException("Public key mismatch");
|
||||
Sha256Hash hash = Sha256Hash.hash(proto.getPeerSeeds().toByteArray());
|
||||
pubkey.verifyOrThrow(hash.getBytes(), proto.getSignature().toByteArray());
|
||||
details.pubkey.verifyOrThrow(hash.getBytes(), proto.getSignature().toByteArray());
|
||||
}
|
||||
PeerSeedProtos.PeerSeeds seeds = PeerSeedProtos.PeerSeeds.parseFrom(proto.getPeerSeeds());
|
||||
if (seeds.getTimestamp() < Utils.currentTimeSeconds() - (60 * 60 * 24))
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
|
||||
package org.bitcoinj.params;
|
||||
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Utils;
|
||||
import com.google.common.io.*;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.net.discovery.*;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import java.net.*;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
/**
|
||||
* Parameters for the main production network on which people trade goods and services.
|
||||
|
@ -67,6 +69,15 @@ public class MainNetParams extends NetworkParameters {
|
|||
"seed.bitcoinstats.com", // Chris Decker
|
||||
"seed.bitnodes.io", // Addy Yeow
|
||||
};
|
||||
httpSeeds = new HttpDiscovery.Details[] {
|
||||
new HttpDiscovery.Details(
|
||||
ECKey.fromPublicOnly(BaseEncoding.base16().decode(
|
||||
"027a79143a4de36341494d21b6593015af6b2500e720ad2eda1c0b78165f4f38c4".toUpperCase()
|
||||
)),
|
||||
|
||||
URI.create("http://main.seed.vinumeris.com/peers")
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
private static MainNetParams instance;
|
||||
|
|
Loading…
Add table
Reference in a new issue