mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-12 10:15:52 +00:00
Use a local Bitcoin node if one is detected instead of the p2p network. This allows any user of a bitcoinj based app to upgrade from SPV to full mode security by just installing and running Core on the same machine. Can be controlled with a new property on PeerGroup.
This commit is contained in:
parent
812a4f59c4
commit
6679f42f4a
@ -100,11 +100,7 @@ public class PeerAddress extends ChildMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static PeerAddress localhost(NetworkParameters params) {
|
public static PeerAddress localhost(NetworkParameters params) {
|
||||||
try {
|
return new PeerAddress(InetAddress.getLoopbackAddress(), params.getPort());
|
||||||
return new PeerAddress(InetAddress.getLocalHost(), params.getPort());
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
throw new RuntimeException(e); // Broken system.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -33,6 +33,7 @@ import com.google.common.annotations.VisibleForTesting;
|
|||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.common.io.Closeables;
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
import com.google.common.primitives.Longs;
|
import com.google.common.primitives.Longs;
|
||||||
import com.google.common.util.concurrent.*;
|
import com.google.common.util.concurrent.*;
|
||||||
@ -42,8 +43,10 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
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;
|
public static final long DEFAULT_PING_INTERVAL_MSEC = 2000;
|
||||||
private long pingIntervalMsec = DEFAULT_PING_INTERVAL_MSEC;
|
private long pingIntervalMsec = DEFAULT_PING_INTERVAL_MSEC;
|
||||||
|
|
||||||
|
@GuardedBy("lock") private boolean useLocalhostPeerWhenPossible = true;
|
||||||
|
|
||||||
private final NetworkParameters params;
|
private final NetworkParameters params;
|
||||||
private final AbstractBlockChain chain;
|
private final AbstractBlockChain chain;
|
||||||
@GuardedBy("lock") private long fastCatchupTimeSecs;
|
@GuardedBy("lock") private long fastCatchupTimeSecs;
|
||||||
@ -236,7 +241,7 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visible for testing
|
@VisibleForTesting
|
||||||
PeerEventListener startupListener = new PeerStartupListener();
|
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. */
|
/** Picks a peer from discovery and connects to it. If connection fails, picks another and tries again. */
|
||||||
protected void connectToAnyPeer() throws PeerDiscoveryException {
|
protected void connectToAnyPeer() throws PeerDiscoveryException {
|
||||||
final State state = state();
|
final State state = state();
|
||||||
@ -698,6 +729,12 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
|||||||
long retryTime = 0;
|
long retryTime = 0;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
|
if (useLocalhostPeerWhenPossible && maybeCheckForLocalhostPeer()) {
|
||||||
|
log.info("Localhost peer detected, trying to use it instead of P2P discovery");
|
||||||
|
maxConnections = 0;
|
||||||
|
connectToLocalHost();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!haveReadyInactivePeer(nowMillis)) {
|
if (!haveReadyInactivePeer(nowMillis)) {
|
||||||
discoverPeers();
|
discoverPeers();
|
||||||
groupBackoff.trackSuccess();
|
groupBackoff.trackSuccess();
|
||||||
@ -723,7 +760,7 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
|||||||
|
|
||||||
// This method constructs a Peer and puts it into pendingPeers.
|
// 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.
|
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) {
|
private boolean haveReadyInactivePeer(long nowMillis) {
|
||||||
@ -931,7 +968,7 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
|||||||
public Peer connectTo(InetSocketAddress address) {
|
public Peer connectTo(InetSocketAddress address) {
|
||||||
PeerAddress peerAddress = new PeerAddress(address);
|
PeerAddress peerAddress = new PeerAddress(address);
|
||||||
backoffMap.put(peerAddress, new ExponentialBackoff(peerBackoffParams));
|
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() {
|
public Peer connectToLocalHost() {
|
||||||
final PeerAddress localhost = PeerAddress.localhost(params);
|
final PeerAddress localhost = PeerAddress.localhost(params);
|
||||||
backoffMap.put(localhost, new ExponentialBackoff(peerBackoffParams));
|
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
|
@Nullable
|
||||||
protected Peer connectTo(PeerAddress address, boolean incrementMaxConnections) {
|
protected Peer connectTo(PeerAddress address, boolean incrementMaxConnections, int connectTimeoutMillis) {
|
||||||
VersionMessage ver = getVersionMessage().duplicate();
|
VersionMessage ver = getVersionMessage().duplicate();
|
||||||
ver.bestHeight = chain == null ? 0 : chain.getBestChainHeight();
|
ver.bestHeight = chain == null ? 0 : chain.getBestChainHeight();
|
||||||
ver.time = Utils.currentTimeSeconds();
|
ver.time = Utils.currentTimeSeconds();
|
||||||
@ -963,7 +1007,7 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
|||||||
handlePeerDeath(peer);
|
handlePeerDeath(peer);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
peer.setSocketTimeout(vConnectTimeoutMillis);
|
peer.setSocketTimeout(connectTimeoutMillis);
|
||||||
// When the channel has connected and version negotiated successfully, handleNewPeer will end up being called on
|
// When the channel has connected and version negotiated successfully, handleNewPeer will end up being called on
|
||||||
// a worker thread.
|
// 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() {
|
public TorClient getTorClient() {
|
||||||
return torClient;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,10 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
@ -645,4 +648,21 @@ public class PeerGroupTest extends TestWithPeerGroup {
|
|||||||
future.get();
|
future.get();
|
||||||
assertTrue(future.isDone());
|
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")) {
|
} else if (!options.has("tor")) {
|
||||||
// If Tor mode then PeerGroup already has discovery set up.
|
// If Tor mode then PeerGroup already has discovery set up.
|
||||||
if (params == RegTestParams.get()) {
|
// if (params == RegTestParams.get()) {
|
||||||
log.info("Assuming regtest node on localhost");
|
// log.info("Assuming regtest node on localhost");
|
||||||
peers.addAddress(PeerAddress.localhost(params));
|
// peers.addAddress(PeerAddress.localhost(params));
|
||||||
} else {
|
// } else {
|
||||||
peers.addPeerDiscovery(new DnsDiscovery(params));
|
peers.addPeerDiscovery(new DnsDiscovery(params));
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user