mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-07-30 19:41:24 +00:00
Merge remote-tracking branch 'upstream/master'
Conflicts: core/src/main/java/com/dogecoin/dogecoinj/core/PeerGroup.java core/src/main/java/com/dogecoin/dogecoinj/core/TransactionBroadcast.java core/src/main/java/com/dogecoin/dogecoinj/core/Wallet.java core/src/main/java/com/dogecoin/dogecoinj/kits/WalletAppKit.java core/src/main/java/com/dogecoin/dogecoinj/net/discovery/DnsDiscovery.java core/src/main/java/com/dogecoin/dogecoinj/store/SPVBlockStore.java core/src/main/java/org/bitcoinj/net/discovery/HttpDiscovery.java wallettemplate/src/main/java/wallettemplate/Main.java
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
# configuration for https://travis-ci.org/bitcoinj/bitcoinj
|
||||
language: java
|
||||
jdk: oraclejdk8
|
||||
before_install: lsb_release -a
|
||||
install: true # remove default
|
||||
script:
|
||||
- mvn -q clean install
|
||||
|
@@ -168,4 +168,12 @@ public class Address extends VersionedChecksummedBytes {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation narrows the return type to <code>Address</code>.
|
||||
*/
|
||||
@Override
|
||||
public Address clone() throws CloneNotSupportedException {
|
||||
return (Address) super.clone();
|
||||
}
|
||||
}
|
||||
|
@@ -182,6 +182,13 @@ public class ECKey implements EncryptableItem, Serializable {
|
||||
}
|
||||
|
||||
protected ECKey(@Nullable BigInteger priv, ECPoint pub) {
|
||||
if (priv != null) {
|
||||
// Try and catch buggy callers or bad key imports, etc. Zero and one are special because these are often
|
||||
// used as sentinel values and because scripting languages have a habit of auto-casting true and false to
|
||||
// 1 and 0 or vice-versa. Type confusion bugs could therefore result in private keys with these values.
|
||||
checkArgument(!priv.equals(BigInteger.ZERO));
|
||||
checkArgument(!priv.equals(BigInteger.ONE));
|
||||
}
|
||||
this.priv = priv;
|
||||
this.pub = new LazyECPoint(checkNotNull(pub));
|
||||
}
|
||||
|
@@ -22,8 +22,17 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This command is supported only by <a href="http://github.com/bitcoinxt/bitcoinxt">Bitcoin XT</a> nodes, which
|
||||
* advertise themselves using the second service bit flag. It requests a query of the UTXO set keyed by a set of
|
||||
* outpoints (i.e. tx hash and output index). The result contains a bitmap of spentness flags, and the contents of
|
||||
* the associated outputs if they were found. The results aren't authenticated by anything, so the peer could lie,
|
||||
* or a man in the middle could swap out its answer for something else.
|
||||
*/
|
||||
public class GetUTXOsMessage extends Message {
|
||||
public static final int MIN_PROTOCOL_VERSION = 70003;
|
||||
public static final int MIN_PROTOCOL_VERSION = 70002;
|
||||
/** Bitmask of service flags required for a node to support this command (0x3) */
|
||||
public static final int SERVICE_FLAGS_REQUIRED = 3;
|
||||
|
||||
private boolean includeMempool;
|
||||
private ImmutableList<TransactionOutPoint> outPoints;
|
||||
|
@@ -64,6 +64,9 @@ public abstract class NetworkParameters implements Serializable {
|
||||
public static final String PAYMENT_PROTOCOL_ID_MAINNET = "main";
|
||||
/** The string used by the payment protocol to represent the test net. */
|
||||
public static final String PAYMENT_PROTOCOL_ID_TESTNET = "test";
|
||||
/** The string used by the payment protocol to represent unit testing (note that this is non-standard). */
|
||||
public static final String PAYMENT_PROTOCOL_ID_UNIT_TESTS = "unittest";
|
||||
public static final String PAYMENT_PROTOCOL_ID_REGTEST = "regtest";
|
||||
|
||||
// TODO: Seed nodes should be here as well.
|
||||
|
||||
@@ -233,6 +236,10 @@ public abstract class NetworkParameters implements Serializable {
|
||||
return MainNetParams.get();
|
||||
} else if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_TESTNET)) {
|
||||
return TestNet3Params.get();
|
||||
} else if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_UNIT_TESTS)) {
|
||||
return UnitTestParams.get();
|
||||
} else if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_REGTEST)) {
|
||||
return RegTestParams.get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@@ -282,6 +282,14 @@ public class Peer extends PeerSocketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void timeoutOccurred() {
|
||||
super.timeoutOccurred();
|
||||
if (!connectionOpenFuture.isDone()) {
|
||||
connectionClosed(); // Invoke the event handlers to tell listeners e.g. PeerGroup that we never managed to connect.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionClosed() {
|
||||
for (final PeerListenerRegistration registration : eventListeners) {
|
||||
@@ -414,9 +422,9 @@ public class Peer extends PeerSocketHandler {
|
||||
future.set((UTXOsMessage)m);
|
||||
}
|
||||
} else if (m instanceof RejectMessage) {
|
||||
log.error("Received Message {}", m);
|
||||
log.error("{} {}: Received {}", this, getPeerVersionMessage().subVer, m);
|
||||
} else {
|
||||
log.warn("Received unhandled message: {}", m);
|
||||
log.warn("{}: Received unhandled message: {}", this, m);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -65,7 +65,8 @@ public interface PeerEventListener {
|
||||
/**
|
||||
* Called when a peer is disconnected. Note that this won't be called if the listener is registered on a
|
||||
* {@link PeerGroup} and the group is in the process of shutting down. If this listener is registered to a
|
||||
* {@link Peer} instead of a {@link PeerGroup}, peerCount will always be 0.
|
||||
* {@link Peer} instead of a {@link PeerGroup}, peerCount will always be 0. This handler can be called without
|
||||
* a corresponding invocation of onPeerConnected if the initial connection is never successful.
|
||||
*
|
||||
* @param peer
|
||||
* @param peerCount the total number of connected peers
|
||||
|
@@ -19,6 +19,7 @@ package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.params.MainNetParams;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.*;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
@@ -70,17 +71,22 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
* <p>The PeerGroup can broadcast a transaction to the currently connected set of peers. It can
|
||||
* also handle download of the blockchain from peers, restarting the process when peers die.</p>
|
||||
*
|
||||
* <p>PeerGroup implements the {@link Service} interface. This means before it will do anything,
|
||||
* you must call the {@link com.google.common.util.concurrent.Service#start()} method (which returns
|
||||
* a future) or {@link com.google.common.util.concurrent.Service#startAndWait()} method, which will block
|
||||
* until peer discovery is completed and some outbound connections have been initiated (it will return
|
||||
* before handshaking is done, however). You should call {@link com.google.common.util.concurrent.Service#stop()}
|
||||
* when finished. Note that not all methods of PeerGroup are safe to call from a UI thread as some may do
|
||||
* network IO, but starting and stopping the service should be fine.</p>
|
||||
* <p>A PeerGroup won't do anything until you call the {@link PeerGroup#start()} method
|
||||
* which will block until peer discovery is completed and some outbound connections
|
||||
* have been initiated (it will return before handshaking is done, however).
|
||||
* You should call {@link PeerGroup#stop()} when finished. Note that not all methods
|
||||
* of PeerGroup are safe to call from a UI thread as some may do network IO,
|
||||
* but starting and stopping the service should be fine.</p>
|
||||
*/
|
||||
public class PeerGroup implements TransactionBroadcaster {
|
||||
private static final Logger log = LoggerFactory.getLogger(PeerGroup.class);
|
||||
private static final int DEFAULT_CONNECTIONS = 4;
|
||||
/**
|
||||
* The default number of connections to the p2p network the library will try to build. This is set to 12 empirically.
|
||||
* It used to be 4, but because we divide the connection pool in two for broadcasting transactions, that meant we
|
||||
* were only sending transactions to two peers and sometimes this wasn't reliable enough: transactions wouldn't
|
||||
* get through.
|
||||
*/
|
||||
public static final int DEFAULT_CONNECTIONS = 12;
|
||||
private static final int TOR_TIMEOUT_SECONDS = 60;
|
||||
private int vMaxPeersToDiscoverCount = 100;
|
||||
|
||||
@@ -501,7 +507,8 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
|
||||
private void triggerConnections() {
|
||||
// Run on a background thread due to the need to potentially retry and back off in the background.
|
||||
executor.execute(triggerConnectionsJob);
|
||||
if (!executor.isShutdown())
|
||||
executor.execute(triggerConnectionsJob);
|
||||
}
|
||||
|
||||
/** The maximum number of connections that we will create to peers. */
|
||||
@@ -885,6 +892,7 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
torClient.stop();
|
||||
}
|
||||
vRunning = false;
|
||||
log.info("Stopped.");
|
||||
} catch (Throwable e) {
|
||||
log.error("Exception when shutting down", e); // The executor swallows exceptions :(
|
||||
}
|
||||
@@ -898,6 +906,7 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
public void stop() {
|
||||
try {
|
||||
stopAsync();
|
||||
log.info("Awaiting PeerGroup shutdown ...");
|
||||
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -1030,7 +1039,7 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
return inFlightRecalculations.get(mode);
|
||||
inFlightRecalculations.put(mode, future);
|
||||
}
|
||||
executor.execute(new Runnable() {
|
||||
Runnable command = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
@@ -1080,7 +1089,12 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
}
|
||||
future.set(result.filter);
|
||||
}
|
||||
});
|
||||
};
|
||||
try {
|
||||
executor.execute(command);
|
||||
} catch (RejectedExecutionException e) {
|
||||
// Can happen during shutdown.
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
@@ -1168,10 +1182,15 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
pendingPeers.add(peer);
|
||||
|
||||
try {
|
||||
channels.openConnection(address.toSocketAddress(), peer);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to connect to " + address + ": " + e.getMessage());
|
||||
handlePeerDeath(peer, e);
|
||||
log.info("Attempting connection to {} ({} connected, {} pending, {} max)", address,
|
||||
peers.size(), pendingPeers.size(), maxConnections);
|
||||
ListenableFuture<SocketAddress> future = channels.openConnection(address.toSocketAddress(), peer);
|
||||
if (future.isDone())
|
||||
Uninterruptibles.getUninterruptibly(future);
|
||||
} catch (ExecutionException e) {
|
||||
Throwable cause = Throwables.getRootCause(e);
|
||||
log.warn("Failed to connect to " + address + ": " + cause.getMessage());
|
||||
handlePeerDeath(peer, cause);
|
||||
return null;
|
||||
}
|
||||
peer.setSocketTimeout(connectTimeoutMillis);
|
||||
@@ -1244,10 +1263,10 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
backoffMap.get(peer.getAddress()).trackSuccess();
|
||||
|
||||
// Sets up the newly connected peer so it can do everything it needs to.
|
||||
log.info("{}: New peer", peer);
|
||||
pendingPeers.remove(peer);
|
||||
peers.add(peer);
|
||||
newSize = peers.size();
|
||||
log.info("{}: New peer ({} connected, {} pending, {} max)", peer, newSize, pendingPeers.size(), maxConnections);
|
||||
// Give the peer a filter that can be used to probabilistically drop transactions that
|
||||
// aren't relevant to our wallet. We may still receive some false positives, which is
|
||||
// OK because it helps improve wallet privacy. Old nodes will just ignore the message.
|
||||
@@ -1385,7 +1404,7 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
}
|
||||
}
|
||||
|
||||
protected void handlePeerDeath(final Peer peer, @Nullable Exception exception) {
|
||||
protected void handlePeerDeath(final Peer peer, @Nullable Throwable exception) {
|
||||
// Peer deaths can occur during startup if a connect attempt after peer discovery aborts immediately.
|
||||
if (!isRunning()) return;
|
||||
|
||||
@@ -1398,7 +1417,7 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
|
||||
PeerAddress address = peer.getAddress();
|
||||
|
||||
log.info("{}: Peer died", address);
|
||||
log.info("{}: Peer died ({} connected, {} pending, {} max)", address, peers.size(), pendingPeers.size(), maxConnections);
|
||||
if (peer == downloadPeer) {
|
||||
log.info("Download peer died. Picking a new one.");
|
||||
setDownloadPeer(null);
|
||||
@@ -1416,11 +1435,12 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
|
||||
groupBackoff.trackFailure();
|
||||
|
||||
if (!(exception instanceof NoRouteToHostException)) {
|
||||
if (exception instanceof NoRouteToHostException) {
|
||||
if (address.getAddr() instanceof Inet6Address && !ipv6Unreachable) {
|
||||
ipv6Unreachable = true;
|
||||
log.warn("IPv6 peer connect failed due to routing failure, ignoring IPv6 addresses from now on");
|
||||
}
|
||||
} else {
|
||||
backoffMap.get(address).trackFailure();
|
||||
// Put back on inactive list
|
||||
inactives.offer(address);
|
||||
@@ -1502,7 +1522,7 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mutable array list of peers that implement the given protocol version or better.
|
||||
* Returns an array list of peers that implement the given protocol version or better.
|
||||
*/
|
||||
public List<Peer> findPeersOfAtLeastVersion(long protocolVersion) {
|
||||
lock.lock();
|
||||
@@ -1517,11 +1537,58 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a future that is triggered when there are at least the requested number of connected peers that support
|
||||
* the given protocol version or higher. To block immediately, just call get() on the result.
|
||||
*
|
||||
* @param numPeers How many peers to wait for.
|
||||
* @param mask An integer representing a bit mask that will be ANDed with the peers advertised service masks.
|
||||
* @return a future that will be triggered when the number of connected peers implementing protocolVersion or higher >= numPeers
|
||||
*/
|
||||
public ListenableFuture<List<Peer>> waitForPeersWithServiceMask(final int numPeers, final int mask) {
|
||||
lock.lock();
|
||||
try {
|
||||
List<Peer> foundPeers = findPeersWithServiceMask(mask);
|
||||
if (foundPeers.size() >= numPeers)
|
||||
return Futures.immediateFuture(foundPeers);
|
||||
final SettableFuture<List<Peer>> future = SettableFuture.create();
|
||||
addEventListener(new AbstractPeerEventListener() {
|
||||
@Override
|
||||
public void onPeerConnected(Peer peer, int peerCount) {
|
||||
final List<Peer> peers = findPeersWithServiceMask(mask);
|
||||
if (peers.size() >= numPeers) {
|
||||
future.set(peers);
|
||||
removeEventListener(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
return future;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array list of peers that match the requested service bit mask.
|
||||
*/
|
||||
public List<Peer> findPeersWithServiceMask(int mask) {
|
||||
lock.lock();
|
||||
try {
|
||||
ArrayList<Peer> results = new ArrayList<Peer>(peers.size());
|
||||
for (Peer peer : peers)
|
||||
if ((peer.getPeerVersionMessage().localServices & mask) == mask)
|
||||
results.add(peer);
|
||||
return results;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of connections that are required before transactions will be broadcast. If there aren't
|
||||
* enough, {@link PeerGroup#broadcastTransaction(Transaction)} will wait until the minimum number is reached so
|
||||
* propagation across the network can be observed. If no value has been set using
|
||||
* {@link PeerGroup#setMinBroadcastConnections(int)} a default of half of whatever
|
||||
* {@link PeerGroup#setMinBroadcastConnections(int)} a default of 80% of whatever
|
||||
* {@link com.dogecoin.dogecoinj.core.PeerGroup#getMaxConnections()} returns is used.
|
||||
*/
|
||||
public int getMinBroadcastConnections() {
|
||||
@@ -1532,7 +1599,7 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
if (max <= 1)
|
||||
return max;
|
||||
else
|
||||
return (int) Math.round(getMaxConnections() / 2.0);
|
||||
return (int) Math.round(getMaxConnections() * 0.8);
|
||||
}
|
||||
return minBroadcastConnections;
|
||||
} finally {
|
||||
@@ -1609,9 +1676,8 @@ public class PeerGroup implements TransactionBroadcaster {
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable throwable) {
|
||||
// This can't happen with the current code, but just in case one day that changes ...
|
||||
// This can happen if we get a reject message from a peer.
|
||||
runningBroadcasts.remove(broadcast);
|
||||
throw new RuntimeException(throwable);
|
||||
}
|
||||
});
|
||||
// Keep a reference to the TransactionBroadcast object. This is important because otherwise, the entire tree
|
||||
|
@@ -72,8 +72,13 @@ public class RejectMessage extends Message {
|
||||
super(params, payload, 0);
|
||||
}
|
||||
|
||||
public RejectMessage(NetworkParameters params, byte[] payload, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException {
|
||||
super(params, payload, 0, parseLazy, parseRetain, length);
|
||||
/** Constructs a reject message that fingers the object with the given hash as rejected for the given reason. */
|
||||
public RejectMessage(NetworkParameters params, RejectCode code, Sha256Hash hash, String message, String reason) throws ProtocolException {
|
||||
super(params);
|
||||
this.code = code;
|
||||
this.messageHash = hash;
|
||||
this.message = message;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,7 +107,7 @@ public class RejectMessage extends Message {
|
||||
stream.write(new VarInt(reasonBytes.length).encode());
|
||||
stream.write(reasonBytes);
|
||||
if (message.equals("block") || message.equals("tx"))
|
||||
stream.write(messageHash.getBytes());
|
||||
stream.write(Utils.reverseBytes(messageHash.getBytes()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -16,18 +16,14 @@
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.utils.Threading;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.google.common.annotations.*;
|
||||
import com.google.common.base.*;
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.dogecoin.dogecoinj.utils.*;
|
||||
import org.slf4j.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import javax.annotation.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents a single transaction broadcast that we are performing. A broadcast occurs after a new transaction is created
|
||||
@@ -49,8 +45,12 @@ public class TransactionBroadcast {
|
||||
/** Used for shuffling the peers before broadcast: unit tests can replace this to make themselves deterministic. */
|
||||
@VisibleForTesting
|
||||
public static Random random = new Random();
|
||||
|
||||
private Transaction pinnedTx;
|
||||
|
||||
// Tracks which nodes sent us a reject message about this broadcast, if any. Useful for debugging.
|
||||
private Map<Peer, RejectMessage> rejects = Collections.synchronizedMap(new HashMap<Peer, RejectMessage>());
|
||||
|
||||
// TODO: Context being owned by BlockChain isn't right w.r.t future intentions so it shouldn't really be optional here.
|
||||
TransactionBroadcast(PeerGroup peerGroup, @Nullable Context context, Transaction tx) {
|
||||
this.peerGroup = peerGroup;
|
||||
@@ -73,8 +73,14 @@ public class TransactionBroadcast {
|
||||
if (m instanceof RejectMessage) {
|
||||
RejectMessage rejectMessage = (RejectMessage)m;
|
||||
if (tx.getHash().equals(rejectMessage.getRejectedObjectHash())) {
|
||||
future.setException(new RejectedTransactionException(tx, rejectMessage));
|
||||
peerGroup.removeEventListener(this);
|
||||
rejects.put(peer, rejectMessage);
|
||||
int size = rejects.size();
|
||||
long threshold = Math.round(numWaitingFor / 2.0);
|
||||
if (size > threshold) {
|
||||
log.warn("Threshold for considering broadcast rejected has been reached ({}/{})", size, threshold);
|
||||
future.setException(new RejectedTransactionException(tx, rejectMessage));
|
||||
peerGroup.removeEventListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
@@ -83,7 +89,7 @@ public class TransactionBroadcast {
|
||||
|
||||
public ListenableFuture<Transaction> broadcast() {
|
||||
peerGroup.addEventListener(rejectionListener, Threading.SAME_THREAD);
|
||||
log.info("Waiting for {} peers required for broadcast ...", minConnections);
|
||||
log.info("Waiting for {} peers required for broadcast, we have {} ...", minConnections, peerGroup.getConnectedPeers().size());
|
||||
peerGroup.waitForPeers(minConnections).addListener(new EnoughAvailablePeers(), Threading.SAME_THREAD);
|
||||
return future;
|
||||
}
|
||||
@@ -121,7 +127,7 @@ public class TransactionBroadcast {
|
||||
numWaitingFor = (int) Math.ceil((peers.size() - numToBroadcastTo) / 2.0);
|
||||
Collections.shuffle(peers, random);
|
||||
peers = peers.subList(0, numToBroadcastTo);
|
||||
log.info("broadcastTransaction: We have {} peers, adding {} to the memory pool and sending to {} peers, will wait for {}: {}",
|
||||
log.info("broadcastTransaction: We have {} peers, adding {} to the memory pool and sending to {} peers, will wait for {}, sending to: {}",
|
||||
numConnected, tx.getHashAsString(), numToBroadcastTo, numWaitingFor, Joiner.on(",").join(peers));
|
||||
for (Peer peer : peers) {
|
||||
try {
|
||||
@@ -147,7 +153,7 @@ public class TransactionBroadcast {
|
||||
@Override
|
||||
public void onConfidenceChanged(TransactionConfidence conf, ChangeReason reason) {
|
||||
// The number of peers that announced this tx has gone up.
|
||||
int numSeenPeers = conf.numBroadcastPeers();
|
||||
int numSeenPeers = conf.numBroadcastPeers() + rejects.size();
|
||||
boolean mined = tx.getAppearsInHashes() != null;
|
||||
log.info("broadcastTransaction: {}: TX {} seen by {} peers{}", reason, pinnedTx.getHashAsString(),
|
||||
numSeenPeers, mined ? " and mined" : "");
|
||||
|
@@ -22,6 +22,7 @@ import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.primitives.UnsignedBytes;
|
||||
|
||||
/**
|
||||
* <p>In Bitcoin the following format is often used to represent some type of key:</p>
|
||||
@@ -31,7 +32,7 @@ import com.google.common.base.Objects;
|
||||
* <p>and the result is then Base58 encoded. This format is used for addresses, and private keys exported using the
|
||||
* dumpprivkey command.</p>
|
||||
*/
|
||||
public class VersionedChecksummedBytes implements Serializable {
|
||||
public class VersionedChecksummedBytes implements Serializable, Cloneable, Comparable<VersionedChecksummedBytes> {
|
||||
protected final int version;
|
||||
protected byte[] bytes;
|
||||
|
||||
@@ -79,6 +80,34 @@ public class VersionedChecksummedBytes implements Serializable {
|
||||
&& Arrays.equals(this.bytes, other.bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation narrows the return type to <code>VersionedChecksummedBytes</code>
|
||||
* and allows subclasses to throw <code>CloneNotSupportedException</code> even though it
|
||||
* is never thrown by this implementation.
|
||||
*/
|
||||
@Override
|
||||
public VersionedChecksummedBytes clone() throws CloneNotSupportedException {
|
||||
return (VersionedChecksummedBytes) super.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation uses an optimized Google Guava method to compare <code>bytes</code>.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(VersionedChecksummedBytes o) {
|
||||
int versionCompare = Integer.valueOf(this.version).compareTo(Integer.valueOf(o.version)); // JDK 6 way
|
||||
if (versionCompare == 0) {
|
||||
// Would there be a performance benefit to caching the comparator?
|
||||
return UnsignedBytes.lexicographicalComparator().compare(this.bytes, o.bytes);
|
||||
} else {
|
||||
return versionCompare;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "version" or "header" byte: the first byte of the data. This is used to disambiguate what the
|
||||
* contents apply to, for example, which network the key or address is valid on.
|
||||
|
@@ -3601,9 +3601,8 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
req.tx.shuffleOutputs();
|
||||
|
||||
// Now sign the inputs, thus proving that we are entitled to redeem the connected outputs.
|
||||
if (req.signInputs) {
|
||||
if (req.signInputs)
|
||||
signTransaction(req);
|
||||
}
|
||||
|
||||
// Check size.
|
||||
int size = req.tx.bitcoinSerialize().length;
|
||||
@@ -3611,9 +3610,8 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
throw new ExceededMaxTransactionSize();
|
||||
|
||||
final Coin calculatedFee = totalInput.subtract(totalOutput);
|
||||
if (calculatedFee != null) {
|
||||
if (calculatedFee != null)
|
||||
log.info(" with a fee of {}", calculatedFee.toFriendlyString());
|
||||
}
|
||||
|
||||
// Label the transaction as being self created. We can use this later to spend its change output even before
|
||||
// the transaction is confirmed. We deliberately won't bother notifying listeners here as there's not much
|
||||
|
@@ -83,7 +83,8 @@ public class KeyCrypterScrypt implements KeyCrypter, Serializable {
|
||||
|
||||
private static final transient SecureRandom secureRandom;
|
||||
|
||||
private static byte[] randomSalt() {
|
||||
/** Returns SALT_LENGTH (8) bytes of random data */
|
||||
public static byte[] randomSalt() {
|
||||
byte[] salt = new byte[SALT_LENGTH];
|
||||
secureRandom.nextBytes(salt);
|
||||
return salt;
|
||||
|
@@ -15,36 +15,27 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.kits;
|
||||
package org.bitcoinj.kits;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.*;
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.subgraph.orchid.TorClient;
|
||||
import com.subgraph.orchid.*;
|
||||
import com.dogecoin.dogecoinj.core.*;
|
||||
import com.dogecoin.dogecoinj.net.discovery.DnsDiscovery;
|
||||
import com.dogecoin.dogecoinj.net.discovery.PeerDiscovery;
|
||||
import com.dogecoin.dogecoinj.protocols.channels.StoredPaymentChannelClientStates;
|
||||
import com.dogecoin.dogecoinj.protocols.channels.StoredPaymentChannelServerStates;
|
||||
import com.dogecoin.dogecoinj.store.BlockStoreException;
|
||||
import com.dogecoin.dogecoinj.store.SPVBlockStore;
|
||||
import com.dogecoin.dogecoinj.store.WalletProtobufSerializer;
|
||||
import com.dogecoin.dogecoinj.wallet.DeterministicSeed;
|
||||
import com.dogecoin.dogecoinj.wallet.KeyChainGroup;
|
||||
import com.dogecoin.dogecoinj.wallet.Protos;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.dogecoin.dogecoinj.net.discovery.*;
|
||||
import com.dogecoin.dogecoinj.params.*;
|
||||
import com.dogecoin.dogecoinj.protocols.channels.*;
|
||||
import com.dogecoin.dogecoinj.store.*;
|
||||
import com.dogecoin.dogecoinj.wallet.*;
|
||||
import org.slf4j.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.*;
|
||||
import java.io.*;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.net.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
/**
|
||||
* <p>Utility class that wraps the boilerplate needed to set up a new SPV bitcoinj app. Instantiate it with a directory
|
||||
@@ -275,7 +266,7 @@ public class WalletAppKit extends AbstractIdleService {
|
||||
log.info("Deleting the chain file in preparation from restore.");
|
||||
vStore.close();
|
||||
if (!chainFile.delete())
|
||||
throw new Exception("Failed to delete chain file in preparation for restore.");
|
||||
throw new IOException("Failed to delete chain file in preparation for restore.");
|
||||
vStore = new SPVBlockStore(params, chainFile);
|
||||
}
|
||||
} else {
|
||||
@@ -286,7 +277,7 @@ public class WalletAppKit extends AbstractIdleService {
|
||||
log.info("Deleting the chain file in preparation from restore.");
|
||||
vStore.close();
|
||||
if (!chainFile.delete())
|
||||
throw new Exception("Failed to delete chain file in preparation for restore.");
|
||||
throw new IOException("Failed to delete chain file in preparation for restore.");
|
||||
vStore = new SPVBlockStore(params, chainFile);
|
||||
}
|
||||
}
|
||||
@@ -301,7 +292,7 @@ public class WalletAppKit extends AbstractIdleService {
|
||||
for (PeerAddress addr : peerAddresses) vPeerGroup.addAddress(addr);
|
||||
vPeerGroup.setMaxConnections(peerAddresses.length);
|
||||
peerAddresses = null;
|
||||
} else {
|
||||
} else if (params != RegTestParams.get()) {
|
||||
vPeerGroup.addPeerDiscovery(discovery != null ? discovery : new DnsDiscovery(params));
|
||||
}
|
||||
vChain.addWallet(vWallet);
|
||||
|
@@ -30,7 +30,8 @@ import java.net.SocketAddress;
|
||||
*/
|
||||
public interface ClientConnectionManager extends Service {
|
||||
/**
|
||||
* Creates a new connection to the given address, with the given parser used to handle incoming data.
|
||||
* Creates a new connection to the given address, with the given parser used to handle incoming data. Any errors
|
||||
* that occur during connection will be returned in the given future, including errors that can occur immediately.
|
||||
*/
|
||||
ListenableFuture<SocketAddress> openConnection(SocketAddress serverAddress, StreamParser parser);
|
||||
|
||||
|
@@ -17,10 +17,7 @@
|
||||
package com.dogecoin.dogecoinj.net;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.util.concurrent.AbstractExecutionThreadService;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import com.google.common.util.concurrent.*;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -29,7 +26,7 @@ import java.net.SocketAddress;
|
||||
import java.nio.channels.*;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* A class which manages a set of client connections. Uses Java NIO to select network events and processes them in a
|
||||
@@ -185,4 +182,16 @@ public class NioClientManager extends AbstractExecutionThreadService implements
|
||||
handler.closeConnection(); // Removes handler from connectedHandlers before returning
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Executor executor() {
|
||||
return new Executor() {
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
Thread thread = new Thread(command, "NioClientManager");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -17,14 +17,10 @@
|
||||
|
||||
package com.dogecoin.dogecoinj.net.discovery;
|
||||
|
||||
import com.dogecoin.dogecoinj.core.NetworkParameters;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.dogecoin.dogecoinj.utils.DaemonThreadFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.dogecoin.dogecoinj.core.*;
|
||||
import com.dogecoin.dogecoinj.utils.*;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
@@ -39,12 +35,7 @@ import java.util.concurrent.*;
|
||||
* will return up to 30 random peers from the set of those returned within the timeout period. If you want more peers
|
||||
* to connect to, you need to discover them via other means (like addr broadcasts).</p>
|
||||
*/
|
||||
public class DnsDiscovery implements PeerDiscovery {
|
||||
private static final Logger log = LoggerFactory.getLogger(DnsDiscovery.class);
|
||||
|
||||
private final String[] dnsSeeds;
|
||||
private final NetworkParameters netParams;
|
||||
|
||||
public class DnsDiscovery extends MultiplexingDiscovery {
|
||||
/**
|
||||
* Supports finding peers through DNS A records. Community run DNS entry points will be used.
|
||||
*
|
||||
@@ -58,65 +49,54 @@ public class DnsDiscovery implements PeerDiscovery {
|
||||
* Supports finding peers through DNS A records.
|
||||
*
|
||||
* @param dnsSeeds Host names to be examined for seed addresses.
|
||||
* @param netParams Network parameters to be used for port information.
|
||||
* @param params Network parameters to be used for port information.
|
||||
*/
|
||||
public DnsDiscovery(String[] dnsSeeds, NetworkParameters netParams) {
|
||||
this.dnsSeeds = dnsSeeds;
|
||||
this.netParams = netParams;
|
||||
public DnsDiscovery(String[] dnsSeeds, NetworkParameters params) {
|
||||
super(params, buildDiscoveries(params, dnsSeeds));
|
||||
}
|
||||
|
||||
private static List<PeerDiscovery> buildDiscoveries(NetworkParameters params, String[] seeds) {
|
||||
List<PeerDiscovery> discoveries = new ArrayList<PeerDiscovery>(seeds.length);
|
||||
for (String seed : seeds)
|
||||
discoveries.add(new DnsSeedDiscovery(params, seed));
|
||||
return discoveries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException {
|
||||
if (dnsSeeds == null || dnsSeeds.length == 0)
|
||||
throw new PeerDiscoveryException("No DNS seeds configured; unable to find any peers");
|
||||
protected ExecutorService createExecutor() {
|
||||
// Attempted workaround for reported bugs on Linux in which gethostbyname does not appear to be properly
|
||||
// thread safe and can cause segfaults on some libc versions.
|
||||
if (System.getProperty("os.name").toLowerCase().contains("linux"))
|
||||
return Executors.newSingleThreadExecutor(new DaemonThreadFactory());
|
||||
else
|
||||
return Executors.newFixedThreadPool(seeds.size(), new DaemonThreadFactory());
|
||||
}
|
||||
|
||||
// Java doesn't have an async DNS API so we have to do all lookups in a thread pool, as sometimes seeds go
|
||||
// hard down and it takes ages to give up and move on.
|
||||
ExecutorService threadPool = Executors.newFixedThreadPool(dnsSeeds.length, new DaemonThreadFactory());
|
||||
try {
|
||||
List<Callable<InetAddress[]>> tasks = Lists.newArrayList();
|
||||
for (final String seed : dnsSeeds) {
|
||||
tasks.add(new Callable<InetAddress[]>() {
|
||||
@Override
|
||||
public InetAddress[] call() throws Exception {
|
||||
return InetAddress.getAllByName(seed);
|
||||
}
|
||||
});
|
||||
/** Implements discovery from a single DNS host. */
|
||||
public static class DnsSeedDiscovery implements PeerDiscovery {
|
||||
private final String hostname;
|
||||
private final NetworkParameters params;
|
||||
|
||||
public DnsSeedDiscovery(NetworkParameters params, String hostname) {
|
||||
this.hostname = hostname;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException {
|
||||
try {
|
||||
InetAddress[] response = InetAddress.getAllByName(hostname);
|
||||
InetSocketAddress[] result = new InetSocketAddress[response.length];
|
||||
for (int i = 0; i < response.length; i++)
|
||||
result[i] = new InetSocketAddress(response[i], params.getPort());
|
||||
return result;
|
||||
} catch (UnknownHostException e) {
|
||||
throw new PeerDiscoveryException(e);
|
||||
}
|
||||
final List<Future<InetAddress[]>> futures = threadPool.invokeAll(tasks, timeoutValue, timeoutUnit);
|
||||
ArrayList<InetSocketAddress> addrs = Lists.newArrayList();
|
||||
for (int i = 0; i < futures.size(); i++) {
|
||||
Future<InetAddress[]> future = futures.get(i);
|
||||
if (future.isCancelled()) {
|
||||
log.warn("DNS seed {}: timed out", dnsSeeds[i]);
|
||||
continue; // Timed out.
|
||||
}
|
||||
final InetAddress[] inetAddresses;
|
||||
try {
|
||||
inetAddresses = future.get();
|
||||
log.info("DNS seed {}: got {} peers", dnsSeeds[i], inetAddresses.length);
|
||||
} catch (ExecutionException e) {
|
||||
log.error("DNS seed {}: failed to look up: {}", dnsSeeds[i], e.getMessage());
|
||||
continue;
|
||||
}
|
||||
for (InetAddress addr : inetAddresses) {
|
||||
addrs.add(new InetSocketAddress(addr, netParams.getPort()));
|
||||
}
|
||||
}
|
||||
if (addrs.size() == 0)
|
||||
throw new PeerDiscoveryException("Unable to find any peers via DNS");
|
||||
Collections.shuffle(addrs);
|
||||
threadPool.shutdownNow();
|
||||
return addrs.toArray(new InetSocketAddress[addrs.size()]);
|
||||
} catch (InterruptedException e) {
|
||||
throw new PeerDiscoveryException(e);
|
||||
} finally {
|
||||
threadPool.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
/** We don't have a way to abort a DNS lookup, so this does nothing */
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright 2014 Mike Hearn
|
||||
*
|
||||
* 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.dogecoin.dogecoinj.net.discovery;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import org.bitcoin.crawler.PeerSeedProtos;
|
||||
import com.dogecoin.dogecoinj.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 static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
* A class that knows how to read signed sets of seeds over HTTP, using a simple protobuf based protocol. See the
|
||||
* peerseeds.proto file for the definition, with a gzipped delimited SignedPeerSeeds being the root of the data.
|
||||
* This is not currently in use by the Bitcoin community, but rather, is here for experimentation.
|
||||
*/
|
||||
public class HttpDiscovery implements PeerDiscovery {
|
||||
private static final int TIMEOUT_SECS = 20;
|
||||
|
||||
private final ECKey pubkey;
|
||||
private final URI uri;
|
||||
private final NetworkParameters params;
|
||||
|
||||
/**
|
||||
* 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 = params;
|
||||
}
|
||||
|
||||
@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", "dogecoinj " + VersionMessage.BITCOINJ_VERSION);
|
||||
InputStream stream = conn.getInputStream();
|
||||
GZIPInputStream zip = new GZIPInputStream(stream);
|
||||
PeerSeedProtos.SignedPeerSeeds proto = PeerSeedProtos.SignedPeerSeeds.parseDelimitedFrom(zip);
|
||||
stream.close();
|
||||
return protoToAddrs(proto);
|
||||
} catch (Exception e) {
|
||||
throw new PeerDiscoveryException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public InetSocketAddress[] protoToAddrs(PeerSeedProtos.SignedPeerSeeds proto) throws PeerDiscoveryException, InvalidProtocolBufferException, SignatureException {
|
||||
if (pubkey != null) {
|
||||
if (!Arrays.equals(proto.getPubkey().toByteArray(), pubkey.getPubKey()))
|
||||
throw new PeerDiscoveryException("Public key mismatch");
|
||||
Sha256Hash hash = Sha256Hash.create(proto.getPeerSeeds().toByteArray());
|
||||
pubkey.verifyOrThrow(hash.getBytes(), proto.getSignature().toByteArray());
|
||||
}
|
||||
PeerSeedProtos.PeerSeeds seeds = PeerSeedProtos.PeerSeeds.parseFrom(proto.getPeerSeeds());
|
||||
if (seeds.getTimestamp() < Utils.currentTimeSeconds() - (60 * 60 * 24))
|
||||
throw new PeerDiscoveryException("Seed data is more than one day old: replay attack?");
|
||||
if (!seeds.getNet().equals(params.getPaymentProtocolId()))
|
||||
throw new PeerDiscoveryException("Network mismatch");
|
||||
InetSocketAddress[] results = new InetSocketAddress[seeds.getSeedCount()];
|
||||
int i = 0;
|
||||
for (PeerSeedProtos.PeerSeedData data : seeds.getSeedList())
|
||||
results[i++] = new InetSocketAddress(data.getIpAddress(), data.getPort());
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright 2014 Mike Hearn
|
||||
*
|
||||
* 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.dogecoin.dogecoinj.net.discovery;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.dogecoin.dogecoinj.core.NetworkParameters;
|
||||
import com.dogecoin.dogecoinj.utils.DaemonThreadFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
* MultiplexingDiscovery queries multiple PeerDiscovery objects, shuffles their responses and then returns the results,
|
||||
* thus selecting randomly between them and reducing the influence of any particular seed. Any that don't respond
|
||||
* within the timeout are ignored. Backends are queried in parallel. Backends may block.
|
||||
*/
|
||||
public class MultiplexingDiscovery implements PeerDiscovery {
|
||||
private static final Logger log = LoggerFactory.getLogger(MultiplexingDiscovery.class);
|
||||
|
||||
protected final List<PeerDiscovery> seeds;
|
||||
protected final NetworkParameters netParams;
|
||||
private volatile ExecutorService vThreadPool;
|
||||
|
||||
/**
|
||||
* Will query the given seeds in parallel before producing a merged response.
|
||||
*/
|
||||
public MultiplexingDiscovery(NetworkParameters params, List<PeerDiscovery> seeds) {
|
||||
checkArgument(!seeds.isEmpty());
|
||||
this.netParams = params;
|
||||
this.seeds = seeds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress[] getPeers(final long timeoutValue, final TimeUnit timeoutUnit) throws PeerDiscoveryException {
|
||||
vThreadPool = createExecutor();
|
||||
try {
|
||||
List<Callable<InetSocketAddress[]>> tasks = Lists.newArrayList();
|
||||
for (final PeerDiscovery seed : seeds) {
|
||||
tasks.add(new Callable<InetSocketAddress[]>() {
|
||||
@Override
|
||||
public InetSocketAddress[] call() throws Exception {
|
||||
return seed.getPeers(timeoutValue, timeoutUnit);
|
||||
}
|
||||
});
|
||||
}
|
||||
final List<Future<InetSocketAddress[]>> futures = vThreadPool.invokeAll(tasks, timeoutValue, timeoutUnit);
|
||||
ArrayList<InetSocketAddress> addrs = Lists.newArrayList();
|
||||
for (int i = 0; i < futures.size(); i++) {
|
||||
Future<InetSocketAddress[]> future = futures.get(i);
|
||||
if (future.isCancelled()) {
|
||||
log.warn("Seed {}: timed out", seeds.get(i));
|
||||
continue; // Timed out.
|
||||
}
|
||||
final InetSocketAddress[] inetAddresses;
|
||||
try {
|
||||
inetAddresses = future.get();
|
||||
} catch (ExecutionException e) {
|
||||
log.warn("Seed {}: failed to look up: {}", seeds.get(i), e.getMessage());
|
||||
continue;
|
||||
}
|
||||
Collections.addAll(addrs, inetAddresses);
|
||||
}
|
||||
if (addrs.size() == 0)
|
||||
throw new PeerDiscoveryException("No peer discovery returned any results: check internet connection?");
|
||||
Collections.shuffle(addrs);
|
||||
vThreadPool.shutdownNow();
|
||||
return addrs.toArray(new InetSocketAddress[addrs.size()]);
|
||||
} catch (InterruptedException e) {
|
||||
throw new PeerDiscoveryException(e);
|
||||
} finally {
|
||||
vThreadPool.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
protected ExecutorService createExecutor() {
|
||||
return Executors.newFixedThreadPool(seeds.size(), new DaemonThreadFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
ExecutorService tp = vThreadPool;
|
||||
if (tp != null)
|
||||
tp.shutdown();
|
||||
}
|
||||
}
|
@@ -68,6 +68,6 @@ public class RegTestParams extends TestNet2Params {
|
||||
|
||||
@Override
|
||||
public String getPaymentProtocolId() {
|
||||
return null;
|
||||
return PAYMENT_PROTOCOL_ID_REGTEST;
|
||||
}
|
||||
}
|
||||
|
@@ -58,6 +58,6 @@ public class UnitTestParams extends NetworkParameters {
|
||||
|
||||
@Override
|
||||
public String getPaymentProtocolId() {
|
||||
return null;
|
||||
return "unittest";
|
||||
}
|
||||
}
|
||||
|
@@ -17,26 +17,19 @@
|
||||
package com.dogecoin.dogecoinj.store;
|
||||
|
||||
import com.dogecoin.dogecoinj.core.*;
|
||||
import com.dogecoin.dogecoinj.utils.Threading;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.dogecoin.dogecoinj.utils.*;
|
||||
import org.slf4j.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import javax.annotation.*;
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.*;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
// TODO: Lose the mmap in this class. There are too many platform bugs that require odd workarounds.
|
||||
|
||||
/**
|
||||
* An SPVBlockStore holds a limited number of block headers in a memory mapped ring buffer. With such a store, you
|
||||
@@ -269,6 +262,10 @@ public class SPVBlockStore implements BlockStore {
|
||||
public void close() throws BlockStoreException {
|
||||
try {
|
||||
buffer.force();
|
||||
if (System.getProperty("os.name").toLowerCase().contains("win")) {
|
||||
log.info("Windows mmap hack: Forcing buffer cleaning");
|
||||
WindowsMMapHack.forceRelease(buffer);
|
||||
}
|
||||
buffer = null; // Allow it to be GCd and the underlying file mapping to go away.
|
||||
randomAccessFile.close();
|
||||
} catch (IOException e) {
|
||||
|
@@ -0,0 +1,23 @@
|
||||
package com.dogecoin.dogecoinj.store;
|
||||
|
||||
import sun.misc.*;
|
||||
import sun.nio.ch.*;
|
||||
|
||||
import java.nio.*;
|
||||
|
||||
/**
|
||||
* <p>This class knows how to force an mmap'd ByteBuffer to reliquish its file handles before it becomes garbage collected,
|
||||
* by exploiting implementation details of the HotSpot JVM implementation.</p>
|
||||
*
|
||||
* <p>This is required on Windows because otherwise an attempt to delete a file that is still mmapped will fail. This can
|
||||
* happen when a user requests a "restore from seed" function, which involves deleting and recreating the chain file.
|
||||
* At some point we should stop using mmap in SPVBlockStore and we can then delete this class.</p>
|
||||
*
|
||||
* <p>It is a separate class to avoid hitting unknown imports when running on other JVMs.</p>
|
||||
*/
|
||||
public class WindowsMMapHack {
|
||||
public static void forceRelease(MappedByteBuffer buffer) {
|
||||
Cleaner cleaner = ((DirectBuffer) buffer).cleaner();
|
||||
if (cleaner != null) cleaner.clean();
|
||||
}
|
||||
}
|
@@ -49,6 +49,11 @@ public class BriefLogFormatter extends Formatter {
|
||||
logger.log(Level.FINE, "test");
|
||||
}
|
||||
|
||||
public static void initWithSilentBitcoinJ() {
|
||||
init();
|
||||
Logger.getLogger("org.bitcoinj").setLevel(Level.SEVERE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(LogRecord logRecord) {
|
||||
Object[] arguments = new Object[6];
|
||||
|
@@ -116,7 +116,7 @@ public class Threading {
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
final int size = tasks.size();
|
||||
if (size > WARNING_THRESHOLD) {
|
||||
if (size == WARNING_THRESHOLD) {
|
||||
log.warn(
|
||||
"User thread has {} pending tasks, memory exhaustion may occur.\n" +
|
||||
"If you see this message, check your memory consumption and see if it's problematic or excessively spikey.\n" +
|
||||
|
@@ -162,6 +162,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||
protected long seedCreationTimeSecs;
|
||||
protected byte[] entropy;
|
||||
protected DeterministicSeed seed;
|
||||
protected DeterministicKey watchingKey;
|
||||
|
||||
protected Builder() {
|
||||
}
|
||||
@@ -214,6 +215,16 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||
return self();
|
||||
}
|
||||
|
||||
public T watchingKey(DeterministicKey watchingKey) {
|
||||
this.watchingKey = watchingKey;
|
||||
return self();
|
||||
}
|
||||
|
||||
public T seedCreationTimeSecs(long seedCreationTimeSecs) {
|
||||
this.seedCreationTimeSecs = seedCreationTimeSecs;
|
||||
return self();
|
||||
}
|
||||
|
||||
/** The passphrase to use with the generated mnemonic, or null if you would like to use the default empty string. Currently must be the empty string. */
|
||||
public T passphrase(String passphrase) {
|
||||
// FIXME support non-empty passphrase
|
||||
@@ -223,7 +234,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||
|
||||
|
||||
public DeterministicKeyChain build() {
|
||||
checkState(random != null || entropy != null || seed != null, "Must provide either entropy or random");
|
||||
checkState(random != null || entropy != null || seed != null || watchingKey!= null, "Must provide either entropy or random or seed or watchingKey");
|
||||
checkState(passphrase == null || seed == null, "Passphrase must not be specified with seed");
|
||||
DeterministicKeyChain chain;
|
||||
|
||||
@@ -232,8 +243,10 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||
chain = new DeterministicKeyChain(random, bits, getPassphrase(), seedCreationTimeSecs);
|
||||
} else if (entropy != null) {
|
||||
chain = new DeterministicKeyChain(entropy, getPassphrase(), seedCreationTimeSecs);
|
||||
} else {
|
||||
} else if (seed != null) {
|
||||
chain = new DeterministicKeyChain(seed);
|
||||
} else {
|
||||
chain = new DeterministicKeyChain(watchingKey, seedCreationTimeSecs);
|
||||
}
|
||||
|
||||
return chain;
|
||||
|
@@ -91,7 +91,7 @@ public class MarriedKeyChain extends DeterministicKeyChain {
|
||||
}
|
||||
|
||||
public MarriedKeyChain build() {
|
||||
checkState(random != null || entropy != null || seed != null, "Must provide either entropy or random");
|
||||
checkState(random != null || entropy != null || seed != null || watchingKey!= null, "Must provide either entropy or random or seed or watchingKey");
|
||||
checkNotNull(followingKeys, "followingKeys must be provided");
|
||||
MarriedKeyChain chain;
|
||||
if (threshold == 0)
|
||||
@@ -100,8 +100,10 @@ public class MarriedKeyChain extends DeterministicKeyChain {
|
||||
chain = new MarriedKeyChain(random, bits, getPassphrase(), seedCreationTimeSecs);
|
||||
} else if (entropy != null) {
|
||||
chain = new MarriedKeyChain(entropy, getPassphrase(), seedCreationTimeSecs);
|
||||
} else {
|
||||
} else if (seed != null) {
|
||||
chain = new MarriedKeyChain(seed);
|
||||
} else {
|
||||
chain = new MarriedKeyChain(watchingKey, seedCreationTimeSecs);
|
||||
}
|
||||
chain.addFollowingAccountKeys(followingKeys, threshold);
|
||||
return chain;
|
||||
@@ -117,6 +119,10 @@ public class MarriedKeyChain extends DeterministicKeyChain {
|
||||
super(accountKey, false);
|
||||
}
|
||||
|
||||
MarriedKeyChain(DeterministicKey accountKey, long seedCreationTimeSecs) {
|
||||
super(accountKey, seedCreationTimeSecs);
|
||||
}
|
||||
|
||||
MarriedKeyChain(DeterministicSeed seed, KeyCrypter crypter) {
|
||||
super(seed, crypter);
|
||||
}
|
||||
|
Binary file not shown.
@@ -168,4 +168,61 @@ public class AddressTest {
|
||||
Address address = Address.fromP2SHScript(mainParams, p2shScript);
|
||||
assertEquals("ACdJj7YT7dJkV6bv6cRenUMCTDQxSdZSo5", address.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cloning() throws Exception {
|
||||
Address a = new Address(testParams, HEX.decode("fda79a24e50ff70ff42f7d89585da5bd19d9e5cc"));
|
||||
Address b = a.clone();
|
||||
|
||||
assertEquals(a, b);
|
||||
assertNotSame(a, b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void comparisonCloneEqualTo() throws Exception {
|
||||
Address a = new Address(mainParams, "1Dorian4RoXcnBv9hnQ4Y2C1an6NJ4UrjX");
|
||||
Address b = a.clone();
|
||||
|
||||
int result = a.compareTo(b);
|
||||
assertEquals(0, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void comparisonEqualTo() throws Exception {
|
||||
Address a = new Address(mainParams, "1Dorian4RoXcnBv9hnQ4Y2C1an6NJ4UrjX");
|
||||
Address b = a.clone();
|
||||
|
||||
int result = a.compareTo(b);
|
||||
assertEquals(0, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void comparisonLessThan() throws Exception {
|
||||
Address a = new Address(mainParams, "1Dorian4RoXcnBv9hnQ4Y2C1an6NJ4UrjX");
|
||||
Address b = new Address(mainParams, "1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P");
|
||||
|
||||
int result = a.compareTo(b);
|
||||
assertTrue(result < 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void comparisonGreaterThan() throws Exception {
|
||||
Address a = new Address(mainParams, "1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P");
|
||||
Address b = new Address(mainParams, "1Dorian4RoXcnBv9hnQ4Y2C1an6NJ4UrjX");
|
||||
|
||||
int result = a.compareTo(b);
|
||||
assertTrue(result > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void comparisonBytesVsString() throws Exception {
|
||||
// TODO: To properly test this we need a much larger data set
|
||||
Address a = new Address(mainParams, "1Dorian4RoXcnBv9hnQ4Y2C1an6NJ4UrjX");
|
||||
Address b = new Address(mainParams, "1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P");
|
||||
|
||||
int resultBytes = a.compareTo(b);
|
||||
int resultsString = a.toString().compareTo(b.toString());
|
||||
assertTrue( resultBytes < 0 );
|
||||
assertTrue( resultsString < 0 );
|
||||
}
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -38,4 +39,15 @@ public class DumpedPrivateKeyTest {
|
||||
.readObject();
|
||||
assertEquals(key, keyCopy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cloning() throws Exception {
|
||||
DumpedPrivateKey a = new DumpedPrivateKey(MainNetParams.get(), new ECKey().getPrivKeyBytes(), true);
|
||||
// TODO: Consider overriding clone() in DumpedPrivateKey to narrow the type
|
||||
DumpedPrivateKey b = (DumpedPrivateKey) a.clone();
|
||||
|
||||
assertEquals(a, b);
|
||||
assertNotSame(a, b);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -239,7 +239,7 @@ public class PeerGroupTest extends TestWithPeerGroup {
|
||||
|
||||
|
||||
@Test
|
||||
public void receiveTxBroadcastOnAddeweldWallet() throws Exception {
|
||||
public void receiveTxBroadcastOnAddedWallet() throws Exception {
|
||||
// Check that when we receive transactions on all our peers, we do the right thing.
|
||||
peerGroup.start();
|
||||
|
||||
@@ -698,6 +698,31 @@ public class PeerGroupTest extends TestWithPeerGroup {
|
||||
assertTrue(future.isDone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void waitForPeersWithServiceFlags() throws Exception {
|
||||
ListenableFuture<List<Peer>> future = peerGroup.waitForPeersWithServiceMask(2, 3);
|
||||
|
||||
VersionMessage ver1 = new VersionMessage(params, 10);
|
||||
ver1.clientVersion = 70000;
|
||||
ver1.localServices = VersionMessage.NODE_NETWORK;
|
||||
VersionMessage ver2 = new VersionMessage(params, 10);
|
||||
ver2.clientVersion = 70000;
|
||||
ver2.localServices = VersionMessage.NODE_NETWORK | 2;
|
||||
peerGroup.start();
|
||||
assertFalse(future.isDone());
|
||||
connectPeer(1, ver1);
|
||||
assertTrue(peerGroup.findPeersWithServiceMask(3).isEmpty());
|
||||
assertFalse(future.isDone());
|
||||
connectPeer(2, ver2);
|
||||
assertFalse(future.isDone());
|
||||
assertEquals(1, peerGroup.findPeersWithServiceMask(3).size());
|
||||
assertTrue(peerGroup.waitForPeersWithServiceMask(1, 0x3).isDone()); // Immediate completion.
|
||||
connectPeer(3, ver2);
|
||||
future.get();
|
||||
assertTrue(future.isDone());
|
||||
peerGroup.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preferLocalPeer() throws IOException {
|
||||
// Because we are using the same port (8333 or 18333) that is used by Satoshi client
|
||||
|
@@ -32,6 +32,7 @@ import org.junit.runners.Parameterized;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static com.dogecoin.dogecoinj.core.Coin.*;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@@ -97,6 +98,27 @@ public class TransactionBroadcastTest extends TestWithPeerGroup {
|
||||
assertTrue(future.isDone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectHandling() throws Exception {
|
||||
InboundMessageQueuer[] channels = { connectPeer(0), connectPeer(1), connectPeer(2), connectPeer(3), connectPeer(4) };
|
||||
Transaction tx = new Transaction(params);
|
||||
TransactionBroadcast broadcast = new TransactionBroadcast(peerGroup, blockChain.getContext(), tx);
|
||||
ListenableFuture<Transaction> future = broadcast.broadcast();
|
||||
// 0 and 3 are randomly selected to receive the broadcast.
|
||||
assertEquals(tx, outbound(channels[1]));
|
||||
assertEquals(tx, outbound(channels[2]));
|
||||
assertEquals(tx, outbound(channels[4]));
|
||||
RejectMessage reject = new RejectMessage(params, RejectMessage.RejectCode.DUST, tx.getHash(), "tx", "dust");
|
||||
inbound(channels[1], reject);
|
||||
inbound(channels[4], reject);
|
||||
try {
|
||||
future.get();
|
||||
fail();
|
||||
} catch (ExecutionException e) {
|
||||
assertEquals(RejectedTransactionException.class, e.getCause().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retryFailedBroadcast() throws Exception {
|
||||
// If we create a spend, it's sent to a peer that swallows it, and the peergroup is removed/re-added then
|
||||
|
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright 2014 BitcoinJ Project
|
||||
*
|
||||
* 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.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.params.MainNetParams;
|
||||
import com.dogecoin.dogecoinj.params.TestNet3Params;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.dogecoin.dogecoinj.core.Utils.HEX;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class VersionedChecksummedBytesTest {
|
||||
static final NetworkParameters testParams = TestNet3Params.get();
|
||||
static final NetworkParameters mainParams = MainNetParams.get();
|
||||
|
||||
@Test
|
||||
public void stringification() throws Exception {
|
||||
// Test a testnet address.
|
||||
VersionedChecksummedBytes a = new VersionedChecksummedBytes(testParams.getAddressHeader(), HEX.decode("fda79a24e50ff70ff42f7d89585da5bd19d9e5cc"));
|
||||
assertEquals("n4eA2nbYqErp7H6jebchxAN59DmNpksexv", a.toString());
|
||||
|
||||
VersionedChecksummedBytes b = new VersionedChecksummedBytes(mainParams.getAddressHeader(), HEX.decode("4a22c3c4cbb31e4d03b15550636762bda0baf85a"));
|
||||
assertEquals("17kzeh4N8g49GFvdDzSf8PjaPfyoD1MndL", b.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cloning() throws Exception {
|
||||
VersionedChecksummedBytes a = new VersionedChecksummedBytes(testParams.getAddressHeader(), HEX.decode("fda79a24e50ff70ff42f7d89585da5bd19d9e5cc"));
|
||||
VersionedChecksummedBytes b = a.clone();
|
||||
|
||||
assertEquals(a, b);
|
||||
assertNotSame(a, b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void comparisonCloneEqualTo() throws Exception {
|
||||
VersionedChecksummedBytes a = new VersionedChecksummedBytes(testParams.getAddressHeader(), HEX.decode("fda79a24e50ff70ff42f7d89585da5bd19d9e5cc"));
|
||||
VersionedChecksummedBytes b = a.clone();
|
||||
|
||||
assertTrue(a.compareTo(b) == 0);
|
||||
}
|
||||
}
|
@@ -3092,7 +3092,7 @@ public class WalletTest extends TestWithWallet {
|
||||
|
||||
@Test(expected = java.lang.IllegalStateException.class)
|
||||
public void sendCoinsNoBroadcasterTest() throws InsufficientMoneyException {
|
||||
ECKey key = ECKey.fromPrivate(BigInteger.ONE);
|
||||
ECKey key = ECKey.fromPrivate(BigInteger.TEN);
|
||||
Address notMyAddr = key.toAddress(params);
|
||||
SendRequest req = SendRequest.to(notMyAddr.getParameters(), key, SATOSHI.multiply(12));
|
||||
wallet.sendCoins(req);
|
||||
@@ -3100,7 +3100,7 @@ public class WalletTest extends TestWithWallet {
|
||||
|
||||
@Test
|
||||
public void sendCoinsWithBroadcasterTest() throws InsufficientMoneyException {
|
||||
ECKey key = ECKey.fromPrivate(BigInteger.ONE);
|
||||
ECKey key = ECKey.fromPrivate(BigInteger.TEN);
|
||||
Address notMyAddr = key.toAddress(params);
|
||||
receiveATransactionAmount(wallet, myAddress, Coin.COIN);
|
||||
MockTransactionBroadcaster broadcaster = new MockTransactionBroadcaster(wallet);
|
||||
|
@@ -28,6 +28,7 @@ import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
|
||||
public class BIP38PrivateKeyTest {
|
||||
|
||||
@@ -157,4 +158,15 @@ public class BIP38PrivateKeyTest {
|
||||
.readObject();
|
||||
assertEquals(key, keyCopy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cloning() throws Exception {
|
||||
BIP38PrivateKey a = new BIP38PrivateKey(TESTNET, "6PfMmVHn153N3x83Yiy4Nf76dHUkXufe2Adr9Fw5bewrunGNeaw2QCpifb");
|
||||
// TODO: Consider overriding clone() in BIP38PrivateKey to narrow the type
|
||||
BIP38PrivateKey b = (BIP38PrivateKey) a.clone();
|
||||
|
||||
assertEquals(a, b);
|
||||
assertNotSame(a, b);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -52,7 +52,7 @@ public class BuildCheckpoints {
|
||||
private static final File TEXTUAL_CHECKPOINTS_FILE = new File("checkpoints.txt");
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
BriefLogFormatter.init();
|
||||
BriefLogFormatter.initWithSilentBitcoinJ();
|
||||
|
||||
// Sorted map of block height to StoredBlock object.
|
||||
final TreeMap<Integer, StoredBlock> checkpoints = new TreeMap<Integer, StoredBlock>();
|
||||
|
@@ -166,6 +166,7 @@ public class WalletTool {
|
||||
ADD_KEY,
|
||||
ADD_ADDR,
|
||||
DELETE_KEY,
|
||||
CURRENT_RECEIVE_ADDR,
|
||||
SYNC,
|
||||
RESET,
|
||||
SEND,
|
||||
@@ -342,6 +343,7 @@ public class WalletTool {
|
||||
case ADD_KEY: addKey(); break;
|
||||
case ADD_ADDR: addAddr(); break;
|
||||
case DELETE_KEY: deleteKey(); break;
|
||||
case CURRENT_RECEIVE_ADDR: currentReceiveAddr(); break;
|
||||
case RESET: reset(); break;
|
||||
case SYNC: syncChain(); break;
|
||||
case SEND:
|
||||
@@ -1074,6 +1076,11 @@ public class WalletTool {
|
||||
wallet.removeKey(key);
|
||||
}
|
||||
|
||||
private static void currentReceiveAddr() {
|
||||
ECKey key = wallet.currentReceiveKey();
|
||||
System.out.println(key.toAddress(params) + " " + key);
|
||||
}
|
||||
|
||||
private static void dumpWallet() throws BlockStoreException {
|
||||
// Setup to get the chain height so we can estimate lock times, but don't wipe the transactions if it's not
|
||||
// there just for the dump case.
|
||||
|
@@ -21,6 +21,8 @@ Usage: wallet-tool --flags action-name
|
||||
If --lookahead-size is specified, pre-generate at least this many keys ahead of where we are.
|
||||
add-addr Requires --addr to be specified, and adds it as a watching address.
|
||||
delete-key Removes the key specified by --pubkey or --addr from the wallet.
|
||||
current-receive-addr Prints the current receive address, deriving one if needed. Addresses derived with this action are
|
||||
independent of addresses derived with the add-key action.
|
||||
sync Sync the wallet with the latest block chain (download new transactions).
|
||||
If the chain file does not exist this will RESET the wallet.
|
||||
reset Deletes all transactions from the wallet, for if you want to replay the chain.
|
||||
|
@@ -1,10 +1,9 @@
|
||||
package wallettemplate;
|
||||
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.dogecoin.dogecoinj.core.NetworkParameters;
|
||||
import com.dogecoin.dogecoinj.kits.WalletAppKit;
|
||||
import com.dogecoin.dogecoinj.params.MainNetParams;
|
||||
import com.dogecoin.dogecoinj.params.RegTestParams;
|
||||
import com.dogecoin.dogecoinj.params.TestNet3Params;
|
||||
import com.dogecoin.dogecoinj.params.*;
|
||||
import com.dogecoin.dogecoinj.utils.BriefLogFormatter;
|
||||
import com.dogecoin.dogecoinj.utils.Threading;
|
||||
import com.dogecoin.dogecoinj.wallet.DeterministicSeed;
|
||||
@@ -30,7 +29,7 @@ import static wallettemplate.utils.GuiUtils.*;
|
||||
public class Main extends Application {
|
||||
public static String APP_NAME = "WalletTemplate";
|
||||
|
||||
public static NetworkParameters params = TestNet3Params.get();
|
||||
public static NetworkParameters params = MainNetParams.get();
|
||||
public static WalletAppKit bitcoin;
|
||||
public static Main instance;
|
||||
|
||||
@@ -96,20 +95,23 @@ public class Main extends Application {
|
||||
|
||||
mainWindow.show();
|
||||
|
||||
bitcoin.addListener(new Service.Listener() {
|
||||
@Override
|
||||
public void failed(Service.State from, Throwable failure) {
|
||||
GuiUtils.crashAlert(failure);
|
||||
}
|
||||
}, Platform::runLater);
|
||||
bitcoin.startAsync();
|
||||
}
|
||||
|
||||
public void setupWalletKit(@Nullable DeterministicSeed seed) {
|
||||
// If seed is non-null it means we are restoring from backup.
|
||||
bitcoin = new WalletAppKit(params, new File("."), APP_NAME) {
|
||||
bitcoin = new WalletAppKit(params, new File("."), APP_NAME + "-" + params.getPaymentProtocolId()) {
|
||||
@Override
|
||||
protected void onSetupCompleted() {
|
||||
// Don't make the user wait for confirmations for now, as the intention is they're sending it
|
||||
// their own money!
|
||||
bitcoin.wallet().allowSpendingUnconfirmedTransactions();
|
||||
if (params != RegTestParams.get())
|
||||
bitcoin.peerGroup().setMaxConnections(11);
|
||||
bitcoin.peerGroup().setBloomFilterFalsePositiveRate(0.00001);
|
||||
Platform.runLater(controller::onBitcoinSetup);
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user