mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-07-31 20:11:23 +00:00
Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2733004e71 | ||
|
34dcef91a9 | ||
|
b8708c4680 | ||
|
eceae231e8 | ||
|
30cc5c6a12 | ||
|
4faa8b486c | ||
|
ea8e122cef | ||
|
1bc77f843b | ||
|
60b0d917e7 | ||
|
95009f0310 | ||
|
86e63fb048 | ||
|
f399dafefc | ||
|
d5ffe88cff | ||
|
635002ce11 | ||
|
1e58cdc4cf | ||
|
4eb792c74d | ||
|
cae096295f | ||
|
de58cb0444 | ||
|
1a15ae2f52 | ||
|
25c332a71b | ||
|
e5677c2f53 | ||
|
9010b9a411 | ||
|
74cf34db82 | ||
|
16aeea9104 | ||
|
8fc63e482e | ||
|
9592f25ba1 | ||
|
afff0f701d | ||
|
38e103feea | ||
|
b3e6a23f0e | ||
|
fd6ceac30c | ||
|
112f6ab624 | ||
|
60199f810a | ||
|
95abd140f0 | ||
|
e783895dd3 | ||
|
cc89cd0d16 | ||
|
28a5d8abbc | ||
|
3ffcce49f9 | ||
|
f581a53aa0 | ||
|
a5bc2f3794 | ||
|
5543b166cc | ||
|
3755835d11 | ||
|
0ee3dca7a0 | ||
|
1250aa8e3d | ||
|
410d4547a7 | ||
|
71391c1271 |
7
README
7
README
@@ -11,10 +11,13 @@ find your unzipped Maven install directory.
|
||||
Now try running one of the example apps:
|
||||
|
||||
cd examples
|
||||
mvn exec:java -Dexec.mainClass=com.google.bitcoin.examples.ForwardingService <insert a bitcoin address here>
|
||||
mvn exec:java -Dexec.mainClass=com.google.bitcoin.examples.ForwardingService -Dexec.args="<insert a bitcoin address here>"
|
||||
|
||||
It will download the block chain and eventually print a Bitcoin address. If you send coins to it,
|
||||
it will forward them on to the address you specified.
|
||||
it will forward them on to the address you specified. Note that this example app does not use
|
||||
checkpointing, so the initial chain sync will be pretty slow. You can make an app that starts up and
|
||||
does the initial sync much faster by including a checkpoints file; see the documentation for
|
||||
more info on this.
|
||||
|
||||
Now you are ready to follow the tutorial:
|
||||
|
||||
|
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<groupId>com.google</groupId>
|
||||
<artifactId>bitcoinj-parent</artifactId>
|
||||
<version>0.11-SNAPSHOT</version>
|
||||
<version>0.11.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>bitcoinj</artifactId>
|
||||
@@ -152,12 +152,11 @@
|
||||
<urns>
|
||||
<urn>com.google.code.findbugs:jsr305:1.3.9:jar:null:compile:40719ea6961c0cb6afaeb6a921eaa1f6afd4cfdf</urn>
|
||||
<urn>com.google.guava:guava:13.0.1:jar:null:compile:0d6f22b1e60a2f1ef99e22c9f5fde270b2088365</urn>
|
||||
<urn>com.google.protobuf:protobuf-java:2.4.1:jar:null:compile:0c589509ec6fd86d5d2fda37e07c08538235d3b9</urn>
|
||||
<urn>com.google.protobuf:protobuf-java:2.5.0:jar:null:compile:a10732c76bfacdbd633a7eb0f7968b1059a65dfa</urn>
|
||||
<urn>com.h2database:h2:1.3.167:jar:null:compile:d3867d586f087e53eb12fc65e5693d8ee9a5da17</urn>
|
||||
<urn>com.lambdaworks:scrypt:1.3.3:jar:null:compile:06d6813de41e177189e1722717979b4fb5454b1d</urn>
|
||||
<urn>com.madgag:sc-light-jdk15on:1.47.0.2:jar:null:compile:d5c98671cc97fa0d928be1c7eb5edd3fb95d3234</urn>
|
||||
<urn>net.jcip:jcip-annotations:1.0:jar:null:compile:afba4942caaeaf46aab0b976afd57cc7c181467e</urn>
|
||||
<urn>net.sf.jopt-simple:jopt-simple:4.3:jar:null:compile:88ffca34311a6564a98f14820431e17b4382a069</urn>
|
||||
<urn>org.slf4j:slf4j-api:1.7.5:jar:null:compile:6b262da268f8ad9eff941b25503a9198f0a0ac93</urn>
|
||||
<urn>org.slf4j:slf4j-jdk14:1.7.5:jar:null:runtime:33cf4abac351aa45dd130d31a1e7e33fbbba4762</urn>
|
||||
<!-- A check for the rules themselves -->
|
||||
|
@@ -106,7 +106,7 @@ public class Address extends VersionedChecksummedBytes {
|
||||
|
||||
/*
|
||||
* Returns true if this address is a Pay-To-Script-Hash (P2SH) address.
|
||||
* See also https://en.bitcoin.it/wiki/BIP_0013: Address Format for pay-to-script-hash
|
||||
* See also https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki: Address Format for pay-to-script-hash
|
||||
*/
|
||||
public boolean isP2SHAddress() {
|
||||
final NetworkParameters parameters = getParameters();
|
||||
|
@@ -293,16 +293,15 @@ public class BitcoinSerializer {
|
||||
|
||||
// The command is a NULL terminated string, unless the command fills all twelve bytes
|
||||
// in which case the termination is implicit.
|
||||
int mark = cursor;
|
||||
for (; header[cursor] != 0 && cursor - mark < COMMAND_LEN; cursor++) ;
|
||||
byte[] commandBytes = new byte[cursor - mark];
|
||||
System.arraycopy(header, mark, commandBytes, 0, cursor - mark);
|
||||
for (; header[cursor] != 0 && cursor < COMMAND_LEN; cursor++) ;
|
||||
byte[] commandBytes = new byte[cursor];
|
||||
System.arraycopy(header, 0, commandBytes, 0, cursor);
|
||||
try {
|
||||
command = new String(commandBytes, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
cursor = mark + COMMAND_LEN;
|
||||
cursor = COMMAND_LEN;
|
||||
|
||||
size = (int) readUint32(header, cursor);
|
||||
cursor += 4;
|
||||
|
@@ -70,25 +70,20 @@ public class BloomFilter extends Message {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Constructs a new Bloom Filter which will provide approximately the given false positive
|
||||
* rate when the given number of elements have been inserted.</p>
|
||||
* <p>Constructs a new Bloom Filter which will provide approximately the given false positive rate when the given
|
||||
* number of elements have been inserted. If the filter would otherwise be larger than the maximum allowed size,
|
||||
* it will be automatically downsized to the maximum size.</p>
|
||||
*
|
||||
* <p>If the filter would otherwise be larger than the maximum allowed size, it will be
|
||||
* automatically downsized to the maximum size.</p>
|
||||
* <p>To check the theoretical false positive rate of a given filter, use
|
||||
* {@link BloomFilter#getFalsePositiveRate(int)}.</p>
|
||||
*
|
||||
* <p>To check the theoretical false positive rate of a given filter, use {@link BloomFilter#getFalsePositiveRate(int)}</p>
|
||||
*
|
||||
* <p>The anonymity of which coins are yours to any peer which you send a BloomFilter to is
|
||||
* controlled by the false positive rate.</p>
|
||||
*
|
||||
* <p>For reference, as of block 187,000, the total number of addresses used in the chain was roughly 4.5 million.</p>
|
||||
*
|
||||
* <p>Thus, if you use a false positive rate of 0.001 (0.1%), there will be, on average, 4,500 distinct public
|
||||
* keys/addresses which will be thought to be yours by nodes which have your bloom filter, but which are not
|
||||
* actually yours.</p>
|
||||
*
|
||||
* <p>Keep in mind that a remote node can do a pretty good job estimating the order of magnitude of the false positive
|
||||
* rate of a given filter you provide it when considering the anonymity of a given filter.</p>
|
||||
* <p>The anonymity of which coins are yours to any peer which you send a BloomFilter to is controlled by the
|
||||
* false positive rate. For reference, as of block 187,000, the total number of addresses used in the chain was
|
||||
* roughly 4.5 million. Thus, if you use a false positive rate of 0.001 (0.1%), there will be, on average, 4,500
|
||||
* distinct public keys/addresses which will be thought to be yours by nodes which have your bloom filter, but
|
||||
* which are not actually yours. Keep in mind that a remote node can do a pretty good job estimating the order of
|
||||
* magnitude of the false positive rate of a given filter you provide it when considering the anonymity of a given
|
||||
* filter.</p>
|
||||
*
|
||||
* <p>In order for filtered block download to function efficiently, the number of matched transactions in any given
|
||||
* block should be less than (with some headroom) the maximum size of the MemoryPool used by the Peer
|
||||
@@ -98,7 +93,10 @@ public class BloomFilter extends Message {
|
||||
* <p>randomNonce is a tweak for the hash function used to prevent some theoretical DoS attacks.
|
||||
* It should be a random value, however secureness of the random value is of no great consequence.</p>
|
||||
*
|
||||
* <p>updateFlag is used to control filter behavior</p>
|
||||
* <p>updateFlag is used to control filter behaviour on the server (remote node) side when it encounters a hit.
|
||||
* See {@link com.google.bitcoin.core.BloomFilter.BloomUpdate} for a brief description of each mode. The purpose
|
||||
* of this flag is to reduce network round-tripping and avoid over-dirtying the filter for the most common
|
||||
* wallet configurations.</p>
|
||||
*/
|
||||
public BloomFilter(int elements, double falsePositiveRate, long randomNonce, BloomUpdate updateFlag) {
|
||||
// The following formulas were stolen from Wikipedia's page on Bloom Filters (with the addition of min(..., MAX_...))
|
||||
@@ -210,8 +208,8 @@ public class BloomFilter extends Message {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given object matches the filter
|
||||
* (either because it was inserted, or because we have a false-positive)
|
||||
* Returns true if the given object matches the filter either because it was inserted, or because we have a
|
||||
* false-positive.
|
||||
*/
|
||||
public boolean contains(byte[] object) {
|
||||
for (int i = 0; i < hashFuncs; i++) {
|
||||
@@ -221,9 +219,7 @@ public class BloomFilter extends Message {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the given arbitrary data into the filter
|
||||
*/
|
||||
/** Insert the given arbitrary data into the filter */
|
||||
public void insert(byte[] object) {
|
||||
for (int i = 0; i < hashFuncs; i++)
|
||||
Utils.setBitLE(data, hash(i, object));
|
||||
|
@@ -838,7 +838,9 @@ public class ECKey implements Serializable {
|
||||
final byte[] privKeyBytes = getPrivKeyBytes();
|
||||
checkState(privKeyBytes != null, "Private key is not available");
|
||||
EncryptedPrivateKey encryptedPrivateKey = keyCrypter.encrypt(privKeyBytes, aesKey);
|
||||
return new ECKey(encryptedPrivateKey, getPubKey(), keyCrypter);
|
||||
ECKey result = new ECKey(encryptedPrivateKey, getPubKey(), keyCrypter);
|
||||
result.setCreationTimeSeconds(creationTimeSeconds);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -860,6 +862,7 @@ public class ECKey implements Serializable {
|
||||
ECKey key = new ECKey(new BigInteger(1, unencryptedPrivateKey), null, isCompressed());
|
||||
if (!Arrays.equals(key.getPubKey(), getPubKey()))
|
||||
throw new KeyCrypterException("Provided AES key is wrong");
|
||||
key.setCreationTimeSeconds(creationTimeSeconds);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
@@ -164,7 +164,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
if (!params.isCheckpoint(height)) {
|
||||
// BIP30 violator blocks are ones that contain a duplicated transaction. They are all in the
|
||||
// checkpoints list and we therefore only check non-checkpoints for duplicated transactions here. See the
|
||||
// BIP30 document for more details on this: https://en.bitcoin.it/wiki/BIP_0030
|
||||
// BIP30 document for more details on this: https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki
|
||||
for (Transaction tx : block.transactions) {
|
||||
Sha256Hash hash = tx.getHash();
|
||||
// If we already have unspent outputs for this hash, we saw the tx already. Either the block is
|
||||
|
@@ -200,8 +200,8 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
|
||||
// Exponential backoff for peers starts at 1 second and maxes at 10 minutes.
|
||||
private ExponentialBackoff.Params peerBackoffParams = new ExponentialBackoff.Params(1000, 1.5f, 10 * 60 * 1000);
|
||||
// Tracks failures globally in case of a network failure
|
||||
private ExponentialBackoff groupBackoff = new ExponentialBackoff(new ExponentialBackoff.Params(100, 1.1f, 30 * 1000));
|
||||
// Tracks failures globally in case of a network failure.
|
||||
private ExponentialBackoff groupBackoff = new ExponentialBackoff(new ExponentialBackoff.Params(1000, 1.5f, 10 * 1000));
|
||||
|
||||
// Things for the dedicated PeerGroup management thread to do.
|
||||
private LinkedBlockingQueue<Runnable> jobQueue = new LinkedBlockingQueue<Runnable>();
|
||||
@@ -342,7 +342,7 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
do {
|
||||
try {
|
||||
connectToAnyPeer();
|
||||
} catch(PeerDiscoveryException e) {
|
||||
} catch (PeerDiscoveryException e) {
|
||||
groupBackoff.trackFailure();
|
||||
}
|
||||
} while (isRunning() && countConnectedAndPendingPeers() < getMaxConnections());
|
||||
@@ -400,7 +400,7 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
* Sets the {@link VersionMessage} that will be announced on newly created connections. A version message is
|
||||
* primarily interesting because it lets you customize the "subVer" field which is used a bit like the User-Agent
|
||||
* field from HTTP. It means your client tells the other side what it is, see
|
||||
* <a href="https://en.bitcoin.it/wiki/BIP_0014">BIP 14</a>.
|
||||
* <a href="https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki">BIP 14</a>.
|
||||
*
|
||||
* The VersionMessage you provide is copied and the best chain height/time filled in for each new connection,
|
||||
* therefore you don't have to worry about setting that. The provided object is really more of a template.
|
||||
@@ -575,6 +575,8 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
}
|
||||
|
||||
protected void discoverPeers() throws PeerDiscoveryException {
|
||||
if (peerDiscoverers.isEmpty())
|
||||
throw new PeerDiscoveryException("No peer discoverers registered");
|
||||
long start = System.currentTimeMillis();
|
||||
Set<PeerAddress> addressSet = Sets.newHashSet();
|
||||
for (PeerDiscovery peerDiscovery : peerDiscoverers) {
|
||||
@@ -630,10 +632,10 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
final State state = state();
|
||||
if (!(state == State.STARTING || state == State.RUNNING)) return;
|
||||
|
||||
final PeerAddress addr;
|
||||
PeerAddress addr = null;
|
||||
|
||||
long nowMillis = Utils.currentTimeMillis();
|
||||
|
||||
long retryTime = 0;
|
||||
lock.lock();
|
||||
try {
|
||||
if (!haveReadyInactivePeer(nowMillis)) {
|
||||
@@ -646,18 +648,21 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
return;
|
||||
}
|
||||
addr = inactives.poll();
|
||||
retryTime = backoffMap.get(addr).getRetryTime();
|
||||
} finally {
|
||||
// discoverPeers might throw an exception if something goes wrong: we then hit this path with addr == null.
|
||||
retryTime = Math.max(retryTime, groupBackoff.getRetryTime());
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
// Delay if any backoff is required
|
||||
long retryTime = Math.max(backoffMap.get(addr).getRetryTime(), groupBackoff.getRetryTime());
|
||||
if (retryTime > nowMillis) {
|
||||
// Sleep until retry time
|
||||
Utils.sleep(retryTime - nowMillis);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -803,11 +808,12 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
BloomFilter filter = new BloomFilter(lastBloomFilterElementCount, bloomFilterFPRate, bloomFilterTweak, bloomFlags);
|
||||
for (PeerFilterProvider p : peerFilterProviders)
|
||||
filter.merge(p.getBloomFilter(lastBloomFilterElementCount, bloomFilterFPRate, bloomFilterTweak));
|
||||
bloomFilter = filter;
|
||||
|
||||
boolean changed = !filter.equals(bloomFilter);
|
||||
boolean send = false;
|
||||
|
||||
bloomFilter = filter;
|
||||
|
||||
switch (mode) {
|
||||
case SEND_IF_CHANGED: send = changed; break;
|
||||
case DONT_SEND: send = false; break;
|
||||
@@ -1407,39 +1413,11 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
* If multiple heights are tied, the highest is returned. If no peers are connected, returns zero.
|
||||
*/
|
||||
public static int getMostCommonChainHeight(final List<Peer> peers) {
|
||||
int s = peers.size();
|
||||
int[] heights = new int[s];
|
||||
int[] counts = new int[s];
|
||||
int maxCount = 0;
|
||||
// Calculate the frequencies of each reported height.
|
||||
for (Peer peer : peers) {
|
||||
int h = (int) peer.getBestHeight();
|
||||
// Find the index of the peers height in the heights array.
|
||||
for (int cursor = 0; cursor < s; cursor++) {
|
||||
if (heights[cursor] == h) {
|
||||
maxCount = Math.max(++counts[cursor], maxCount);
|
||||
break;
|
||||
} else if (heights[cursor] == 0) {
|
||||
// A new height we didn't see before.
|
||||
checkState(counts[cursor] == 0);
|
||||
heights[cursor] = h;
|
||||
counts[cursor] = 1;
|
||||
maxCount = Math.max(maxCount, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Find the heights that have the highest frequencies.
|
||||
int[] freqHeights = new int[s];
|
||||
int cursor = 0;
|
||||
for (int i = 0; i < s; i++) {
|
||||
if (counts[i] == maxCount) {
|
||||
freqHeights[cursor++] = heights[i];
|
||||
}
|
||||
}
|
||||
// Return the highest of the most common heights.
|
||||
Arrays.sort(freqHeights);
|
||||
return freqHeights[s - 1];
|
||||
if (peers.isEmpty())
|
||||
return 0;
|
||||
List<Integer> heights = new ArrayList<Integer>(peers.size());
|
||||
for (Peer peer : peers) heights.add((int) peer.getBestHeight());
|
||||
return Utils.maxOfMostFreq(heights);
|
||||
}
|
||||
|
||||
private static class PeerAndPing {
|
||||
|
@@ -375,6 +375,18 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any of the outputs is marked as spent.
|
||||
*/
|
||||
public boolean isAnyOutputSpent() {
|
||||
maybeParse();
|
||||
for (TransactionOutput output : outputs) {
|
||||
if (!output.isAvailableForSpending())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if this transaction has at least one output that is owned by the given wallet and unspent, true
|
||||
* otherwise.
|
||||
@@ -602,8 +614,15 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
try {
|
||||
Script scriptSig = in.getScriptSig();
|
||||
s.append(scriptSig);
|
||||
s.append(" / ");
|
||||
s.append(in.getOutpoint().toString());
|
||||
s.append("\n ");
|
||||
s.append("outpoint:");
|
||||
final TransactionOutPoint outpoint = in.getOutpoint();
|
||||
s.append(outpoint.toString());
|
||||
final TransactionOutput connectedOutput = outpoint.getConnectedOutput();
|
||||
if (connectedOutput != null) {
|
||||
s.append(" hash160:");
|
||||
s.append(Utils.bytesToHexString(connectedOutput.getScriptPubKey().getPubKeyHash()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s.append("[exception: ").append(e.getMessage()).append("]");
|
||||
}
|
||||
|
@@ -402,10 +402,12 @@ public class TransactionInput extends ChildMessage implements Serializable {
|
||||
* @throws VerificationException If the outpoint doesn't match the given output.
|
||||
*/
|
||||
public void verify(TransactionOutput output) throws VerificationException {
|
||||
if (!getOutpoint().getHash().equals(output.parentTransaction.getHash()))
|
||||
throw new VerificationException("This input does not refer to the tx containing the output.");
|
||||
if (getOutpoint().getIndex() != output.getIndex())
|
||||
throw new VerificationException("This input refers to a different output on the given tx.");
|
||||
if (output.parentTransaction != null) {
|
||||
if (!getOutpoint().getHash().equals(output.parentTransaction.getHash()))
|
||||
throw new VerificationException("This input does not refer to the tx containing the output.");
|
||||
if (getOutpoint().getIndex() != output.getIndex())
|
||||
throw new VerificationException("This input refers to a different output on the given tx.");
|
||||
}
|
||||
Script pubKey = output.getScriptPubKey();
|
||||
int myIndex = parentTransaction.getInputs().indexOf(this);
|
||||
getScriptSig().correctlySpends(parentTransaction, myIndex, pubKey, true);
|
||||
|
@@ -174,7 +174,7 @@ public class TransactionOutput extends ChildMessage implements Serializable {
|
||||
}
|
||||
|
||||
int getIndex() {
|
||||
checkNotNull(parentTransaction);
|
||||
checkNotNull(parentTransaction, "This output is not attached to a parent transaction.");
|
||||
for (int i = 0; i < parentTransaction.getOutputs().size(); i++) {
|
||||
if (parentTransaction.getOutputs().get(i) == this)
|
||||
return i;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +18,9 @@
|
||||
package com.google.bitcoin.core;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.UnsignedLongs;
|
||||
import org.spongycastle.crypto.digests.RIPEMD160Digest;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
@@ -28,8 +32,7 @@ import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -460,13 +463,20 @@ public class Utils {
|
||||
*/
|
||||
public static Date rollMockClockMillis(long millis) {
|
||||
if (mockTime == null)
|
||||
mockTime = new Date();
|
||||
throw new IllegalStateException("You need to use setMockClock() first.");
|
||||
mockTime = new Date(mockTime.getTime() + millis);
|
||||
return mockTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mock clock to the given time (in seconds)
|
||||
* Sets the mock clock to the current time.
|
||||
*/
|
||||
public static void setMockClock() {
|
||||
mockTime = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mock clock to the given time (in seconds).
|
||||
*/
|
||||
public static void setMockClock(long mockClock) {
|
||||
mockTime = new Date(mockClock * 1000);
|
||||
@@ -596,4 +606,43 @@ public class Utils {
|
||||
mockSleepQueue.offer(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Pair implements Comparable<Pair> {
|
||||
int item, count;
|
||||
public Pair(int item, int count) { this.count = count; this.item = item; }
|
||||
@Override public int compareTo(Pair o) { return -Ints.compare(count, o.count); }
|
||||
}
|
||||
|
||||
public static int maxOfMostFreq(int... items) {
|
||||
// Java 6 sucks.
|
||||
ArrayList<Integer> list = new ArrayList<Integer>(items.length);
|
||||
for (int item : items) list.add(item);
|
||||
return maxOfMostFreq(list);
|
||||
}
|
||||
|
||||
public static int maxOfMostFreq(List<Integer> items) {
|
||||
if (items.isEmpty())
|
||||
return 0;
|
||||
// This would be much easier in a functional language (or in Java 8).
|
||||
items = Ordering.natural().reverse().sortedCopy(items);
|
||||
LinkedList<Pair> pairs = Lists.newLinkedList();
|
||||
pairs.add(new Pair(items.get(0), 0));
|
||||
for (int item : items) {
|
||||
Pair pair = pairs.getLast();
|
||||
if (pair.item != item)
|
||||
pairs.add((pair = new Pair(item, 0)));
|
||||
pair.count++;
|
||||
}
|
||||
// pairs now contains a uniqified list of the sorted inputs, with counts for how often that item appeared.
|
||||
// Now sort by how frequently they occur, and pick the max of the most frequent.
|
||||
Collections.sort(pairs);
|
||||
int maxCount = pairs.getFirst().count;
|
||||
int maxItem = pairs.getFirst().item;
|
||||
for (Pair pair : pairs) {
|
||||
if (pair.count != maxCount)
|
||||
break;
|
||||
maxItem = Math.max(maxItem, pair.item);
|
||||
}
|
||||
return maxItem;
|
||||
}
|
||||
}
|
||||
|
@@ -74,7 +74,7 @@ public class VersionMessage extends Message {
|
||||
public boolean relayTxesBeforeFilter;
|
||||
|
||||
/** The version of this library release, as a string. */
|
||||
public static final String BITCOINJ_VERSION = "0.11-SNAPSHOT";
|
||||
public static final String BITCOINJ_VERSION = "0.11.2";
|
||||
/** The value that is prepended to the subVer field of this application. */
|
||||
public static final String LIBRARY_SUBVER = "/BitCoinJ:" + BITCOINJ_VERSION + "/";
|
||||
|
||||
@@ -275,7 +275,7 @@ public class VersionMessage extends Message {
|
||||
* set of "/BitCoinJ:1.0/MultiBit:1.0(Windows)/. Therefore the / ( and ) characters are reserved in all these
|
||||
* components. If you don't want to add a comment (recommended), pass null.<p>
|
||||
*
|
||||
* See <a href="https://en.bitcoin.it/wiki/BIP_0014">BIP 14</a> for more information.
|
||||
* See <a href="https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki">BIP 14</a> for more information.
|
||||
*
|
||||
* @param comments Optional (can be null) platform or other node specific information.
|
||||
* @throws IllegalArgumentException if name, version or comments contains invalid characters.
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,6 +32,7 @@ import com.google.bitcoin.wallet.*;
|
||||
import com.google.bitcoin.wallet.WalletTransaction.Pool;
|
||||
import com.google.common.collect.*;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
@@ -39,6 +41,7 @@ import org.bitcoinj.wallet.Protos.Wallet.EncryptionType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -434,6 +437,25 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Disables auto-saving, after it had been enabled with
|
||||
* {@link Wallet#autosaveToFile(java.io.File, long, java.util.concurrent.TimeUnit, com.google.bitcoin.wallet.WalletFiles.Listener)}
|
||||
* before. This method blocks until finished.
|
||||
* </p>
|
||||
*/
|
||||
public void shutdownAutosaveAndWait() {
|
||||
lock.lock();
|
||||
try {
|
||||
WalletFiles files = vFileManager;
|
||||
vFileManager = null;
|
||||
checkState(files != null, "Auto saving not enabled.");
|
||||
files.shutdownAndWait();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveLater() {
|
||||
WalletFiles files = vFileManager;
|
||||
if (files != null)
|
||||
@@ -529,7 +551,13 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) log.error(toString());
|
||||
if (!success) {
|
||||
try {
|
||||
log.error(toString());
|
||||
} catch (RuntimeException x) {
|
||||
log.error("Printing inconsistent wallet failed", x);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
@@ -1065,10 +1093,13 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
log.warn("updateForSpends: saw double spend from chain, handling later.");
|
||||
} else {
|
||||
// We saw two pending transactions that double spend each other. We don't know which will win.
|
||||
// This should not happen.
|
||||
log.warn("Saw two pending transactions double spend each other: {} vs {}",
|
||||
tx.getHash(), input.getConnectedOutput().getSpentBy().getParentTransaction().getHash());
|
||||
// This can happen in the case of bad network nodes that mutate transactions. Do a hex dump
|
||||
// so the exact nature of the mutation can be examined.
|
||||
log.warn("Saw two pending transactions double spend each other");
|
||||
log.warn(" offending input is input {}", tx.getInputs().indexOf(input));
|
||||
log.warn("{}: {}", tx.getHash(), new String(Hex.encode(tx.unsafeBitcoinSerialize())));
|
||||
Transaction other = input.getConnectedOutput().getSpentBy().getParentTransaction();
|
||||
log.warn("{}: {}", other.getHash(), new String(Hex.encode(tx.unsafeBitcoinSerialize())));
|
||||
}
|
||||
} else if (result == TransactionInput.ConnectionResult.SUCCESS) {
|
||||
// Otherwise we saw a transaction spend our coins, but we didn't try and spend them ourselves yet.
|
||||
@@ -1424,6 +1455,40 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the wallet. Currently, it only removes risky pending transaction from the wallet and only if their
|
||||
* outputs have not been spent.
|
||||
*/
|
||||
public void cleanup() {
|
||||
lock.lock();
|
||||
try {
|
||||
boolean dirty = false;
|
||||
for (Iterator<Transaction> i = pending.values().iterator(); i.hasNext();) {
|
||||
Transaction tx = i.next();
|
||||
if (isTransactionRisky(tx, null) && !acceptRiskyTransactions) {
|
||||
log.debug("Found risky transaction {} in wallet during cleanup.", tx.getHashAsString());
|
||||
if (!tx.isAnyOutputSpent()) {
|
||||
tx.disconnectInputs();
|
||||
i.remove();
|
||||
transactions.remove(tx.getHash());
|
||||
dirty = true;
|
||||
log.info("Removed transaction {} from pending pool during cleanup.", tx.getHashAsString());
|
||||
} else {
|
||||
log.info(
|
||||
"Cannot remove transaction {} from pending pool during cleanup, as it's already spent partially.",
|
||||
tx.getHashAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dirty) {
|
||||
checkState(isConsistent());
|
||||
saveLater();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
EnumSet<Pool> getContainingPools(Transaction tx) {
|
||||
lock.lock();
|
||||
try {
|
||||
@@ -1572,7 +1637,7 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
/**
|
||||
* The AES key to use to decrypt the private keys before signing.
|
||||
* If null then no decryption will be performed and if decryption is required an exception will be thrown.
|
||||
* You can get this from a password by doing wallet.getKeyCrypter().derivePassword(password).
|
||||
* You can get this from a password by doing wallet.getKeyCrypter().deriveKey(password).
|
||||
*/
|
||||
public KeyParameter aesKey = null;
|
||||
|
||||
@@ -2013,10 +2078,10 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
|
||||
// If the key has a keyCrypter that does not match the Wallet's then a KeyCrypterException is thrown.
|
||||
// This is done because only one keyCrypter is persisted per Wallet and hence all the keys must be homogenous.
|
||||
if (keyCrypter != null && keyCrypter.getUnderstoodEncryptionType() != EncryptionType.UNENCRYPTED) {
|
||||
if (key.isEncrypted() && !keyCrypter.equals(key.getKeyCrypter())) {
|
||||
throw new KeyCrypterException("Cannot add key " + key.toString() + " because the keyCrypter does not match the wallets. Keys must be homogenous.");
|
||||
}
|
||||
if (isEncrypted() && (!key.isEncrypted() || !keyCrypter.equals(key.getKeyCrypter()))) {
|
||||
throw new KeyCrypterException("Cannot add key " + key.toString() + " because the keyCrypter does not match the wallets. Keys must be homogenous.");
|
||||
} else if (key.isEncrypted() && !isEncrypted()) {
|
||||
throw new KeyCrypterException("Cannot add key because it's encrypted and this wallet is not.");
|
||||
}
|
||||
keychain.add(key);
|
||||
added++;
|
||||
@@ -2264,6 +2329,30 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
return toString(false, true, true, null);
|
||||
}
|
||||
|
||||
private static final Comparator<Transaction> SORT_ORDER_BY_UPDATE_TIME = new Comparator<Transaction>() {
|
||||
|
||||
@Override
|
||||
public int compare(final Transaction tx1, final Transaction tx2) {
|
||||
|
||||
final long time1 = tx1.getUpdateTime().getTime();
|
||||
final long time2 = tx2.getUpdateTime().getTime();
|
||||
|
||||
return -(Longs.compare(time1, time2));
|
||||
}
|
||||
};
|
||||
|
||||
private static final Comparator<Transaction> SORT_ORDER_BY_HEIGHT = new Comparator<Transaction>() {
|
||||
|
||||
@Override
|
||||
public int compare(final Transaction tx1, final Transaction tx2) {
|
||||
|
||||
final int height1 = tx1.getConfidence().getAppearedAtChainHeight();
|
||||
final int height2 = tx2.getConfidence().getAppearedAtChainHeight();
|
||||
|
||||
return -(Ints.compare(height1, height2));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats the wallet as a human readable piece of text. Intended for debugging, the format is not meant to be
|
||||
* stable or human readable.
|
||||
@@ -2277,12 +2366,13 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
lock.lock();
|
||||
try {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
BigInteger balance = getBalance(BalanceType.ESTIMATED);
|
||||
builder.append(String.format("Wallet containing %s BTC (%d satoshis) in:%n",
|
||||
bitcoinValueToPlainString(balance), balance.longValue()));
|
||||
BigInteger estimatedBalance = getBalance(BalanceType.ESTIMATED);
|
||||
BigInteger availableBalance = getBalance(BalanceType.AVAILABLE);
|
||||
builder.append(String.format("Wallet containing %s BTC (available: %s BTC) in:%n",
|
||||
bitcoinValueToPlainString(estimatedBalance), bitcoinValueToPlainString(availableBalance)));
|
||||
builder.append(String.format(" %d pending transactions%n", pending.size()));
|
||||
builder.append(String.format(" %d unspent transactions%n", unspent.size()));
|
||||
builder.append(String.format(" %d spent transactions%n", spent.size()));
|
||||
builder.append(String.format(" %d pending transactions%n", pending.size()));
|
||||
builder.append(String.format(" %d dead transactions%n", dead.size()));
|
||||
final Date lastBlockSeenTime = getLastBlockSeenTime();
|
||||
final String lastBlockSeenTimeStr = lastBlockSeenTime == null ? "time unknown" : lastBlockSeenTime.toString();
|
||||
@@ -2294,8 +2384,11 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
// Do the keys.
|
||||
builder.append("\nKeys:\n");
|
||||
for (ECKey key : keychain) {
|
||||
final Address address = key.toAddress(params);
|
||||
builder.append(" addr:");
|
||||
builder.append(key.toAddress(params));
|
||||
builder.append(address.toString());
|
||||
builder.append(" hash160:");
|
||||
builder.append(Utils.bytesToHexString(address.getHash160()));
|
||||
builder.append(" ");
|
||||
builder.append(includePrivateKeys ? key.toStringWithPrivate() : key.toString());
|
||||
builder.append("\n");
|
||||
@@ -2312,21 +2405,21 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
|
||||
if (includeTransactions) {
|
||||
// Print the transactions themselves
|
||||
if (pending.size() > 0) {
|
||||
builder.append("\n>>> PENDING:\n");
|
||||
toStringHelper(builder, pending, chain, SORT_ORDER_BY_UPDATE_TIME);
|
||||
}
|
||||
if (unspent.size() > 0) {
|
||||
builder.append("\n>>> UNSPENT:\n");
|
||||
toStringHelper(builder, unspent, chain);
|
||||
toStringHelper(builder, unspent, chain, SORT_ORDER_BY_HEIGHT);
|
||||
}
|
||||
if (spent.size() > 0) {
|
||||
builder.append("\n>>> SPENT:\n");
|
||||
toStringHelper(builder, spent, chain);
|
||||
}
|
||||
if (pending.size() > 0) {
|
||||
builder.append("\n>>> PENDING:\n");
|
||||
toStringHelper(builder, pending, chain);
|
||||
toStringHelper(builder, spent, chain, SORT_ORDER_BY_HEIGHT);
|
||||
}
|
||||
if (dead.size() > 0) {
|
||||
builder.append("\n>>> DEAD:\n");
|
||||
toStringHelper(builder, dead, chain);
|
||||
toStringHelper(builder, dead, chain, SORT_ORDER_BY_HEIGHT);
|
||||
}
|
||||
}
|
||||
if (includeExtensions && extensions.size() > 0) {
|
||||
@@ -2342,9 +2435,18 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
}
|
||||
|
||||
private void toStringHelper(StringBuilder builder, Map<Sha256Hash, Transaction> transactionMap,
|
||||
@Nullable AbstractBlockChain chain) {
|
||||
@Nullable AbstractBlockChain chain, @Nullable Comparator<Transaction> sortOrder) {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
for (Transaction tx : transactionMap.values()) {
|
||||
|
||||
final Collection<Transaction> txns;
|
||||
if (sortOrder != null) {
|
||||
txns = new TreeSet<Transaction>(sortOrder);
|
||||
txns.addAll(transactionMap.values());
|
||||
} else {
|
||||
txns = transactionMap.values();
|
||||
}
|
||||
|
||||
for (Transaction tx : txns) {
|
||||
try {
|
||||
builder.append("Sends ");
|
||||
builder.append(Utils.bitcoinValueToFriendlyString(tx.getValueSentFromMe(this)));
|
||||
|
@@ -39,7 +39,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A deterministic key is a node in a {@link DeterministicHierarchy}. As per
|
||||
* <a href="https://en.bitcoin.it/wiki/BIP_0032">the BIP 32 specification</a> it is a pair (key, chaincode). If you
|
||||
* <a href="https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki">the BIP 32 specification</a> it is a pair (key, chaincode). If you
|
||||
* know its path in the tree you can derive more keys from this.
|
||||
*/
|
||||
public class DeterministicKey implements Serializable {
|
||||
|
@@ -31,6 +31,7 @@ import org.spongycastle.crypto.params.ParametersWithIV;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@@ -154,11 +155,10 @@ public class KeyCrypterScrypt implements KeyCrypter, Serializable {
|
||||
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()));
|
||||
cipher.init(true, keyWithIv);
|
||||
byte[] encryptedBytes = new byte[cipher.getOutputSize(plainBytes.length)];
|
||||
int length = cipher.processBytes(plainBytes, 0, plainBytes.length, encryptedBytes, 0);
|
||||
final int length1 = cipher.processBytes(plainBytes, 0, plainBytes.length, encryptedBytes, 0);
|
||||
final int length2 = cipher.doFinal(encryptedBytes, length1);
|
||||
|
||||
cipher.doFinal(encryptedBytes, length);
|
||||
|
||||
return new EncryptedPrivateKey(iv, encryptedBytes);
|
||||
return new EncryptedPrivateKey(iv, Arrays.copyOf(encryptedBytes, length1 + length2));
|
||||
} catch (Exception e) {
|
||||
throw new KeyCrypterException("Could not encrypt bytes.", e);
|
||||
}
|
||||
@@ -185,16 +185,11 @@ public class KeyCrypterScrypt implements KeyCrypter, Serializable {
|
||||
cipher.init(false, keyWithIv);
|
||||
|
||||
byte[] cipherBytes = privateKeyToDecode.getEncryptedBytes();
|
||||
int minimumSize = cipher.getOutputSize(cipherBytes.length);
|
||||
byte[] outputBuffer = new byte[minimumSize];
|
||||
int length1 = cipher.processBytes(cipherBytes, 0, cipherBytes.length, outputBuffer, 0);
|
||||
int length2 = cipher.doFinal(outputBuffer, length1);
|
||||
int actualLength = length1 + length2;
|
||||
byte[] decryptedBytes = new byte[cipher.getOutputSize(cipherBytes.length)];
|
||||
final int length1 = cipher.processBytes(cipherBytes, 0, cipherBytes.length, decryptedBytes, 0);
|
||||
final int length2 = cipher.doFinal(decryptedBytes, length1);
|
||||
|
||||
byte[] decryptedBytes = new byte[actualLength];
|
||||
System.arraycopy(outputBuffer, 0, decryptedBytes, 0, actualLength);
|
||||
|
||||
return decryptedBytes;
|
||||
return Arrays.copyOf(decryptedBytes, length1 + length2);
|
||||
} catch (Exception e) {
|
||||
throw new KeyCrypterException("Could not decrypt bytes", e);
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* A MnemonicCode object may be used to convert between binary seed values and
|
||||
* lists of words per <a href="https://en.bitcoin.it/wiki/BIP_0039">the BIP 39
|
||||
* lists of words per <a href="https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki">the BIP 39
|
||||
* specification</a>
|
||||
*/
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,6 +23,7 @@ import com.google.bitcoin.script.ScriptBuilder;
|
||||
import com.google.bitcoin.uri.BitcoinURI;
|
||||
import com.google.bitcoin.utils.Threading;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
@@ -42,6 +44,7 @@ import java.net.*;
|
||||
import java.security.*;
|
||||
import java.security.cert.*;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
@@ -72,6 +75,7 @@ import java.util.concurrent.Callable;
|
||||
* "processing" or that an error occurred.
|
||||
*
|
||||
* @author Kevin Greene
|
||||
* @author Andreas Schildbach
|
||||
* @see <a href="https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki">BIP 0070</a>
|
||||
*/
|
||||
public class PaymentSession {
|
||||
@@ -227,26 +231,30 @@ public class PaymentSession {
|
||||
* Message returned by the merchant in response to a Payment message.
|
||||
*/
|
||||
public class Ack {
|
||||
private String memo = "";
|
||||
@Nullable private String memo;
|
||||
|
||||
Ack(String memo) {
|
||||
Ack(@Nullable String memo) {
|
||||
this.memo = memo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the memo included by the merchant in the payment ack. This message is typically displayed to the user
|
||||
* as a notification (e.g. "Your payment was received and is being processed").
|
||||
* as a notification (e.g. "Your payment was received and is being processed"). If none was provided, returns
|
||||
* null.
|
||||
*/
|
||||
public String getMemo() {
|
||||
@Nullable public String getMemo() {
|
||||
return memo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the memo included by the merchant in the payment request.
|
||||
* Returns the memo included by the merchant in the payment request, or null if not found.
|
||||
*/
|
||||
public String getMemo() {
|
||||
return paymentDetails.getMemo();
|
||||
@Nullable public String getMemo() {
|
||||
if (paymentDetails.hasMemo())
|
||||
return paymentDetails.getMemo();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,7 +382,7 @@ public class PaymentSession {
|
||||
InputStream inStream = connection.getInputStream();
|
||||
Protos.PaymentACK.Builder paymentAckBuilder = Protos.PaymentACK.newBuilder().mergeFrom(inStream);
|
||||
Protos.PaymentACK paymentAck = paymentAckBuilder.build();
|
||||
String memo = "";
|
||||
String memo = null;
|
||||
if (paymentAck.hasMemo())
|
||||
memo = paymentAck.getMemo();
|
||||
return new Ack(memo);
|
||||
@@ -386,10 +394,47 @@ public class PaymentSession {
|
||||
* Information about the X509 signature's issuer and subject.
|
||||
*/
|
||||
public static class PkiVerificationData {
|
||||
public String name;
|
||||
public PublicKey merchantSigningKey;
|
||||
public TrustAnchor rootAuthority;
|
||||
public String orgName;
|
||||
/** Display name of the payment requestor, could be a domain name, email address, legal name, etc */
|
||||
public final String name;
|
||||
/** The "org" part of the payment requestors ID. */
|
||||
public final String orgName;
|
||||
/** SSL public key that was used to sign. */
|
||||
public final PublicKey merchantSigningKey;
|
||||
/** Object representing the CA that verified the merchant's ID */
|
||||
public final TrustAnchor rootAuthority;
|
||||
/** String representing the display name of the CA that verified the merchant's ID */
|
||||
public final String rootAuthorityName;
|
||||
|
||||
private PkiVerificationData(@Nullable String name, @Nullable String orgName, PublicKey merchantSigningKey,
|
||||
TrustAnchor rootAuthority) throws PaymentRequestException.PkiVerificationException {
|
||||
this.name = name;
|
||||
this.orgName = orgName;
|
||||
this.merchantSigningKey = merchantSigningKey;
|
||||
this.rootAuthority = rootAuthority;
|
||||
this.rootAuthorityName = getNameFromCert(rootAuthority);
|
||||
}
|
||||
|
||||
private @Nullable String getNameFromCert(TrustAnchor rootAuthority) throws PaymentRequestException.PkiVerificationException {
|
||||
org.spongycastle.asn1.x500.X500Name name = new X500Name(rootAuthority.getTrustedCert().getSubjectX500Principal().getName());
|
||||
String commonName = null, org = null, location = null, country = null;
|
||||
for (RDN rdn : name.getRDNs()) {
|
||||
AttributeTypeAndValue pair = rdn.getFirst();
|
||||
String val = ((ASN1String)pair.getValue()).getString();
|
||||
if (pair.getType().equals(RFC4519Style.cn))
|
||||
commonName = val;
|
||||
else if (pair.getType().equals(RFC4519Style.o))
|
||||
org = val;
|
||||
else if (pair.getType().equals(RFC4519Style.l))
|
||||
location = val;
|
||||
else if (pair.getType().equals(RFC4519Style.c))
|
||||
country = val;
|
||||
}
|
||||
if (org != null) {
|
||||
return Joiner.on(", ").skipNulls().join(org, location, country);
|
||||
} else {
|
||||
return commonName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,7 +492,8 @@ public class PaymentSession {
|
||||
throw new PaymentRequestException.PkiVerificationException("Invalid signature, this payment request is not valid.");
|
||||
|
||||
// Signature verifies, get the names from the identity we just verified for presentation to the user.
|
||||
X500Principal principal = certs.get(0).getSubjectX500Principal();
|
||||
final X509Certificate cert = certs.get(0);
|
||||
X500Principal principal = cert.getSubjectX500Principal();
|
||||
// At this point the Java crypto API falls flat on its face and dies - there's no clean way to get the
|
||||
// different parts of the certificate name except for parsing the string. That's hard because of various
|
||||
// custom escaping rules and the usual crap. So, use Bouncy Castle to re-parse the string into binary form
|
||||
@@ -461,13 +507,19 @@ public class PaymentSession {
|
||||
else if (pair.getType().equals(RFC4519Style.o))
|
||||
orgName = ((ASN1String)pair.getValue()).getString();
|
||||
}
|
||||
|
||||
if (entityName == null && orgName == null) {
|
||||
// This cert might not be an SSL cert. Just grab the first "subject alt name" if present, e.g. for
|
||||
// S/MIME certs.
|
||||
final Iterator<List<?>> it = cert.getSubjectAlternativeNames().iterator();
|
||||
List<?> list;
|
||||
// email addresses have a type code of one.
|
||||
if (it.hasNext() && (list = it.next()) != null && (Integer) list.get(0) == 1)
|
||||
entityName = (String) list.get(1);
|
||||
if (entityName == null)
|
||||
throw new PaymentRequestException.PkiVerificationException("Could not extract name from certificate");
|
||||
}
|
||||
// Everything is peachy. Return some useful data to the caller.
|
||||
PkiVerificationData data = new PkiVerificationData();
|
||||
data.name = entityName;
|
||||
data.orgName = orgName;
|
||||
data.merchantSigningKey = publicKey;
|
||||
data.rootAuthority = result.getTrustAnchor();
|
||||
PkiVerificationData data = new PkiVerificationData(entityName, orgName, publicKey, result.getTrustAnchor());
|
||||
// Cache the result so we don't have to re-verify if this method is called again.
|
||||
pkiVerificationData = data;
|
||||
return data;
|
||||
@@ -557,8 +609,6 @@ public class PaymentSession {
|
||||
try {
|
||||
if (request == null)
|
||||
throw new PaymentRequestException("request cannot be null");
|
||||
if (!request.hasPaymentDetailsVersion())
|
||||
throw new PaymentRequestException.InvalidVersion("No version");
|
||||
if (request.getPaymentDetailsVersion() != 1)
|
||||
throw new PaymentRequestException.InvalidVersion("Version 1 required. Received version " + request.getPaymentDetailsVersion());
|
||||
paymentRequest = request;
|
||||
@@ -581,10 +631,20 @@ public class PaymentSession {
|
||||
}
|
||||
// This won't ever happen in practice. It would only happen if the user provided outputs
|
||||
// that are obviously invalid. Still, we don't want to silently overflow.
|
||||
if (totalValue.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0)
|
||||
if (totalValue.compareTo(NetworkParameters.MAX_MONEY) > 0)
|
||||
throw new PaymentRequestException.InvalidOutputs("The outputs are way too big.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new PaymentRequestException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the protobuf that this object was instantiated with. */
|
||||
public Protos.PaymentRequest getPaymentRequest() {
|
||||
return paymentRequest;
|
||||
}
|
||||
|
||||
/** Returns the protobuf that describes the payment to be made. */
|
||||
public Protos.PaymentDetails getPaymentDetails() {
|
||||
return paymentDetails;
|
||||
}
|
||||
}
|
||||
|
@@ -472,7 +472,7 @@ public class Script {
|
||||
* spending input to provide a program matching that hash. This rule is "soft enforced" by the network as it does
|
||||
* not exist in Satoshis original implementation. It means blocks containing P2SH transactions that don't match
|
||||
* correctly are considered valid, but won't be mined upon, so they'll be rapidly re-orgd out of the chain. This
|
||||
* logic is defined by <a href="https://en.bitcoin.it/wiki/BIP_0016">BIP 16</a>.</p>
|
||||
* logic is defined by <a href="https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki">BIP 16</a>.</p>
|
||||
*
|
||||
* <p>bitcoinj does not support creation of P2SH transactions today. The goal of P2SH is to allow short addresses
|
||||
* even for complex scripts (eg, multi-sig outputs) so they are convenient to work with in things like QRcodes or
|
||||
@@ -660,10 +660,7 @@ public class Script {
|
||||
continue;
|
||||
|
||||
switch(opcode) {
|
||||
case OP_0:
|
||||
// This is also OP_FALSE (they are both zero).
|
||||
stack.add(new byte[]{0});
|
||||
break;
|
||||
// OP_0 is no opcode
|
||||
case OP_1NEGATE:
|
||||
stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.ONE.negate(), false)));
|
||||
break;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Copyright 2012 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,7 +26,10 @@ import com.google.bitcoin.script.Script;
|
||||
import com.google.bitcoin.wallet.WalletTransaction;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.CodedInputStream;
|
||||
import com.google.protobuf.TextFormat;
|
||||
import com.google.protobuf.WireFormat;
|
||||
|
||||
import org.bitcoinj.wallet.Protos;
|
||||
import org.bitcoinj.wallet.Protos.Wallet.EncryptionType;
|
||||
import org.slf4j.Logger;
|
||||
@@ -693,4 +697,25 @@ public class WalletProtobufSerializer {
|
||||
default: confidence.setSource(TransactionConfidence.Source.UNKNOWN); break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cheap test to see if input stream is a wallet. This checks for a magic value at the beginning of the stream.
|
||||
*
|
||||
* @param is
|
||||
* input stream to test
|
||||
* @return true if input stream is a wallet
|
||||
*/
|
||||
public static boolean isWallet(InputStream is) {
|
||||
try {
|
||||
final CodedInputStream cis = CodedInputStream.newInstance(is);
|
||||
final int tag = cis.readTag();
|
||||
final int field = WireFormat.getTagFieldNumber(tag);
|
||||
if (field != 1) // network_identifier
|
||||
return false;
|
||||
final String network = cis.readString();
|
||||
return NetworkParameters.fromID(network) != null;
|
||||
} catch (IOException x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
* Copyright 2012, 2014 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -32,6 +32,7 @@ import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@@ -73,7 +74,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
* @author Andreas Schildbach (initial code)
|
||||
* @author Jim Burton (enhancements for MultiBit)
|
||||
* @author Gary Rowe (BIP21 support)
|
||||
* @see <a href="https://en.bitcoin.it/wiki/BIP_0021">BIP 0021</a>
|
||||
* @see <a href="https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki">BIP 0021</a>
|
||||
*/
|
||||
public class BitcoinURI {
|
||||
/**
|
||||
@@ -192,14 +193,15 @@ public class BitcoinURI {
|
||||
private void parseParameters(@Nullable NetworkParameters params, String addressToken, String[] nameValuePairTokens) throws BitcoinURIParseException {
|
||||
// Attempt to decode the rest of the tokens into a parameter map.
|
||||
for (String nameValuePairToken : nameValuePairTokens) {
|
||||
String[] tokens = nameValuePairToken.split("=");
|
||||
if (tokens.length != 2 || "".equals(tokens[0])) {
|
||||
throw new BitcoinURIParseException("Malformed Bitcoin URI - cannot parse name value pair '" +
|
||||
final int sepIndex = nameValuePairToken.indexOf('=');
|
||||
if (sepIndex == -1)
|
||||
throw new BitcoinURIParseException("Malformed Bitcoin URI - no separator in '" +
|
||||
nameValuePairToken + "'");
|
||||
}
|
||||
|
||||
String nameToken = tokens[0].toLowerCase();
|
||||
String valueToken = tokens[1];
|
||||
if (sepIndex == 0)
|
||||
throw new BitcoinURIParseException("Malformed Bitcoin URI - empty name '" +
|
||||
nameValuePairToken + "'");
|
||||
final String nameToken = nameValuePairToken.substring(0, sepIndex).toLowerCase(Locale.ENGLISH);
|
||||
final String valueToken = nameValuePairToken.substring(sepIndex + 1);
|
||||
|
||||
// Parse the amount.
|
||||
if (FIELD_AMOUNT.equals(nameToken)) {
|
||||
@@ -219,7 +221,8 @@ public class BitcoinURI {
|
||||
} else {
|
||||
// Known fields and unknown parameters that are optional.
|
||||
try {
|
||||
putWithValidation(nameToken, URLDecoder.decode(valueToken, "UTF-8"));
|
||||
if (valueToken.length() > 0)
|
||||
putWithValidation(nameToken, URLDecoder.decode(valueToken, "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// Unreachable.
|
||||
throw new RuntimeException(e);
|
||||
|
@@ -51,8 +51,11 @@ public class BlockFileLoader implements Iterable<Block>, Iterator<Block> {
|
||||
*/
|
||||
public static List<File> getReferenceClientBlockFileList() {
|
||||
String defaultDataDir;
|
||||
if (System.getProperty("os.name").toLowerCase().indexOf("win") >= 0) {
|
||||
String OS = System.getProperty("os.name").toLowerCase();
|
||||
if (OS.indexOf("win") >= 0) {
|
||||
defaultDataDir = System.getenv("APPDATA") + "\\.bitcoin\\blocks\\";
|
||||
} else if (OS.indexOf("mac") >= 0 || (OS.indexOf("darwin") >= 0)) {
|
||||
defaultDataDir = System.getProperty("user.home") + "/Library/Application Support/Bitcoin/blocks/";
|
||||
} else {
|
||||
defaultDataDir = System.getProperty("user.home") + "/.bitcoin/blocks/";
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,11 +17,15 @@
|
||||
|
||||
package com.google.bitcoin.wallet;
|
||||
|
||||
import com.google.bitcoin.core.NetworkParameters;
|
||||
import com.google.bitcoin.core.Transaction;
|
||||
import com.google.bitcoin.core.TransactionConfidence;
|
||||
import com.google.bitcoin.core.TransactionOutput;
|
||||
import com.google.bitcoin.core.Wallet;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
@@ -30,10 +35,18 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
* of specialised protocols you should not encounter non-final transactions.
|
||||
*/
|
||||
public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
/**
|
||||
* Any standard output smaller than this value (in satoshis) will be considered risky, as it's most likely be
|
||||
* rejected by the network. Currently it's 546 satoshis. This is different from {@link Transaction#MIN_NONDUST_OUTPUT}
|
||||
* because of an upcoming fee change in Bitcoin Core 0.9.
|
||||
*/
|
||||
public static final BigInteger MIN_ANALYSIS_NONDUST_OUTPUT = BigInteger.valueOf(546);
|
||||
|
||||
protected final Transaction tx;
|
||||
protected final List<Transaction> dependencies;
|
||||
protected final Wallet wallet;
|
||||
|
||||
private Transaction nonStandard;
|
||||
protected Transaction nonFinal;
|
||||
protected boolean analyzed;
|
||||
|
||||
@@ -48,6 +61,14 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
checkState(!analyzed);
|
||||
analyzed = true;
|
||||
|
||||
Result result = analyzeIsFinal();
|
||||
if (result != Result.OK)
|
||||
return result;
|
||||
|
||||
return analyzeIsStandard();
|
||||
}
|
||||
|
||||
private Result analyzeIsFinal() {
|
||||
// Transactions we create ourselves are, by definition, not at risk of double spending against us.
|
||||
if (tx.getConfidence().getSource() == TransactionConfidence.Source.SELF)
|
||||
return Result.OK;
|
||||
@@ -71,6 +92,49 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
return Result.OK;
|
||||
}
|
||||
|
||||
private Result analyzeIsStandard() {
|
||||
if (!wallet.getNetworkParameters().getId().equals(NetworkParameters.ID_MAINNET))
|
||||
return Result.OK;
|
||||
|
||||
nonStandard = isStandard(tx);
|
||||
if (nonStandard != null)
|
||||
return Result.NON_STANDARD;
|
||||
|
||||
for (Transaction dep : dependencies) {
|
||||
nonStandard = isStandard(dep);
|
||||
if (nonStandard != null)
|
||||
return Result.NON_STANDARD;
|
||||
}
|
||||
|
||||
return Result.OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks if a transaction is considered "standard" by the reference client's IsStandardTx and AreInputsStandard
|
||||
* functions.</p>
|
||||
*
|
||||
* <p>Note that this method currently only implements a minimum of checks. More to be added later.</p>
|
||||
*
|
||||
* @return Either null if the transaction is standard, or the first transaction found which is considered nonstandard
|
||||
*/
|
||||
public Transaction isStandard(Transaction tx) {
|
||||
if (tx.getVersion() > 1 || tx.getVersion() < 1)
|
||||
return tx;
|
||||
|
||||
for (TransactionOutput output : tx.getOutputs()) {
|
||||
if (MIN_ANALYSIS_NONDUST_OUTPUT.compareTo(output.getValue()) > 0)
|
||||
return tx;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns the transaction that was found to be non-standard, or null. */
|
||||
@Nullable
|
||||
public Transaction getNonStandard() {
|
||||
return nonStandard;
|
||||
}
|
||||
|
||||
/** Returns the transaction that was found to be non-final, or null. */
|
||||
@Nullable
|
||||
public Transaction getNonFinal() {
|
||||
@@ -83,6 +147,8 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
return "Pending risk analysis for " + tx.getHashAsString();
|
||||
else if (nonFinal != null)
|
||||
return "Risky due to non-finality of " + nonFinal.getHashAsString();
|
||||
else if (nonStandard != null)
|
||||
return "Risky due to non-standard tx " + nonStandard.getHashAsString();
|
||||
else
|
||||
return "Non-risky";
|
||||
}
|
||||
|
@@ -34,7 +34,8 @@ import java.util.List;
|
||||
public interface RiskAnalysis {
|
||||
public enum Result {
|
||||
OK,
|
||||
NON_FINAL
|
||||
NON_FINAL,
|
||||
NON_STANDARD
|
||||
}
|
||||
|
||||
public Result analyze();
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -77,6 +78,7 @@ public class WalletFiles {
|
||||
this.executor = new ScheduledThreadPoolExecutor(1, builder.build());
|
||||
this.executor.setKeepAliveTime(5, TimeUnit.SECONDS);
|
||||
this.executor.allowCoreThreadTimeOut(true);
|
||||
this.executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
this.wallet = checkNotNull(wallet);
|
||||
// File must only be accessed from the auto-save executor from now on, to avoid simultaneous access.
|
||||
this.file = checkNotNull(file);
|
||||
@@ -132,4 +134,14 @@ public class WalletFiles {
|
||||
return; // Already pending.
|
||||
executor.schedule(saver, delay, delayTimeUnit);
|
||||
}
|
||||
|
||||
/** Shut down auto-saving. */
|
||||
public void shutdownAndWait() {
|
||||
executor.shutdown();
|
||||
try {
|
||||
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); // forever
|
||||
} catch (InterruptedException x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -52,6 +52,7 @@ public class ChainSplitTest {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
BriefLogFormatter.init();
|
||||
Utils.setMockClock(); // Use mock clock
|
||||
Wallet.SendRequest.DEFAULT_FEE_PER_KB = BigInteger.ZERO;
|
||||
unitTestParams = UnitTestParams.get();
|
||||
wallet = new Wallet(unitTestParams);
|
||||
|
@@ -51,6 +51,7 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static com.google.bitcoin.core.Utils.reverseBytes;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ECKeyTest {
|
||||
@@ -249,55 +250,25 @@ public class ECKeyTest {
|
||||
|
||||
@Test
|
||||
public void testUnencryptedCreate() throws Exception {
|
||||
ECKey unencryptedKey = new ECKey();
|
||||
|
||||
// The key should initially be unencrypted.
|
||||
assertTrue(!unencryptedKey.isEncrypted());
|
||||
|
||||
// Copy the private key bytes for checking later.
|
||||
byte[] originalPrivateKeyBytes = new byte[32];
|
||||
System.arraycopy(unencryptedKey.getPrivKeyBytes(), 0, originalPrivateKeyBytes, 0, 32);
|
||||
log.info("Original private key = " + Utils.bytesToHexString(originalPrivateKeyBytes));
|
||||
|
||||
// Encrypt the key.
|
||||
ECKey encryptedKey = unencryptedKey.encrypt(keyCrypter, keyCrypter.deriveKey(PASSWORD1));
|
||||
|
||||
// The key should now be encrypted.
|
||||
assertTrue("Key is not encrypted but it should be", encryptedKey.isEncrypted());
|
||||
|
||||
// The unencrypted private key bytes of the encrypted keychain
|
||||
// should be null or all be blank.
|
||||
byte[] privateKeyBytes = encryptedKey.getPrivKeyBytes();
|
||||
if (privateKeyBytes != null) {
|
||||
for (int i = 0; i < privateKeyBytes.length; i++) {
|
||||
assertEquals("Byte " + i + " of the private key was not zero but should be", 0, privateKeyBytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt the key.
|
||||
unencryptedKey = encryptedKey.decrypt(keyCrypter, keyCrypter.deriveKey(PASSWORD1));
|
||||
|
||||
// The key should be unencrypted
|
||||
assertTrue("Key is not unencrypted but it should be", !unencryptedKey.isEncrypted());
|
||||
|
||||
// The reborn unencrypted private key bytes should match the
|
||||
// original private key.
|
||||
privateKeyBytes = unencryptedKey.getPrivKeyBytes();
|
||||
log.info("Reborn decrypted private key = " + Utils.bytesToHexString(privateKeyBytes));
|
||||
|
||||
for (int i = 0; i < privateKeyBytes.length; i++) {
|
||||
assertEquals("Byte " + i + " of the private key did not match the original", originalPrivateKeyBytes[i],
|
||||
privateKeyBytes[i]);
|
||||
}
|
||||
Utils.setMockClock();
|
||||
ECKey key = new ECKey();
|
||||
long time = key.getCreationTimeSeconds();
|
||||
assertNotEquals(0, time);
|
||||
assertTrue(!key.isEncrypted());
|
||||
byte[] originalPrivateKeyBytes = key.getPrivKeyBytes();
|
||||
ECKey encryptedKey = key.encrypt(keyCrypter, keyCrypter.deriveKey(PASSWORD1));
|
||||
assertEquals(time, encryptedKey.getCreationTimeSeconds());
|
||||
assertTrue(encryptedKey.isEncrypted());
|
||||
assertNull(encryptedKey.getPrivKeyBytes());
|
||||
key = encryptedKey.decrypt(keyCrypter, keyCrypter.deriveKey(PASSWORD1));
|
||||
assertTrue(!key.isEncrypted());
|
||||
assertArrayEquals(originalPrivateKeyBytes, key.getPrivKeyBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptedCreate() throws Exception {
|
||||
ECKey unencryptedKey = new ECKey();
|
||||
|
||||
// Copy the private key bytes for checking later.
|
||||
byte[] originalPrivateKeyBytes = new byte[32];
|
||||
System.arraycopy(unencryptedKey.getPrivKeyBytes(), 0, originalPrivateKeyBytes, 0, 32);
|
||||
byte[] originalPrivateKeyBytes = checkNotNull(unencryptedKey.getPrivKeyBytes());
|
||||
log.info("Original private key = " + Utils.bytesToHexString(originalPrivateKeyBytes));
|
||||
|
||||
EncryptedPrivateKey encryptedPrivateKey = keyCrypter.encrypt(unencryptedKey.getPrivKeyBytes(), keyCrypter.deriveKey(PASSWORD1));
|
||||
@@ -316,17 +287,8 @@ public class ECKeyTest {
|
||||
|
||||
// Decrypt the key.
|
||||
ECKey rebornUnencryptedKey = encryptedKey.decrypt(keyCrypter, keyCrypter.deriveKey(PASSWORD1));
|
||||
|
||||
// The key should be unencrypted
|
||||
assertTrue("Key is not unencrypted but it should be", !rebornUnencryptedKey.isEncrypted());
|
||||
|
||||
// The reborn unencrypted private key bytes should match the original private key.
|
||||
privateKeyBytes = rebornUnencryptedKey.getPrivKeyBytes();
|
||||
log.info("Reborn decrypted private key = " + Utils.bytesToHexString(privateKeyBytes));
|
||||
|
||||
for (int i = 0; i < privateKeyBytes.length; i++) {
|
||||
assertEquals("Byte " + i + " of the private key did not match the original", originalPrivateKeyBytes[i], privateKeyBytes[i]);
|
||||
}
|
||||
assertTrue(!rebornUnencryptedKey.isEncrypted());
|
||||
assertArrayEquals(originalPrivateKeyBytes, rebornUnencryptedKey.getPrivKeyBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -96,7 +96,7 @@ public class FullBlockTestGenerator {
|
||||
this.params = params;
|
||||
coinbaseOutKey = new ECKey();
|
||||
coinbaseOutKeyPubKey = coinbaseOutKey.getPubKey();
|
||||
Utils.rollMockClock(0); // Set a mock clock for timestamp tests
|
||||
Utils.setMockClock();
|
||||
}
|
||||
|
||||
public RuleList getBlocksToTest(boolean addSigExpensiveBlocks, boolean runLargeReorgs, File blockStorageFile) throws ScriptException, ProtocolException, IOException {
|
||||
|
@@ -500,9 +500,7 @@ public class PeerGroupTest extends TestWithPeerGroup {
|
||||
stopPeerServer(2);
|
||||
assertEquals(2002, disconnectedPeers.take().getAddress().getPort()); // peer died
|
||||
|
||||
// Peer 2 is tried twice before peer 1, since it has a lower backoff due to recent success
|
||||
Utils.passMockSleep();
|
||||
assertEquals(2002, disconnectedPeers.take().getAddress().getPort());
|
||||
// Peer 2 is tried before peer 1, since it has a lower backoff due to recent success
|
||||
Utils.passMockSleep();
|
||||
assertEquals(2002, disconnectedPeers.take().getAddress().getPort());
|
||||
Utils.passMockSleep();
|
||||
@@ -540,4 +538,29 @@ public class PeerGroupTest extends TestWithPeerGroup {
|
||||
assertTrue(p3.lastReceivedFilter.contains(key.getPubKey()));
|
||||
assertTrue(p3.lastReceivedFilter.contains(outpoint.bitcoinSerialize()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBloomResendOnNewKey() throws Exception {
|
||||
// Check that when we add a new key to the wallet, the Bloom filter is re-calculated and re-sent.
|
||||
peerGroup.startAndWait();
|
||||
// Create a couple of peers.
|
||||
InboundMessageQueuer p1 = connectPeer(1);
|
||||
InboundMessageQueuer p2 = connectPeer(2);
|
||||
BloomFilter f1 = p1.lastReceivedFilter;
|
||||
BloomFilter f2 = p2.lastReceivedFilter;
|
||||
final ECKey key = new ECKey();
|
||||
wallet.addKey(key);
|
||||
peerGroup.waitForJobQueue();
|
||||
BloomFilter f3 = (BloomFilter) outbound(p1);
|
||||
BloomFilter f4 = (BloomFilter) outbound(p2);
|
||||
assertTrue(outbound(p1) instanceof MemoryPoolMessage);
|
||||
assertTrue(outbound(p2) instanceof MemoryPoolMessage);
|
||||
assertNotEquals(f1, f3);
|
||||
assertNotEquals(f2, f4);
|
||||
assertEquals(f3, f4);
|
||||
assertTrue(f3.contains(key.getPubKey()));
|
||||
assertTrue(f3.contains(key.getPubKeyHash()));
|
||||
assertFalse(f1.contains(key.getPubKey()));
|
||||
assertFalse(f1.contains(key.getPubKeyHash()));
|
||||
}
|
||||
}
|
||||
|
@@ -36,7 +36,6 @@ import java.math.BigInteger;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -436,7 +435,7 @@ public class PeerTest extends TestWithNetworkConnections {
|
||||
@Test
|
||||
public void fastCatchup() throws Exception {
|
||||
connect();
|
||||
|
||||
Utils.setMockClock();
|
||||
// Check that blocks before the fast catchup point are retrieved using getheaders, and after using getblocks.
|
||||
// This test is INCOMPLETE because it does not check we handle >2000 blocks correctly.
|
||||
Block b1 = createFakeBlock(blockStore).block;
|
||||
@@ -484,7 +483,7 @@ public class PeerTest extends TestWithNetworkConnections {
|
||||
@Test
|
||||
public void pingPong() throws Exception {
|
||||
connect();
|
||||
Utils.rollMockClock(0);
|
||||
Utils.setMockClock();
|
||||
// No ping pong happened yet.
|
||||
assertEquals(Long.MAX_VALUE, peer.getLastPingTime());
|
||||
assertEquals(Long.MAX_VALUE, peer.getPingTime());
|
||||
|
@@ -52,6 +52,7 @@ public class TransactionBroadcastTest extends TestWithPeerGroup {
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Utils.setMockClock(); // Use mock clock
|
||||
super.setUp(new MemoryBlockStore(UnitTestParams.get()));
|
||||
peerGroup.addWallet(wallet);
|
||||
// Fix the random permutation that TransactionBroadcast uses to shuffle the peers.
|
||||
|
@@ -115,4 +115,13 @@ public class UtilsTest {
|
||||
Assert.assertArrayEquals(new byte[0], Utils.reverseDwordBytes(new byte[] {4,3,2,1,8,7,6,5}, 0));
|
||||
Assert.assertArrayEquals(new byte[0], Utils.reverseDwordBytes(new byte[0], 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxOfMostFreq() throws Exception {
|
||||
assertEquals(0, Utils.maxOfMostFreq());
|
||||
assertEquals(0, Utils.maxOfMostFreq(0, 0, 1));
|
||||
assertEquals(2, Utils.maxOfMostFreq(1, 1, 2, 2));
|
||||
assertEquals(1, Utils.maxOfMostFreq(1, 1, 2, 2, 1));
|
||||
assertEquals(-1, Utils.maxOfMostFreq(-1, -1, 2, 2, -1));
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +19,8 @@ package com.google.bitcoin.core;
|
||||
|
||||
import com.google.bitcoin.core.Transaction.SigHash;
|
||||
import com.google.bitcoin.core.Wallet.SendRequest;
|
||||
import com.google.bitcoin.wallet.DefaultCoinSelector;
|
||||
import com.google.bitcoin.wallet.RiskAnalysis;
|
||||
import com.google.bitcoin.wallet.WalletTransaction;
|
||||
import com.google.bitcoin.wallet.WalletTransaction.Pool;
|
||||
import com.google.bitcoin.crypto.KeyCrypter;
|
||||
@@ -67,8 +70,6 @@ public class WalletTest extends TestWithWallet {
|
||||
private Address myEncryptedAddress2;
|
||||
|
||||
private Wallet encryptedWallet;
|
||||
// A wallet with an initial unencrypted private key and an encrypted private key.
|
||||
private Wallet encryptedMixedWallet;
|
||||
|
||||
private static CharSequence PASSWORD1 = "my helicopter contains eels";
|
||||
private static CharSequence WRONG_PASSWORD = "nothing noone nobody nowhere";
|
||||
@@ -89,16 +90,11 @@ public class WalletTest extends TestWithWallet {
|
||||
keyCrypter = new KeyCrypterScrypt(scryptParameters);
|
||||
|
||||
encryptedWallet = new Wallet(params, keyCrypter);
|
||||
encryptedMixedWallet = new Wallet(params, keyCrypter);
|
||||
|
||||
aesKey = keyCrypter.deriveKey(PASSWORD1);
|
||||
wrongAesKey = keyCrypter.deriveKey(WRONG_PASSWORD);
|
||||
ECKey myEncryptedKey = encryptedWallet.addNewEncryptedKey(keyCrypter, aesKey);
|
||||
myEncryptedAddress = myEncryptedKey.toAddress(params);
|
||||
|
||||
encryptedMixedWallet.addKey(new ECKey());
|
||||
ECKey myEncryptedKey2 = encryptedMixedWallet.addNewEncryptedKey(keyCrypter, aesKey);
|
||||
myEncryptedAddress2 = myEncryptedKey2.toAddress(params);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -111,7 +107,7 @@ public class WalletTest extends TestWithWallet {
|
||||
public void basicSpending() throws Exception {
|
||||
basicSpendingCommon(wallet, myAddress, new ECKey().toAddress(params), false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void basicSpendingToP2SH() throws Exception {
|
||||
Address destination = new Address(params, params.getP2SHHeader(), Hex.decode("4a22c3c4cbb31e4d03b15550636762bda0baf85a"));
|
||||
@@ -123,9 +119,114 @@ public class WalletTest extends TestWithWallet {
|
||||
basicSpendingCommon(encryptedWallet, myEncryptedAddress, new ECKey().toAddress(params), true);
|
||||
}
|
||||
|
||||
static class TestRiskAnalysis implements RiskAnalysis {
|
||||
private final boolean risky;
|
||||
|
||||
public TestRiskAnalysis(boolean risky) {
|
||||
this.risky = risky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result analyze() {
|
||||
return risky ? Result.NON_FINAL : Result.OK;
|
||||
}
|
||||
|
||||
public static class Analyzer implements RiskAnalysis.Analyzer {
|
||||
private final Transaction riskyTx;
|
||||
|
||||
Analyzer(Transaction riskyTx) {
|
||||
this.riskyTx = riskyTx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RiskAnalysis create(Wallet wallet, Transaction tx, List<Transaction> dependencies) {
|
||||
return new TestRiskAnalysis(tx == riskyTx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TestCoinSelector extends DefaultCoinSelector {
|
||||
@Override
|
||||
protected boolean shouldSelect(Transaction tx) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private Transaction cleanupCommon(Address destination) throws Exception {
|
||||
receiveATransaction(wallet, myAddress);
|
||||
|
||||
BigInteger v2 = toNanoCoins(0, 50);
|
||||
SendRequest req = SendRequest.to(destination, v2);
|
||||
req.fee = toNanoCoins(0, 1);
|
||||
wallet.completeTx(req);
|
||||
|
||||
Transaction t2 = req.tx;
|
||||
|
||||
// Broadcast the transaction and commit.
|
||||
broadcastAndCommit(wallet, t2);
|
||||
|
||||
// At this point we have one pending and one spent
|
||||
|
||||
BigInteger v1 = toNanoCoins(0, 10);
|
||||
Transaction t = sendMoneyToWallet(wallet, v1, myAddress, null);
|
||||
Threading.waitForUserCode();
|
||||
sendMoneyToWallet(wallet, t, null);
|
||||
assertEquals("Wrong number of PENDING.4", 2, wallet.getPoolSize(Pool.PENDING));
|
||||
assertEquals("Wrong number of UNSPENT.4", 0, wallet.getPoolSize(Pool.UNSPENT));
|
||||
assertEquals("Wrong number of ALL.4", 3, wallet.getTransactions(true).size());
|
||||
assertEquals(toNanoCoins(0, 59), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
|
||||
|
||||
// Now we have another incoming pending
|
||||
return t;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicSpendingWithEncryptedMixedWallet() throws Exception {
|
||||
basicSpendingCommon(encryptedMixedWallet, myEncryptedAddress2, new ECKey().toAddress(params), true);
|
||||
public void cleanup() throws Exception {
|
||||
Address destination = new ECKey().toAddress(params);
|
||||
Transaction t = cleanupCommon(destination);
|
||||
|
||||
// Consider the new pending as risky and remove it from the wallet
|
||||
wallet.setRiskAnalyzer(new TestRiskAnalysis.Analyzer(t));
|
||||
|
||||
wallet.cleanup();
|
||||
assertTrue(wallet.isConsistent());
|
||||
assertEquals("Wrong number of PENDING.5", 1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
|
||||
assertEquals("Wrong number of UNSPENT.5", 0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
|
||||
assertEquals("Wrong number of ALL.5", 2, wallet.getTransactions(true).size());
|
||||
assertEquals(toNanoCoins(0, 49), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanupFailsDueToSpend() throws Exception {
|
||||
Address destination = new ECKey().toAddress(params);
|
||||
Transaction t = cleanupCommon(destination);
|
||||
|
||||
// Now we have another incoming pending. Spend everything.
|
||||
BigInteger v3 = toNanoCoins(0, 58);
|
||||
SendRequest req = SendRequest.to(destination, v3);
|
||||
|
||||
// Force selection of the incoming coin so that we can spend it
|
||||
req.coinSelector = new TestCoinSelector();
|
||||
|
||||
req.fee = toNanoCoins(0, 1);
|
||||
wallet.completeTx(req);
|
||||
wallet.commitTx(req.tx);
|
||||
|
||||
assertEquals("Wrong number of PENDING.5", 3, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
|
||||
assertEquals("Wrong number of UNSPENT.5", 0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
|
||||
assertEquals("Wrong number of ALL.5", 4, wallet.getTransactions(true).size());
|
||||
|
||||
// Consider the new pending as risky and try to remove it from the wallet
|
||||
wallet.setRiskAnalyzer(new TestRiskAnalysis.Analyzer(t));
|
||||
|
||||
wallet.cleanup();
|
||||
assertTrue(wallet.isConsistent());
|
||||
|
||||
// The removal should have failed
|
||||
assertEquals("Wrong number of PENDING.5", 3, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
|
||||
assertEquals("Wrong number of UNSPENT.5", 0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
|
||||
assertEquals("Wrong number of ALL.5", 4, wallet.getTransactions(true).size());
|
||||
assertEquals(toNanoCoins(0, 0), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
|
||||
}
|
||||
|
||||
private void basicSpendingCommon(Wallet wallet, Address toAddress, Address destination, boolean testEncryption) throws Exception {
|
||||
@@ -825,7 +926,7 @@ public class WalletTest extends TestWithWallet {
|
||||
@Test
|
||||
public void transactionsList() throws Exception {
|
||||
// Check the wallet can give us an ordered list of all received transactions.
|
||||
Utils.rollMockClock(0);
|
||||
Utils.setMockClock();
|
||||
Transaction tx1 = sendMoneyToWallet(Utils.toNanoCoins(1, 0), AbstractBlockChain.NewBlockType.BEST_CHAIN);
|
||||
Utils.rollMockClock(60 * 10);
|
||||
Transaction tx2 = sendMoneyToWallet(Utils.toNanoCoins(0, 5), AbstractBlockChain.NewBlockType.BEST_CHAIN);
|
||||
@@ -863,7 +964,8 @@ public class WalletTest extends TestWithWallet {
|
||||
@Test
|
||||
public void keyCreationTime() throws Exception {
|
||||
wallet = new Wallet(params);
|
||||
long now = Utils.rollMockClock(0).getTime() / 1000; // Fix the mock clock.
|
||||
Utils.setMockClock();
|
||||
long now = Utils.currentTimeMillis() / 1000;
|
||||
// No keys returns current time.
|
||||
assertEquals(now, wallet.getEarliestKeyCreationTime());
|
||||
Utils.rollMockClock(60);
|
||||
@@ -877,7 +979,8 @@ public class WalletTest extends TestWithWallet {
|
||||
@Test
|
||||
public void scriptCreationTime() throws Exception {
|
||||
wallet = new Wallet(params);
|
||||
long now = Utils.rollMockClock(0).getTime() / 1000; // Fix the mock clock.
|
||||
Utils.setMockClock();
|
||||
long now = Utils.currentTimeMillis() / 1000;
|
||||
// No keys returns current time.
|
||||
assertEquals(now, wallet.getEarliestKeyCreationTime());
|
||||
Utils.rollMockClock(60);
|
||||
@@ -1109,9 +1212,24 @@ public class WalletTest extends TestWithWallet {
|
||||
|
||||
// Wait for an auto-save to occur.
|
||||
latch.await();
|
||||
assertFalse(hash4.equals(Sha256Hash.hashFileContents(f))); // File has now changed.
|
||||
Sha256Hash hash5 = Sha256Hash.hashFileContents(f);
|
||||
assertFalse(hash4.equals(hash5)); // File has now changed.
|
||||
assertNotNull(results[0]);
|
||||
assertEquals(f, results[1]);
|
||||
|
||||
// Now we shutdown auto-saving and expect wallet changes to remain unsaved, even "important" changes.
|
||||
wallet.shutdownAutosaveAndWait();
|
||||
results[0] = results[1] = null;
|
||||
ECKey key2 = new ECKey();
|
||||
wallet.addKey(key2);
|
||||
assertEquals(hash5, Sha256Hash.hashFileContents(f)); // File has NOT changed.
|
||||
Transaction t2 = createFakeTx(params, toNanoCoins(5, 0), key2);
|
||||
Block b3 = createFakeBlock(blockStore, t2).block;
|
||||
chain.add(b3);
|
||||
Thread.sleep(2000); // Wait longer than autosave delay. TODO Fix the racyness.
|
||||
assertEquals(hash5, Sha256Hash.hashFileContents(f)); // File has still NOT changed.
|
||||
assertNull(results[0]);
|
||||
assertNull(results[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1178,38 +1296,33 @@ public class WalletTest extends TestWithWallet {
|
||||
|
||||
@Test
|
||||
public void encryptionDecryptionBasic() throws Exception {
|
||||
encryptionDecryptionBasicCommon(encryptedWallet);
|
||||
encryptionDecryptionBasicCommon(encryptedMixedWallet);
|
||||
}
|
||||
|
||||
private void encryptionDecryptionBasicCommon(Wallet wallet) {
|
||||
// Check the wallet is initially of WalletType ENCRYPTED.
|
||||
assertTrue("Wallet is not an encrypted wallet", wallet.getEncryptionType() == EncryptionType.ENCRYPTED_SCRYPT_AES);
|
||||
assertTrue("Wallet is not an encrypted wallet", encryptedWallet.getEncryptionType() == EncryptionType.ENCRYPTED_SCRYPT_AES);
|
||||
|
||||
// Correct password should decrypt first encrypted private key.
|
||||
assertTrue("checkPassword result is wrong with correct password.2", wallet.checkPassword(PASSWORD1));
|
||||
assertTrue("checkPassword result is wrong with correct password.2", encryptedWallet.checkPassword(PASSWORD1));
|
||||
|
||||
// Incorrect password should not decrypt first encrypted private key.
|
||||
assertFalse("checkPassword result is wrong with incorrect password.3", wallet.checkPassword(WRONG_PASSWORD));
|
||||
assertFalse("checkPassword result is wrong with incorrect password.3", encryptedWallet.checkPassword(WRONG_PASSWORD));
|
||||
|
||||
// Decrypt wallet.
|
||||
assertTrue("The keyCrypter is missing but should not be", keyCrypter != null);
|
||||
wallet.decrypt(aesKey);
|
||||
encryptedWallet.decrypt(aesKey);
|
||||
|
||||
// Wallet should now be unencrypted.
|
||||
assertTrue("Wallet is not an unencrypted wallet", wallet.getKeyCrypter() == null);
|
||||
assertTrue("Wallet is not an unencrypted wallet", encryptedWallet.getKeyCrypter() == null);
|
||||
|
||||
// Correct password should not decrypt first encrypted private key as wallet is unencrypted.
|
||||
assertTrue("checkPassword result is wrong with correct password", !wallet.checkPassword(PASSWORD1));
|
||||
assertTrue("checkPassword result is wrong with correct password", !encryptedWallet.checkPassword(PASSWORD1));
|
||||
|
||||
// Incorrect password should not decrypt first encrypted private key as wallet is unencrypted.
|
||||
assertTrue("checkPassword result is wrong with incorrect password", !wallet.checkPassword(WRONG_PASSWORD));
|
||||
assertTrue("checkPassword result is wrong with incorrect password", !encryptedWallet.checkPassword(WRONG_PASSWORD));
|
||||
|
||||
// Encrypt wallet.
|
||||
wallet.encrypt(keyCrypter, aesKey);
|
||||
encryptedWallet.encrypt(keyCrypter, aesKey);
|
||||
|
||||
// Wallet should now be of type WalletType.ENCRYPTED_SCRYPT_AES.
|
||||
assertTrue("Wallet is not an encrypted wallet", wallet.getEncryptionType() == EncryptionType.ENCRYPTED_SCRYPT_AES);
|
||||
assertTrue("Wallet is not an encrypted wallet", encryptedWallet.getEncryptionType() == EncryptionType.ENCRYPTED_SCRYPT_AES);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1260,6 +1373,19 @@ public class WalletTest extends TestWithWallet {
|
||||
assertTrue("Wallet is not an encrypted wallet.3", encryptedWallet.getEncryptionType() == EncryptionType.ENCRYPTED_SCRYPT_AES);
|
||||
}
|
||||
|
||||
@Test(expected = KeyCrypterException.class)
|
||||
public void addUnencryptedKeyToEncryptedWallet() throws Exception {
|
||||
ECKey key1 = new ECKey();
|
||||
encryptedWallet.addKey(key1);
|
||||
}
|
||||
|
||||
@Test(expected = KeyCrypterException.class)
|
||||
public void addEncryptedKeyToUnencryptedWallet() throws Exception {
|
||||
ECKey key1 = new ECKey();
|
||||
key1 = key1.encrypt(keyCrypter, keyCrypter.deriveKey("PASSWORD!"));
|
||||
wallet.addKey(key1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encryptionDecryptionHomogenousKeys() throws Exception {
|
||||
// Check the wallet is currently encrypted
|
||||
@@ -2040,6 +2166,7 @@ public class WalletTest extends TestWithWallet {
|
||||
|
||||
@Test
|
||||
public void keyRotation() throws Exception {
|
||||
Utils.setMockClock();
|
||||
// Watch out for wallet-initiated broadcasts.
|
||||
MockTransactionBroadcaster broadcaster = new MockTransactionBroadcaster(wallet);
|
||||
wallet.setTransactionBroadcaster(broadcaster);
|
||||
@@ -2112,6 +2239,7 @@ public class WalletTest extends TestWithWallet {
|
||||
ECKey key = new ECKey();
|
||||
wallet.addKey(key);
|
||||
Address address = key.toAddress(params);
|
||||
Utils.setMockClock();
|
||||
Utils.rollMockClock(86400);
|
||||
for (int i = 0; i < 800; i++) {
|
||||
sendMoneyToWallet(wallet, Utils.CENT, address, AbstractBlockChain.NewBlockType.BEST_CHAIN);
|
||||
|
@@ -35,7 +35,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A test with test vectors as per BIP 32 spec: https://en.bitcoin.it/wiki/BIP_0032#Test_Vectors
|
||||
* A test with test vectors as per BIP 32 spec: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Test_Vectors
|
||||
*/
|
||||
public class BIP32Test {
|
||||
private static final Logger log = LoggerFactory.getLogger(BIP32Test.class);
|
||||
|
@@ -63,6 +63,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
Utils.setMockClock(); // Use mock clock
|
||||
sendMoneyToWallet(Utils.COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN);
|
||||
sendMoneyToWallet(Utils.COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN);
|
||||
wallet.addExtension(new StoredPaymentChannelClientStates(wallet, failBroadcaster));
|
||||
@@ -267,7 +268,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
@Test
|
||||
public void testChannelResume() throws Exception {
|
||||
// Tests various aspects of channel resuming.
|
||||
Utils.rollMockClock(0);
|
||||
Utils.setMockClock();
|
||||
|
||||
final Sha256Hash someServerId = Sha256Hash.create(new byte[]{});
|
||||
|
||||
|
@@ -114,7 +114,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
public void basic() throws Exception {
|
||||
// Check it all works when things are normal (no attacks, no problems).
|
||||
|
||||
Utils.rollMockClock(0); // Use mock clock
|
||||
Utils.setMockClock(); // Use mock clock
|
||||
final long EXPIRE_TIME = Utils.currentTimeMillis()/1000 + 60*60*24;
|
||||
|
||||
serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME);
|
||||
@@ -228,7 +228,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
StoredPaymentChannelClientStates stateStorage = new StoredPaymentChannelClientStates(wallet, mockBroadcaster);
|
||||
wallet.addOrUpdateExtension(stateStorage);
|
||||
|
||||
Utils.rollMockClock(0); // Use mock clock
|
||||
Utils.setMockClock(); // Use mock clock
|
||||
final long EXPIRE_TIME = Utils.currentTimeMillis()/1000 + 60*60*24;
|
||||
|
||||
serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME);
|
||||
@@ -330,7 +330,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
|
||||
// We'll broadcast only one tx: multisig contract
|
||||
|
||||
Utils.rollMockClock(0); // Use mock clock
|
||||
Utils.setMockClock(); // Use mock clock
|
||||
final long EXPIRE_TIME = Utils.currentTimeMillis()/1000 + 60*60*24;
|
||||
|
||||
serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME);
|
||||
@@ -541,7 +541,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
chain.add(makeSolvedTestBlock(blockStore.getChainHead().getHeader(), createFakeTx(params, Utils.CENT, myAddress)));
|
||||
assertEquals(Utils.CENT, wallet.getBalance());
|
||||
|
||||
Utils.rollMockClock(0); // Use mock clock
|
||||
Utils.setMockClock(); // Use mock clock
|
||||
final long EXPIRE_TIME = Utils.currentTimeMillis()/1000 + 60*60*24;
|
||||
|
||||
serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME);
|
||||
@@ -644,7 +644,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
public void serverAddsFeeTest() throws Exception {
|
||||
// Test that the server properly adds the necessary fee at the end (or just drops the payment if its not worth it)
|
||||
|
||||
Utils.rollMockClock(0); // Use mock clock
|
||||
Utils.setMockClock(); // Use mock clock
|
||||
final long EXPIRE_TIME = Utils.currentTimeMillis()/1000 + 60*60*24;
|
||||
|
||||
serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME);
|
||||
@@ -730,7 +730,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
// Tests that if the client double-spends the multisig contract after it is sent, no more payments are accepted
|
||||
|
||||
// Start with a copy of basic()....
|
||||
Utils.rollMockClock(0); // Use mock clock
|
||||
Utils.setMockClock(); // Use mock clock
|
||||
final long EXPIRE_TIME = Utils.currentTimeMillis()/1000 + 60*60*24;
|
||||
|
||||
serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME);
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -82,6 +83,23 @@ public class PaymentSessionTest {
|
||||
assertTrue(refundScript.equals(payment.getRefundTo(0).getScript()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaults() throws Exception {
|
||||
Protos.Output.Builder outputBuilder = Protos.Output.newBuilder()
|
||||
.setScript(ByteString.copyFrom(outputToMe.getScriptBytes()));
|
||||
Protos.PaymentDetails paymentDetails = Protos.PaymentDetails.newBuilder()
|
||||
.setTime(time)
|
||||
.addOutputs(outputBuilder)
|
||||
.build();
|
||||
Protos.PaymentRequest paymentRequest = Protos.PaymentRequest.newBuilder()
|
||||
.setSerializedPaymentDetails(paymentDetails.toByteString())
|
||||
.build();
|
||||
MockPaymentSession paymentSession = new MockPaymentSession(paymentRequest);
|
||||
assertEquals(BigInteger.ZERO, paymentSession.getValue());
|
||||
assertNull(paymentSession.getPaymentUrl());
|
||||
assertNull(paymentSession.getMemo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpiredPaymentRequest() throws Exception {
|
||||
MockPaymentSession paymentSession = new MockPaymentSession(newExpiredPaymentRequest());
|
||||
@@ -108,6 +126,7 @@ public class PaymentSessionTest {
|
||||
MockPaymentSession paymentSession = new MockPaymentSession(paymentRequest);
|
||||
PaymentSession.PkiVerificationData pkiData = paymentSession.verifyPki();
|
||||
assertEquals("www.bitcoincore.org", pkiData.name);
|
||||
assertEquals("The USERTRUST Network, Salt Lake City, US", pkiData.rootAuthorityName);
|
||||
}
|
||||
|
||||
private Protos.PaymentRequest newSimplePaymentRequest() {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -192,10 +193,15 @@ public class ScriptTest {
|
||||
if (line.trim().endsWith("],") || line.trim().equals("]")) {
|
||||
String[] scripts = script.split(",");
|
||||
try {
|
||||
Script scriptSig = parseScriptString(scripts[0].replaceAll("[\"\\[\\]]", "").trim());
|
||||
Script scriptPubKey = parseScriptString(scripts[1].replaceAll("[\"\\[\\]]", "").trim());
|
||||
scripts[0] = scripts[0].replaceAll("[\"\\[\\]]", "").trim();
|
||||
scripts[1] = scripts[1].replaceAll("[\"\\[\\]]", "").trim();
|
||||
Script scriptSig = parseScriptString(scripts[0]);
|
||||
Script scriptPubKey = parseScriptString(scripts[1]);
|
||||
|
||||
scriptSig.correctlySpends(new Transaction(params), 0, scriptPubKey, true);
|
||||
System.err.println("scriptSig: " + scripts[0]);
|
||||
System.err.println("scriptPubKey: " + scripts[1]);
|
||||
System.err.flush();
|
||||
fail();
|
||||
} catch (VerificationException e) {
|
||||
// Expected.
|
||||
|
@@ -1,3 +1,20 @@
|
||||
/**
|
||||
* Copyright 2012 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.bitcoin.store;
|
||||
|
||||
|
||||
@@ -246,6 +263,8 @@ public class WalletProtobufSerializerTest {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
//System.out.println(WalletProtobufSerializer.walletToText(wallet));
|
||||
new WalletProtobufSerializer().writeWallet(wallet, output);
|
||||
ByteArrayInputStream test = new ByteArrayInputStream(output.toByteArray());
|
||||
assertTrue(WalletProtobufSerializer.isWallet(test));
|
||||
ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
|
||||
return new WalletProtobufSerializer().readWallet(input);
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
* Copyright 2012, 2014 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -271,38 +271,16 @@ public class BitcoinURITest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a badly formatted label field
|
||||
*
|
||||
* @throws BitcoinURIParseException
|
||||
* If something goes wrong
|
||||
*/
|
||||
@Test
|
||||
public void testBad_Label() throws BitcoinURIParseException {
|
||||
try {
|
||||
testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
|
||||
+ "?label=");
|
||||
fail("Expecting BitcoinURIParseException");
|
||||
} catch (BitcoinURIParseException e) {
|
||||
assertTrue(e.getMessage().contains("label"));
|
||||
}
|
||||
public void testEmpty_Label() throws BitcoinURIParseException {
|
||||
assertNull(new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
|
||||
+ "?label=").getLabel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a badly formatted message field
|
||||
*
|
||||
* @throws BitcoinURIParseException
|
||||
* If something goes wrong
|
||||
*/
|
||||
@Test
|
||||
public void testBad_Message() throws BitcoinURIParseException {
|
||||
try {
|
||||
testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
|
||||
+ "?message=");
|
||||
fail("Expecting BitcoinURIParseException");
|
||||
} catch (BitcoinURIParseException e) {
|
||||
assertTrue(e.getMessage().contains("message"));
|
||||
}
|
||||
public void testEmpty_Message() throws BitcoinURIParseException {
|
||||
assertNull(new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
|
||||
+ "?message=").getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,21 +300,10 @@ public class BitcoinURITest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles case when there are too many equals
|
||||
*
|
||||
* @throws BitcoinURIParseException
|
||||
* If something goes wrong
|
||||
*/
|
||||
@Test
|
||||
public void testBad_TooManyEquals() throws BitcoinURIParseException {
|
||||
try {
|
||||
testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
|
||||
+ "?label=aardvark=zebra");
|
||||
fail("Expecting BitcoinURIParseException");
|
||||
} catch (BitcoinURIParseException e) {
|
||||
assertTrue(e.getMessage().contains("cannot parse name value pair"));
|
||||
}
|
||||
public void testGood_ManyEquals() throws BitcoinURIParseException {
|
||||
assertEquals("aardvark=zebra", new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":"
|
||||
+ MAINNET_GOOD_ADDRESS + "?label=aardvark=zebra").getLabel());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,7 +344,7 @@ public class BitcoinURITest {
|
||||
+ "?aardvark");
|
||||
fail("Expecting BitcoinURIParseException");
|
||||
} catch (BitcoinURIParseException e) {
|
||||
assertTrue(e.getMessage().contains("cannot parse name value pair"));
|
||||
assertTrue(e.getMessage().contains("no separator"));
|
||||
}
|
||||
|
||||
// Unknown and required field
|
||||
|
@@ -39,6 +39,7 @@ public class DefaultCoinSelectorTest extends TestWithWallet {
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
Utils.setMockClock(); // Use mock clock
|
||||
}
|
||||
|
||||
@After
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +17,10 @@
|
||||
|
||||
package com.google.bitcoin.wallet;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import com.google.bitcoin.core.*;
|
||||
import com.google.bitcoin.params.UnitTestParams;
|
||||
import com.google.bitcoin.params.MainNetParams;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -27,7 +30,8 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class DefaultRiskAnalysisTest {
|
||||
private static final NetworkParameters params = UnitTestParams.get();
|
||||
// Uses mainnet because isStandard checks are disabled on testnet.
|
||||
private static final NetworkParameters params = MainNetParams.get();
|
||||
private Wallet wallet;
|
||||
private final int TIMESTAMP = 1384190189;
|
||||
private ECKey key1;
|
||||
@@ -119,4 +123,22 @@ public class DefaultRiskAnalysisTest {
|
||||
assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze());
|
||||
assertEquals(tx1, analysis.getNonFinal());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonStandardDust() {
|
||||
Transaction standardTx = new Transaction(params);
|
||||
standardTx.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0));
|
||||
standardTx.addOutput(Utils.COIN, key1);
|
||||
assertEquals(RiskAnalysis.Result.OK, DefaultRiskAnalysis.FACTORY.create(wallet, standardTx, NO_DEPS).analyze());
|
||||
|
||||
Transaction dustTx = new Transaction(params);
|
||||
dustTx.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0));
|
||||
dustTx.addOutput(BigInteger.ONE, key1); // 1 Satoshi
|
||||
assertEquals(RiskAnalysis.Result.NON_STANDARD, DefaultRiskAnalysis.FACTORY.create(wallet, dustTx, NO_DEPS).analyze());
|
||||
|
||||
Transaction edgeCaseTx = new Transaction(params);
|
||||
edgeCaseTx.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0));
|
||||
edgeCaseTx.addOutput(DefaultRiskAnalysis.MIN_ANALYSIS_NONDUST_OUTPUT, key1); // Dust threshold
|
||||
assertEquals(RiskAnalysis.Result.OK, DefaultRiskAnalysis.FACTORY.create(wallet, edgeCaseTx, NO_DEPS).analyze());
|
||||
}
|
||||
}
|
||||
|
@@ -325,5 +325,7 @@
|
||||
["NOP1 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"],
|
||||
|
||||
["0 0x01 0x50", "HASH160 0x14 0xece424a6bb6ddf4db592c0faed60685047a361b1 EQUAL", "OP_RESERVED in P2SH should fail"],
|
||||
["0 0x01 VER", "HASH160 0x14 0x0f4d7845db968f2a81b530b6f3c1d6246d4c7e01 EQUAL", "OP_VER in P2SH should fail"]
|
||||
["0 0x01 VER", "HASH160 0x14 0x0f4d7845db968f2a81b530b6f3c1d6246d4c7e01 EQUAL", "OP_VER in P2SH should fail"],
|
||||
|
||||
["0x00", "'00' EQUAL", "Basic OP_0 execution"]
|
||||
]
|
||||
|
@@ -411,5 +411,7 @@
|
||||
|
||||
["0x4c 0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242",
|
||||
"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL",
|
||||
"Basic PUSHDATA1 signedness check"]
|
||||
"Basic PUSHDATA1 signedness check"],
|
||||
|
||||
["0x00", "SIZE 0 EQUAL", "Basic OP_0 execution"]
|
||||
]
|
||||
|
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.google</groupId>
|
||||
<artifactId>bitcoinj-parent</artifactId>
|
||||
<version>0.11-SNAPSHOT</version>
|
||||
<version>0.11.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@@ -43,7 +43,7 @@ public class ForwardingService {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// This line makes the log output more compact and easily read, especially when using the JDK log adapter.
|
||||
BriefLogFormatter.init();
|
||||
if (args.length < 2) {
|
||||
if (args.length < 1) {
|
||||
System.err.println("Usage: address-to-send-back-to [regtest|testnet]");
|
||||
return;
|
||||
}
|
||||
@@ -51,10 +51,10 @@ public class ForwardingService {
|
||||
// Figure out which network we should connect to. Each one gets its own set of files.
|
||||
NetworkParameters params;
|
||||
String filePrefix;
|
||||
if (args[1].equals("testnet")) {
|
||||
if (args.length > 1 && args[1].equals("testnet")) {
|
||||
params = TestNet3Params.get();
|
||||
filePrefix = "forwarding-service-testnet";
|
||||
} else if (args[1].equals("regtest")) {
|
||||
} else if (args.length > 1 && args[1].equals("regtest")) {
|
||||
params = RegTestParams.get();
|
||||
filePrefix = "forwarding-service-regtest";
|
||||
} else {
|
||||
|
2
pom.xml
2
pom.xml
@@ -4,7 +4,7 @@
|
||||
|
||||
<groupId>com.google</groupId>
|
||||
<artifactId>bitcoinj-parent</artifactId>
|
||||
<version>0.11-SNAPSHOT</version>
|
||||
<version>0.11.2</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
|
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.google</groupId>
|
||||
<artifactId>bitcoinj-parent</artifactId>
|
||||
<version>0.11-SNAPSHOT</version>
|
||||
<version>0.11.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@@ -539,6 +539,7 @@ public class WalletTool {
|
||||
System.out.println("Pki-Verified Name: " + session.pkiVerificationData.name);
|
||||
if (session.pkiVerificationData.orgName != null)
|
||||
System.out.println("Pki-Verified Org: " + session.pkiVerificationData.orgName);
|
||||
System.out.println("PKI data verified by: " + session.pkiVerificationData.rootAuthorityName);
|
||||
}
|
||||
final Wallet.SendRequest req = session.getSendRequest();
|
||||
if (password != null) {
|
||||
|
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>org.bitcoinj</groupId>
|
||||
<artifactId>wallettemplate</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<version>1.0</version>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
@@ -27,7 +27,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google</groupId>
|
||||
<artifactId>bitcoinj</artifactId>
|
||||
<version>0.11-SNAPSHOT</version>
|
||||
<version>0.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
@@ -37,7 +37,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>13.0</version>
|
||||
<version>13.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aquafx-project</groupId>
|
||||
|
@@ -36,7 +36,7 @@ public class SendMoneyController {
|
||||
try {
|
||||
Address destination = new Address(Main.params, address.getText());
|
||||
Wallet.SendRequest req = Wallet.SendRequest.emptyWallet(destination);
|
||||
Main.bitcoin.wallet().sendCoins(req);
|
||||
sendResult = Main.bitcoin.wallet().sendCoins(req);
|
||||
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>() {
|
||||
@Override
|
||||
public void onSuccess(Transaction result) {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?scenebuilder-classpath-element ../../../../target/classes?>
|
||||
<?scenebuilder-classpath-element ../../../../../core/target/bitcoinj-0.11-SNAPSHOT.jar?>
|
||||
<?scenebuilder-classpath-element ../../../../../core/target/bitcoinj-0.11.jar?>
|
||||
<?import java.lang.*?>
|
||||
<?import java.util.*?>
|
||||
<?import javafx.geometry.*?>
|
||||
|
Reference in New Issue
Block a user