mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-02 05:27:17 +00:00
Merge branch 'master' of https://github.com/bitcoinj/bitcoinj
This commit is contained in:
@@ -100,11 +100,7 @@ public class PeerAddress extends ChildMessage {
|
||||
}
|
||||
|
||||
public static PeerAddress localhost(NetworkParameters params) {
|
||||
try {
|
||||
return new PeerAddress(InetAddress.getLocalHost(), params.getPort());
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e); // Broken system.
|
||||
}
|
||||
return new PeerAddress(InetAddress.getLoopbackAddress(), params.getPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.io.Closeables;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.common.util.concurrent.*;
|
||||
@@ -42,8 +43,10 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -119,6 +122,8 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
public static final long DEFAULT_PING_INTERVAL_MSEC = 2000;
|
||||
private long pingIntervalMsec = DEFAULT_PING_INTERVAL_MSEC;
|
||||
|
||||
@GuardedBy("lock") private boolean useLocalhostPeerWhenPossible = true;
|
||||
|
||||
private final NetworkParameters params;
|
||||
private final AbstractBlockChain chain;
|
||||
@GuardedBy("lock") private long fastCatchupTimeSecs;
|
||||
@@ -236,7 +241,7 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
}
|
||||
}
|
||||
|
||||
// Visible for testing
|
||||
@VisibleForTesting
|
||||
PeerEventListener startupListener = new PeerStartupListener();
|
||||
|
||||
/**
|
||||
@@ -687,6 +692,32 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
}
|
||||
}
|
||||
|
||||
private enum LocalhostCheckState {
|
||||
NOT_TRIED,
|
||||
FOUND,
|
||||
FOUND_AND_CONNECTED,
|
||||
NOT_THERE
|
||||
}
|
||||
private LocalhostCheckState localhostCheckState = LocalhostCheckState.NOT_TRIED;
|
||||
|
||||
private boolean maybeCheckForLocalhostPeer() {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
if (localhostCheckState == LocalhostCheckState.NOT_TRIED) {
|
||||
// Do a fast blocking connect to see if anything is listening.
|
||||
try {
|
||||
Socket socket = new Socket();
|
||||
socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), params.getPort()), vConnectTimeoutMillis);
|
||||
localhostCheckState = LocalhostCheckState.FOUND;
|
||||
Closeables.close(socket, true);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
log.info("Localhost peer not detected.");
|
||||
localhostCheckState = LocalhostCheckState.NOT_THERE;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Picks a peer from discovery and connects to it. If connection fails, picks another and tries again. */
|
||||
protected void connectToAnyPeer() throws PeerDiscoveryException {
|
||||
final State state = state();
|
||||
@@ -698,6 +729,12 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
long retryTime = 0;
|
||||
lock.lock();
|
||||
try {
|
||||
if (useLocalhostPeerWhenPossible && maybeCheckForLocalhostPeer()) {
|
||||
log.info("Localhost peer detected, trying to use it instead of P2P discovery");
|
||||
maxConnections = 0;
|
||||
connectToLocalHost();
|
||||
return;
|
||||
}
|
||||
if (!haveReadyInactivePeer(nowMillis)) {
|
||||
discoverPeers();
|
||||
groupBackoff.trackSuccess();
|
||||
@@ -716,14 +753,14 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
if (retryTime > nowMillis) {
|
||||
// Sleep until retry time
|
||||
final long millis = retryTime - nowMillis;
|
||||
log.info("Waiting {} msec before next connect attempt {}", millis, addr == null ? "" : " to " + addr);
|
||||
log.info("Waiting {} msec before next connect attempt {}", millis, addr == null ? "" : "to " + addr);
|
||||
Utils.sleep(millis);
|
||||
}
|
||||
}
|
||||
|
||||
// This method constructs a Peer and puts it into pendingPeers.
|
||||
checkNotNull(addr); // Help static analysis which can't see that addr is always set if we didn't throw above.
|
||||
connectTo(addr, false);
|
||||
connectTo(addr, false, vConnectTimeoutMillis);
|
||||
}
|
||||
|
||||
private boolean haveReadyInactivePeer(long nowMillis) {
|
||||
@@ -931,7 +968,7 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
public Peer connectTo(InetSocketAddress address) {
|
||||
PeerAddress peerAddress = new PeerAddress(address);
|
||||
backoffMap.put(peerAddress, new ExponentialBackoff(peerBackoffParams));
|
||||
return connectTo(peerAddress, true);
|
||||
return connectTo(peerAddress, true, vConnectTimeoutMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -941,12 +978,19 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
public Peer connectToLocalHost() {
|
||||
final PeerAddress localhost = PeerAddress.localhost(params);
|
||||
backoffMap.put(localhost, new ExponentialBackoff(peerBackoffParams));
|
||||
return connectTo(localhost, true);
|
||||
return connectTo(localhost, true, vConnectTimeoutMillis);
|
||||
}
|
||||
|
||||
// Internal version.
|
||||
/**
|
||||
* Creates a version message to send, constructs a Peer object and attempts to connect it. Returns the peer on
|
||||
* success or null on failure.
|
||||
* @param address Remote network address
|
||||
* @param incrementMaxConnections Whether to consider this connection an attempt to fill our quota, or something
|
||||
* explicitly requested.
|
||||
* @return Peer or null.
|
||||
*/
|
||||
@Nullable
|
||||
protected Peer connectTo(PeerAddress address, boolean incrementMaxConnections) {
|
||||
protected Peer connectTo(PeerAddress address, boolean incrementMaxConnections, int connectTimeoutMillis) {
|
||||
VersionMessage ver = getVersionMessage().duplicate();
|
||||
ver.bestHeight = chain == null ? 0 : chain.getBestChainHeight();
|
||||
ver.time = Utils.currentTimeSeconds();
|
||||
@@ -963,7 +1007,7 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
handlePeerDeath(peer);
|
||||
return null;
|
||||
}
|
||||
peer.setSocketTimeout(vConnectTimeoutMillis);
|
||||
peer.setSocketTimeout(connectTimeoutMillis);
|
||||
// When the channel has connected and version negotiated successfully, handleNewPeer will end up being called on
|
||||
// a worker thread.
|
||||
|
||||
@@ -1576,8 +1620,35 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link com.subgraph.orchid.TorClient} object for this peer group, if Tor is in use, null otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
public TorClient getTorClient() {
|
||||
return torClient;
|
||||
}
|
||||
|
||||
/** See {@link #setUseLocalhostPeerWhenPossible(boolean)} */
|
||||
public boolean getUseLocalhostPeerWhenPossible() {
|
||||
lock.lock();
|
||||
try {
|
||||
return useLocalhostPeerWhenPossible;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When true (the default), PeerGroup will attempt to connect to a Bitcoin node running on localhost before
|
||||
* attempting to use the P2P network. If successful, only localhost will be used. This makes for a simple
|
||||
* and easy way for a user to upgrade a bitcoinj based app running in SPV mode to fully validating security.
|
||||
*/
|
||||
public void setUseLocalhostPeerWhenPossible(boolean useLocalhostPeerWhenPossible) {
|
||||
lock.lock();
|
||||
try {
|
||||
this.useLocalhostPeerWhenPossible = useLocalhostPeerWhenPossible;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,6 +548,11 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isAndroidRuntime() {
|
||||
final String runtime = System.getProperty("java.runtime.name");
|
||||
return runtime != null && runtime.equals("Android Runtime");
|
||||
}
|
||||
|
||||
private static class Pair implements Comparable<Pair> {
|
||||
int item, count;
|
||||
public Pair(int item, int count) { this.count = count; this.item = item; }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.google.bitcoin.crypto;
|
||||
|
||||
import com.google.bitcoin.core.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -10,11 +11,6 @@ import java.lang.reflect.Method;
|
||||
public class DRMWorkaround {
|
||||
private static Logger log = LoggerFactory.getLogger(DRMWorkaround.class);
|
||||
|
||||
public static boolean isAndroidRuntime() {
|
||||
final String runtime = System.getProperty("java.runtime.name");
|
||||
return runtime != null && runtime.equals("Android Runtime");
|
||||
}
|
||||
|
||||
private static boolean done = false;
|
||||
|
||||
public static void maybeDisableExportControls() {
|
||||
@@ -26,7 +22,7 @@ public class DRMWorkaround {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
if (isAndroidRuntime())
|
||||
if (Utils.isAndroidRuntime())
|
||||
return;
|
||||
try {
|
||||
Field gate = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
package com.google.bitcoin.crypto;
|
||||
|
||||
import com.google.bitcoin.core.Sha256Hash;
|
||||
import com.google.bitcoin.core.Utils;
|
||||
import com.google.common.base.Joiner;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@@ -39,6 +42,8 @@ import static com.google.bitcoin.core.Utils.HEX;
|
||||
*/
|
||||
|
||||
public class MnemonicCode {
|
||||
private static final Logger log = LoggerFactory.getLogger(MnemonicCode.class);
|
||||
|
||||
private ArrayList<String> wordList;
|
||||
|
||||
public static String BIP39_ENGLISH_SHA256 = "ad90bf3beb7b0eb7e5acd74727dc0da96e0a280a258354e7293fb7e211ac03db";
|
||||
@@ -48,6 +53,19 @@ public class MnemonicCode {
|
||||
|
||||
private static final int PBKDF2_ROUNDS = 2048;
|
||||
|
||||
public static MnemonicCode INSTANCE;
|
||||
|
||||
static {
|
||||
try {
|
||||
INSTANCE = new MnemonicCode();
|
||||
} catch (IOException e) {
|
||||
// We expect failure on Android. The developer has to set INSTANCE themselves.
|
||||
if (!Utils.isAndroidRuntime())
|
||||
log.error("Failed to load word list", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Initialise from the included word list. Won't work on Android. */
|
||||
public MnemonicCode() throws IOException {
|
||||
this(MnemonicCode.class.getResourceAsStream("mnemonic/wordlist/english.txt"), BIP39_ENGLISH_SHA256);
|
||||
}
|
||||
|
||||
@@ -19,15 +19,14 @@ package com.google.bitcoin.wallet;
|
||||
|
||||
import com.google.bitcoin.crypto.*;
|
||||
import com.google.bitcoin.store.UnreadableWalletException;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
|
||||
import org.bitcoinj.wallet.Protos;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
@@ -42,31 +41,15 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
* code.
|
||||
*/
|
||||
public class DeterministicSeed implements EncryptableItem {
|
||||
// It would take more than 10^12 years to brute-force a 128 bit seed using $1B worth
|
||||
// of computing equipment.
|
||||
// It would take more than 10^12 years to brute-force a 128 bit seed using $1B worth of computing equipment.
|
||||
public static final int DEFAULT_SEED_ENTROPY_BITS = 128;
|
||||
public static final int MAX_SEED_ENTROPY_BITS = 512;
|
||||
public static final String UTF_8 = "UTF-8";
|
||||
|
||||
@Nullable private final byte[] seed;
|
||||
@Nullable private List<String> mnemonicCode;
|
||||
@Nullable private EncryptedData encryptedMnemonicCode;
|
||||
private final long creationTimeSeconds;
|
||||
|
||||
private static MnemonicCode MNEMONIC_CODEC;
|
||||
|
||||
private static synchronized MnemonicCode getCachedMnemonicCodec() {
|
||||
try {
|
||||
// This object can be large and has to load the word list from disk, so we lazy cache it.
|
||||
if (MNEMONIC_CODEC == null) {
|
||||
MNEMONIC_CODEC = new MnemonicCode();
|
||||
}
|
||||
return MNEMONIC_CODEC;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
DeterministicSeed(String mnemonicCode, String passphrase, long creationTimeSeconds) throws UnreadableWalletException {
|
||||
this(decodeMnemonicCode(mnemonicCode), passphrase, creationTimeSeconds);
|
||||
}
|
||||
@@ -120,7 +103,7 @@ public class DeterministicSeed implements EncryptableItem {
|
||||
Preconditions.checkArgument(entropy.length * 8 >= DEFAULT_SEED_ENTROPY_BITS, "entropy size too small");
|
||||
|
||||
try {
|
||||
this.mnemonicCode = getCachedMnemonicCodec().toMnemonic(entropy);
|
||||
this.mnemonicCode = MnemonicCode.INSTANCE.toMnemonic(entropy);
|
||||
} catch (MnemonicException.MnemonicLengthException e) {
|
||||
// cannot happen
|
||||
throw new RuntimeException(e);
|
||||
@@ -168,6 +151,7 @@ public class DeterministicSeed implements EncryptableItem {
|
||||
return getMnemonicAsBytes();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public byte[] getSeedBytes() {
|
||||
return seed;
|
||||
}
|
||||
@@ -196,11 +180,7 @@ public class DeterministicSeed implements EncryptableItem {
|
||||
}
|
||||
|
||||
private byte[] getMnemonicAsBytes() {
|
||||
try {
|
||||
return Joiner.on(" ").join(mnemonicCode).getBytes(UTF_8);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return Joiner.on(" ").join(mnemonicCode).getBytes(Charsets.UTF_8);
|
||||
}
|
||||
|
||||
public DeterministicSeed decrypt(KeyCrypter crypter, String passphrase, KeyParameter aesKey) {
|
||||
@@ -249,11 +229,11 @@ public class DeterministicSeed implements EncryptableItem {
|
||||
*/
|
||||
public void check() throws MnemonicException {
|
||||
if (mnemonicCode != null)
|
||||
getCachedMnemonicCodec().check(mnemonicCode);
|
||||
MnemonicCode.INSTANCE.check(mnemonicCode);
|
||||
}
|
||||
|
||||
byte[] getEntropyBytes() throws MnemonicException {
|
||||
return getCachedMnemonicCodec().toEntropy(mnemonicCode);
|
||||
return MnemonicCode.INSTANCE.toEntropy(mnemonicCode);
|
||||
}
|
||||
|
||||
/** Get the mnemonic code, or null if unknown. */
|
||||
@@ -263,13 +243,11 @@ public class DeterministicSeed implements EncryptableItem {
|
||||
}
|
||||
|
||||
private static List<String> decodeMnemonicCode(byte[] mnemonicCode) throws UnreadableWalletException {
|
||||
String code = null;
|
||||
try {
|
||||
code = new String(mnemonicCode, "UTF-8");
|
||||
return Splitter.on(" ").splitToList(new String(mnemonicCode, "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new UnreadableWalletException(e.toString());
|
||||
}
|
||||
return Splitter.on(" ").splitToList(code);
|
||||
}
|
||||
|
||||
private static List<String> decodeMnemonicCode(String mnemonicCode) {
|
||||
|
||||
@@ -34,7 +34,10 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -645,4 +648,21 @@ public class PeerGroupTest extends TestWithPeerGroup {
|
||||
future.get();
|
||||
assertTrue(future.isDone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preferLocalPeer() throws IOException {
|
||||
// Check that if we have a localhost port 8333 then it's used instead of the p2p network.
|
||||
ServerSocket local = new ServerSocket(params.getPort(), 100, InetAddress.getLoopbackAddress());
|
||||
try {
|
||||
peerGroup.startAsync();
|
||||
peerGroup.awaitRunning();
|
||||
local.accept().close(); // Probe connect
|
||||
local.accept(); // Real connect
|
||||
// If we get here it used the local peer. Check no others are in use.
|
||||
assertEquals(1, peerGroup.getMaxConnections());
|
||||
assertEquals(PeerAddress.localhost(params), peerGroup.getPendingPeers().get(0).getAddress());
|
||||
} finally {
|
||||
local.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -823,12 +823,12 @@ public class WalletTool {
|
||||
}
|
||||
} else if (!options.has("tor")) {
|
||||
// If Tor mode then PeerGroup already has discovery set up.
|
||||
if (params == RegTestParams.get()) {
|
||||
log.info("Assuming regtest node on localhost");
|
||||
peers.addAddress(PeerAddress.localhost(params));
|
||||
} else {
|
||||
// if (params == RegTestParams.get()) {
|
||||
// log.info("Assuming regtest node on localhost");
|
||||
// peers.addAddress(PeerAddress.localhost(params));
|
||||
// } else {
|
||||
peers.addPeerDiscovery(new DnsDiscovery(params));
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user