mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-01 21:17:13 +00:00
Merge trunk into peerroup
This commit is contained in:
3
AUTHORS
3
AUTHORS
@@ -5,4 +5,5 @@ Thilo Planz <thilo@cpan.org>
|
|||||||
Micheal Swiggs <bobby.simpson87@gmail.com>
|
Micheal Swiggs <bobby.simpson87@gmail.com>
|
||||||
Gary Rowe <g.rowe@froot.co.uk>
|
Gary Rowe <g.rowe@froot.co.uk>
|
||||||
Noa Resare <noa@resare.com>
|
Noa Resare <noa@resare.com>
|
||||||
John Sample <jwsample@gmail.com>
|
John Sample <jwsample@gmail.com>
|
||||||
|
Jan Møller <jan.moller@gmail.com>
|
||||||
33
pom.xml
33
pom.xml
@@ -3,7 +3,8 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.google</groupId>
|
<groupId>com.google</groupId>
|
||||||
<artifactId>bitcoinj</artifactId>
|
<artifactId>bitcoinj</artifactId>
|
||||||
<version>0.2</version>
|
<!-- This should always be a SNAPSHOT until release build -->
|
||||||
|
<version>0.3-SNAPSHOT</version>
|
||||||
<name>BitCoinJ</name>
|
<name>BitCoinJ</name>
|
||||||
<description>A Java implementation of a Bitcoin client-only node</description>
|
<description>A Java implementation of a Bitcoin client-only node</description>
|
||||||
<url>http://code.google.com/p/bitcoinj</url>
|
<url>http://code.google.com/p/bitcoinj</url>
|
||||||
@@ -82,6 +83,36 @@
|
|||||||
<!-- Public read-only source -->
|
<!-- Public read-only source -->
|
||||||
<url>http://bitcoinj.googlecode.com/svn/trunk/</url>
|
<url>http://bitcoinj.googlecode.com/svn/trunk/</url>
|
||||||
</scm>
|
</scm>
|
||||||
|
<!-- Distribution management handled by build server (no external authentication) -->
|
||||||
|
<distributionManagement>
|
||||||
|
<repository>
|
||||||
|
<id>central</id>
|
||||||
|
<name>repo</name>
|
||||||
|
<url>http://localhost:8081/artifactory/internal-release</url>
|
||||||
|
</repository>
|
||||||
|
<snapshotRepository>
|
||||||
|
<id>snapshots</id>
|
||||||
|
<name>repo</name>
|
||||||
|
<url>http://localhost:8081/artifactory/internal-snapshot</url>
|
||||||
|
</snapshotRepository>
|
||||||
|
</distributionManagement>
|
||||||
|
<!-- Include the BitCoinJ repositories in your project if required -->
|
||||||
|
<repositories>
|
||||||
|
<!-- Released versions of the library are stable -->
|
||||||
|
<repository>
|
||||||
|
<id>bitcoinj-release</id>
|
||||||
|
<name>BitCoinJ Release</name>
|
||||||
|
<releases />
|
||||||
|
<url>http://ci.bitcoinj.org/artifactory/internal-release</url>
|
||||||
|
</repository>
|
||||||
|
<!-- Snapshot versions of the library are subject to frequent change -->
|
||||||
|
<repository>
|
||||||
|
<id>bitcoinj-snapshot</id>
|
||||||
|
<name>BitCoinJ Snapshot</name>
|
||||||
|
<snapshots />
|
||||||
|
<url>http://ci.bitcoinj.org/artifactory/internal-snapshot</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>maven-3</id>
|
<id>maven-3</id>
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ public class BitcoinSerializer
|
|||||||
private boolean usesChecksumming;
|
private boolean usesChecksumming;
|
||||||
|
|
||||||
private static Map<Class<? extends Message>, String> names = new HashMap<Class<? extends Message>,String>();
|
private static Map<Class<? extends Message>, String> names = new HashMap<Class<? extends Message>,String>();
|
||||||
private static Map<String, Constructor<? extends Message>>
|
|
||||||
messageConstructors = new HashMap<String, Constructor<? extends Message>>();
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
names.put(VersionMessage.class, "version");
|
names.put(VersionMessage.class, "version");
|
||||||
@@ -75,14 +73,6 @@ public class BitcoinSerializer
|
|||||||
public BitcoinSerializer(NetworkParameters params, boolean usesChecksumming) {
|
public BitcoinSerializer(NetworkParameters params, boolean usesChecksumming) {
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.usesChecksumming = usesChecksumming;
|
this.usesChecksumming = usesChecksumming;
|
||||||
|
|
||||||
// some Message subclasses can only be sent for now, ignore missing constructors
|
|
||||||
for (Class<? extends Message> c : names.keySet()) {
|
|
||||||
Constructor<? extends Message> ct = makeConstructor(c);
|
|
||||||
if (ct != null) {
|
|
||||||
messageConstructors.put(names.get(c),ct);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void useChecksumming(boolean usesChecksumming) {
|
public void useChecksumming(boolean usesChecksumming) {
|
||||||
@@ -121,7 +111,8 @@ public class BitcoinSerializer
|
|||||||
out.write(header);
|
out.write(header);
|
||||||
out.write(payload);
|
out.write(payload);
|
||||||
|
|
||||||
log.debug("Sending {} message: {}", name, bytesToHexString(header) + bytesToHexString(payload));
|
if (log.isDebugEnabled())
|
||||||
|
log.debug("Sending {} message: {}", name, bytesToHexString(header) + bytesToHexString(payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -216,17 +207,36 @@ public class BitcoinSerializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Constructor<? extends Message> c = messageConstructors.get(command);
|
return makeMessage(command, payloadBytes);
|
||||||
if (c == null) {
|
|
||||||
throw new ProtocolException("No support for deserializing message with name " + command);
|
|
||||||
}
|
|
||||||
return c.newInstance(params, payloadBytes);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ProtocolException("Error deserializing message " + Utils.bytesToHexString(payloadBytes) + "\n", e);
|
throw new ProtocolException("Error deserializing message " + Utils.bytesToHexString(payloadBytes) + "\n", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Message makeMessage(String command, byte[] payloadBytes) throws ProtocolException {
|
||||||
|
// We use an if ladder rather than reflection because reflection is very slow on Android.
|
||||||
|
if (command.equals("version")) {
|
||||||
|
return new VersionMessage(params, payloadBytes);
|
||||||
|
} else if (command.equals("inv")) {
|
||||||
|
return new InventoryMessage(params, payloadBytes);
|
||||||
|
} else if (command.equals("block")) {
|
||||||
|
return new Block(params, payloadBytes);
|
||||||
|
} else if (command.equals("getdata")) {
|
||||||
|
return new GetDataMessage(params, payloadBytes);
|
||||||
|
} else if (command.equals("tx")) {
|
||||||
|
return new Transaction(params, payloadBytes);
|
||||||
|
} else if (command.equals("addr")) {
|
||||||
|
return new AddressMessage(params, payloadBytes);
|
||||||
|
} else if (command.equals("ping")) {
|
||||||
|
return new Ping();
|
||||||
|
} else if (command.equals("verack")) {
|
||||||
|
return new VersionAck(params, payloadBytes);
|
||||||
|
} else {
|
||||||
|
throw new ProtocolException("No support for deserializing message with name " + command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Constructor<? extends Message> makeConstructor(Class<? extends Message> c) {
|
private Constructor<? extends Message> makeConstructor(Class<? extends Message> c) {
|
||||||
Class<?> parTypes[] = new Class<?>[2];
|
Class<?> parTypes[] = new Class<?>[2];
|
||||||
parTypes[0] = NetworkParameters.class;
|
parTypes[0] = NetworkParameters.class;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import static com.google.bitcoin.core.Utils.*;
|
|||||||
* over its contents. See the BitCoin technical paper for more detail on blocks.<p>
|
* over its contents. See the BitCoin technical paper for more detail on blocks.<p>
|
||||||
*
|
*
|
||||||
* To get a block, you can either build one from the raw bytes you can get from another implementation,
|
* To get a block, you can either build one from the raw bytes you can get from another implementation,
|
||||||
* or request one specifically using {@link Peer#getBlock(byte[])}, or grab one from a downloaded {@link BlockChain}.
|
* or request one specifically using {@link Peer#getBlock(Sha256Hash)}, or grab one from a downloaded {@link BlockChain}.
|
||||||
*/
|
*/
|
||||||
public class Block extends Message {
|
public class Block extends Message {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Block.class);
|
private static final Logger log = LoggerFactory.getLogger(Block.class);
|
||||||
@@ -49,12 +49,13 @@ public class Block extends Message {
|
|||||||
|
|
||||||
// For unit testing. If not zero, use this instead of the current time.
|
// For unit testing. If not zero, use this instead of the current time.
|
||||||
static long fakeClock = 0;
|
static long fakeClock = 0;
|
||||||
|
|
||||||
|
// Fields defined as part of the protocol format.
|
||||||
private long version;
|
private long version;
|
||||||
private Sha256Hash prevBlockHash;
|
private Sha256Hash prevBlockHash;
|
||||||
private Sha256Hash merkleRoot;
|
private Sha256Hash merkleRoot;
|
||||||
private long time;
|
private long time;
|
||||||
private long difficultyTarget; // "nBits"
|
private long difficultyTarget; // "nBits"
|
||||||
|
|
||||||
private long nonce;
|
private long nonce;
|
||||||
|
|
||||||
/** If null, it means this object holds only the headers. */
|
/** If null, it means this object holds only the headers. */
|
||||||
@@ -62,7 +63,7 @@ public class Block extends Message {
|
|||||||
/** Stores the hash of the block. If null, getHash() will recalculate it. */
|
/** Stores the hash of the block. If null, getHash() will recalculate it. */
|
||||||
private transient Sha256Hash hash;
|
private transient Sha256Hash hash;
|
||||||
|
|
||||||
/** Special case constructor, used for the genesis node and unit tests. */
|
/** Special case constructor, used for the genesis node, cloneAsHeader and unit tests. */
|
||||||
Block(NetworkParameters params) {
|
Block(NetworkParameters params) {
|
||||||
super(params);
|
super(params);
|
||||||
// Set up a few basic things. We are not complete after this though.
|
// Set up a few basic things. We are not complete after this though.
|
||||||
@@ -169,14 +170,16 @@ public class Block extends Message {
|
|||||||
|
|
||||||
/** Returns a copy of the block, but without any transactions. */
|
/** Returns a copy of the block, but without any transactions. */
|
||||||
public Block cloneAsHeader() {
|
public Block cloneAsHeader() {
|
||||||
try {
|
Block block = new Block(params);
|
||||||
Block block = new Block(params, bitcoinSerialize());
|
block.nonce = nonce;
|
||||||
block.transactions = null;
|
block.prevBlockHash = prevBlockHash.clone();
|
||||||
return block;
|
block.merkleRoot = getMerkleRoot().clone();
|
||||||
} catch (ProtocolException e) {
|
block.version = version;
|
||||||
// Should not be able to happen unless our state is internally inconsistent.
|
block.time = time;
|
||||||
throw new RuntimeException(e);
|
block.difficultyTarget = difficultyTarget;
|
||||||
}
|
block.transactions = null;
|
||||||
|
block.hash = getHash().clone();
|
||||||
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -345,13 +348,13 @@ public class Block extends Message {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the block data to ensure it follows the rules laid out in the network parameters. Specifically, throws
|
* Checks the block data to ensure it follows the rules laid out in the network parameters. Specifically, throws
|
||||||
* an exception if the proof of work is invalid, if the timestamp is too far from what it should be, or if the
|
* an exception if the proof of work is invalid, or if the timestamp is too far from what it should be. This is
|
||||||
* transactions don't hash to the value in the merkle root field. This is <b>not</b> everything that is required
|
* <b>not</b> everything that is required for a block to be valid, only what is checkable independent of the
|
||||||
* for a block to be valid, only what is checkable independent of the chain.
|
* chain and without a transaction index.
|
||||||
*
|
*
|
||||||
* @throws VerificationException
|
* @throws VerificationException
|
||||||
*/
|
*/
|
||||||
public void verify() throws VerificationException {
|
public void verifyHeader() throws VerificationException {
|
||||||
// Prove that this block is OK. It might seem that we can just ignore most of these checks given that the
|
// Prove that this block is OK. It might seem that we can just ignore most of these checks given that the
|
||||||
// network is also verifying the blocks, but we cannot as it'd open us to a variety of obscure attacks.
|
// network is also verifying the blocks, but we cannot as it'd open us to a variety of obscure attacks.
|
||||||
//
|
//
|
||||||
@@ -359,15 +362,28 @@ public class Block extends Message {
|
|||||||
// enough, it's probably been done by the network.
|
// enough, it's probably been done by the network.
|
||||||
checkProofOfWork(true);
|
checkProofOfWork(true);
|
||||||
checkTimestamp();
|
checkTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the block contents
|
||||||
|
* @throws VerificationException
|
||||||
|
*/
|
||||||
|
public void verifyTransactions() throws VerificationException {
|
||||||
// Now we need to check that the body of the block actually matches the headers. The network won't generate
|
// Now we need to check that the body of the block actually matches the headers. The network won't generate
|
||||||
// an invalid block, but if we didn't validate this then an untrusted man-in-the-middle could obtain the next
|
// an invalid block, but if we didn't validate this then an untrusted man-in-the-middle could obtain the next
|
||||||
// valid block from the network and simply replace the transactions in it with their own fictional
|
// valid block from the network and simply replace the transactions in it with their own fictional
|
||||||
// transactions that reference spent or non-existant inputs.
|
// transactions that reference spent or non-existant inputs.
|
||||||
if (transactions != null) {
|
assert transactions.size() > 0;
|
||||||
assert transactions.size() > 0;
|
checkTransactions();
|
||||||
checkTransactions();
|
checkMerkleRoot();
|
||||||
checkMerkleRoot();
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Verifies both the header and that the transactions hash to the merkle root.
|
||||||
|
*/
|
||||||
|
public void verify() throws VerificationException {
|
||||||
|
verifyHeader();
|
||||||
|
verifyTransactions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -487,7 +503,7 @@ public class Block extends Message {
|
|||||||
b.setTime(time);
|
b.setTime(time);
|
||||||
b.solve();
|
b.solve();
|
||||||
try {
|
try {
|
||||||
b.verify();
|
b.verifyHeader();
|
||||||
} catch (VerificationException e) {
|
} catch (VerificationException e) {
|
||||||
throw new RuntimeException(e); // Cannot happen.
|
throw new RuntimeException(e); // Cannot happen.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public class BlockChain {
|
|||||||
protected StoredBlock chainHead;
|
protected StoredBlock chainHead;
|
||||||
|
|
||||||
protected final NetworkParameters params;
|
protected final NetworkParameters params;
|
||||||
protected final Wallet wallet;
|
protected final List<Wallet> wallets;
|
||||||
|
|
||||||
// Holds blocks that we have received but can't plug into the chain yet, eg because they were created whilst we
|
// Holds blocks that we have received but can't plug into the chain yet, eg because they were created whilst we
|
||||||
// were downloading the block chain.
|
// were downloading the block chain.
|
||||||
@@ -80,6 +80,23 @@ public class BlockChain {
|
|||||||
* {@link com.google.bitcoin.store.BoundedOverheadBlockStore} if you'd like to ensure fast startup the next time you run the program.
|
* {@link com.google.bitcoin.store.BoundedOverheadBlockStore} if you'd like to ensure fast startup the next time you run the program.
|
||||||
*/
|
*/
|
||||||
public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) {
|
public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) {
|
||||||
|
this(params, new ArrayList<Wallet>(), blockStore);
|
||||||
|
if (wallet != null)
|
||||||
|
addWallet(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a BlockChain that has no wallet at all. This is helpful when you don't actually care about sending
|
||||||
|
* and receiving coins but rather, just want to explore the network data structures.
|
||||||
|
*/
|
||||||
|
public BlockChain(NetworkParameters params, BlockStore blockStore) {
|
||||||
|
this(params, new ArrayList<Wallet>(), blockStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a BlockChain connected to the given list of wallets and a store.
|
||||||
|
*/
|
||||||
|
public BlockChain(NetworkParameters params, List<Wallet> wallets, BlockStore blockStore){
|
||||||
try {
|
try {
|
||||||
this.blockStore = blockStore;
|
this.blockStore = blockStore;
|
||||||
chainHead = blockStore.getChainHead();
|
chainHead = blockStore.getChainHead();
|
||||||
@@ -87,9 +104,17 @@ public class BlockChain {
|
|||||||
} catch (BlockStoreException e) {
|
} catch (BlockStoreException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.wallet = wallet;
|
this.wallets = new ArrayList<Wallet>(wallets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a wallet to the BlockChain. Note that the wallet will be unaffected by any blocks received while it
|
||||||
|
* was not part of this BlockChain. This method is useful if the wallet has just been created, and its keys
|
||||||
|
* have never been in use, or if the wallet has been loaded along with the BlockChain
|
||||||
|
*/
|
||||||
|
public synchronized void addWallet(Wallet wallet) {
|
||||||
|
wallets.add(wallet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,12 +149,27 @@ public class BlockChain {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prove the block is internally valid: hash is lower than target, merkle root is correct and so on.
|
// Does this block contain any transactions we might care about? Check this up front before verifying the
|
||||||
|
// blocks validity so we can skip the merkle root verification if the contents aren't interesting. This saves
|
||||||
|
// a lot of time for big blocks.
|
||||||
|
boolean contentsImportant = false;
|
||||||
|
HashMap<Wallet, List<Transaction>> walletToTxMap = new HashMap<Wallet, List<Transaction>>();;
|
||||||
|
if (block.transactions != null) {
|
||||||
|
scanTransactions(block, walletToTxMap);
|
||||||
|
contentsImportant = walletToTxMap.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prove the block is internally valid: hash is lower than target, etc. This only checks the block contents
|
||||||
|
// if there is a tx sending or receiving coins using an address in one of our wallets. And those transactions
|
||||||
|
// are only lightly verified: presence in a valid connecting block is taken as proof of validity. See the
|
||||||
|
// article here for more details: http://code.google.com/p/bitcoinj/wiki/SecurityModel
|
||||||
try {
|
try {
|
||||||
block.verify();
|
block.verifyHeader();
|
||||||
|
if (contentsImportant)
|
||||||
|
block.verifyTransactions();
|
||||||
} catch (VerificationException e) {
|
} catch (VerificationException e) {
|
||||||
log.error("Failed to verify block:", e);
|
log.error("Failed to verify block: ", e);
|
||||||
log.error(block.toString());
|
log.error(block.getHashAsString());
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,9 +191,7 @@ public class BlockChain {
|
|||||||
StoredBlock newStoredBlock = storedPrev.build(block);
|
StoredBlock newStoredBlock = storedPrev.build(block);
|
||||||
checkDifficultyTransitions(storedPrev, newStoredBlock);
|
checkDifficultyTransitions(storedPrev, newStoredBlock);
|
||||||
blockStore.put(newStoredBlock);
|
blockStore.put(newStoredBlock);
|
||||||
// block.transactions may be null here if we received only a header and not a full block. This does not
|
connectBlock(newStoredBlock, storedPrev, walletToTxMap);
|
||||||
// happen currently but might in future if getheaders is implemented.
|
|
||||||
connectBlock(newStoredBlock, storedPrev, block.transactions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tryConnecting)
|
if (tryConnecting)
|
||||||
@@ -163,7 +201,8 @@ public class BlockChain {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectBlock(StoredBlock newStoredBlock, StoredBlock storedPrev, List<Transaction> newTransactions)
|
private void connectBlock(StoredBlock newStoredBlock, StoredBlock storedPrev,
|
||||||
|
HashMap<Wallet, List<Transaction>> newTransactions)
|
||||||
throws BlockStoreException, VerificationException {
|
throws BlockStoreException, VerificationException {
|
||||||
if (storedPrev.equals(chainHead)) {
|
if (storedPrev.equals(chainHead)) {
|
||||||
// This block connects to the best known block, it is a normal continuation of the system.
|
// This block connects to the best known block, it is a normal continuation of the system.
|
||||||
@@ -214,10 +253,12 @@ public class BlockChain {
|
|||||||
// Then build a list of all blocks in the old part of the chain and the new part.
|
// Then build a list of all blocks in the old part of the chain and the new part.
|
||||||
List<StoredBlock> oldBlocks = getPartialChain(chainHead, splitPoint);
|
List<StoredBlock> oldBlocks = getPartialChain(chainHead, splitPoint);
|
||||||
List<StoredBlock> newBlocks = getPartialChain(newChainHead, splitPoint);
|
List<StoredBlock> newBlocks = getPartialChain(newChainHead, splitPoint);
|
||||||
// Now inform the wallet. This is necessary so the set of currently active transactions (that we can spend)
|
// Now inform the wallets. This is necessary so the set of currently active transactions (that we can spend)
|
||||||
// can be updated to take into account the re-organize. We might also have received new coins we didn't have
|
// can be updated to take into account the re-organize. We might also have received new coins we didn't have
|
||||||
// before and our previous spends might have been undone.
|
// before and our previous spends might have been undone.
|
||||||
wallet.reorganize(oldBlocks, newBlocks);
|
for (Wallet wallet : wallets) {
|
||||||
|
wallet.reorganize(oldBlocks, newBlocks);
|
||||||
|
}
|
||||||
// Update the pointer to the best known block.
|
// Update the pointer to the best known block.
|
||||||
setChainHead(newChainHead);
|
setChainHead(newChainHead);
|
||||||
}
|
}
|
||||||
@@ -269,14 +310,16 @@ public class BlockChain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendTransactionsToWallet(StoredBlock block, NewBlockType blockType,
|
private void sendTransactionsToWallet(StoredBlock block, NewBlockType blockType,
|
||||||
List<Transaction> newTransactions) throws VerificationException {
|
HashMap<Wallet, List<Transaction>> newTransactions) throws VerificationException {
|
||||||
// Scan the transactions to find out if any mention addresses we own.
|
for (Wallet wallet : newTransactions.keySet()) {
|
||||||
for (Transaction tx : newTransactions) {
|
|
||||||
try {
|
try {
|
||||||
scanTransaction(block, tx, blockType);
|
List<Transaction> txns = newTransactions.get(wallet);
|
||||||
|
for (Transaction tx : txns) {
|
||||||
|
wallet.receive(tx, block, blockType);
|
||||||
|
}
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
// We don't want scripts we don't understand to break the block chain,
|
// We don't want scripts we don't understand to break the block chain so just note that this tx was
|
||||||
// so just note that this tx was not scanned here and continue.
|
// not scanned here and continue.
|
||||||
log.warn("Failed to parse a script: " + e.toString());
|
log.warn("Failed to parse a script: " + e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -382,31 +425,50 @@ public class BlockChain {
|
|||||||
receivedDifficulty.toString(16) + " vs " + newDifficulty.toString(16));
|
receivedDifficulty.toString(16) + " vs " + newDifficulty.toString(16));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scanTransaction(StoredBlock block, Transaction tx, NewBlockType blockType)
|
/**
|
||||||
throws ScriptException, VerificationException {
|
* For the transactions in the given block, update the txToWalletMap such that each wallet maps to a list of
|
||||||
boolean shouldReceive = false;
|
* transactions for which it is relevant.
|
||||||
for (TransactionOutput output : tx.outputs) {
|
*/
|
||||||
// TODO: Handle more types of outputs, not just regular to address outputs.
|
private void scanTransactions(Block block, HashMap<Wallet, List<Transaction>> walletToTxMap)
|
||||||
if (output.getScriptPubKey().isSentToIP()) return;
|
throws VerificationException {
|
||||||
// This is not thread safe as a key could be removed between the call to isMine and receive.
|
for (Transaction tx : block.transactions) {
|
||||||
if (output.isMine(wallet)) {
|
try {
|
||||||
shouldReceive = true;
|
for (Wallet wallet : wallets) {
|
||||||
}
|
boolean shouldReceive = false;
|
||||||
}
|
for (TransactionOutput output : tx.outputs) {
|
||||||
|
// TODO: Handle more types of outputs, not just regular to address outputs.
|
||||||
|
if (output.getScriptPubKey().isSentToIP()) return;
|
||||||
|
// This is not thread safe as a key could be removed between the call to isMine and receive.
|
||||||
|
if (output.isMine(wallet)) {
|
||||||
|
shouldReceive = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Coinbase transactions don't have anything useful in their inputs (as they create coins out of thin air).
|
// Coinbase transactions don't have anything useful in their inputs (as they create coins out of thin air).
|
||||||
if (!tx.isCoinBase()) {
|
if (!shouldReceive && !tx.isCoinBase()) {
|
||||||
for (TransactionInput i : tx.inputs) {
|
for (TransactionInput i : tx.inputs) {
|
||||||
byte[] pubkey = i.getScriptSig().getPubKey();
|
byte[] pubkey = i.getScriptSig().getPubKey();
|
||||||
// This is not thread safe as a key could be removed between the call to isPubKeyMine and receive.
|
// This is not thread safe as a key could be removed between the call to isPubKeyMine and receive.
|
||||||
if (wallet.isPubKeyMine(pubkey)) {
|
if (wallet.isPubKeyMine(pubkey)) {
|
||||||
shouldReceive = true;
|
shouldReceive = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldReceive) continue;
|
||||||
|
List<Transaction> txList = walletToTxMap.get(wallet);
|
||||||
|
if (txList == null) {
|
||||||
|
txList = new LinkedList<Transaction>();
|
||||||
|
walletToTxMap.put(wallet, txList);
|
||||||
|
}
|
||||||
|
txList.add(tx);
|
||||||
}
|
}
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
// We don't want scripts we don't understand to break the block chain so just note that this tx was
|
||||||
|
// not scanned here and continue.
|
||||||
|
log.warn("Failed to parse a script: " + e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldReceive)
|
|
||||||
wallet.receive(tx, block, blockType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public class NetworkParameters implements Serializable {
|
|||||||
n.genesisBlock.setDifficultyTarget(0x1d07fff8L);
|
n.genesisBlock.setDifficultyTarget(0x1d07fff8L);
|
||||||
n.genesisBlock.setNonce(384568319);
|
n.genesisBlock.setNonce(384568319);
|
||||||
String genesisHash = n.genesisBlock.getHashAsString();
|
String genesisHash = n.genesisBlock.getHashAsString();
|
||||||
assert genesisHash.equals("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008");
|
assert genesisHash.equals("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008") : genesisHash;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ public class Peer {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public Future<Block> getBlock(Sha256Hash blockHash) throws IOException {
|
public Future<Block> getBlock(Sha256Hash blockHash) throws IOException {
|
||||||
InventoryMessage getdata = new InventoryMessage(params);
|
GetDataMessage getdata = new GetDataMessage(params);
|
||||||
InventoryItem inventoryItem = new InventoryItem(InventoryItem.Type.Block, blockHash);
|
InventoryItem inventoryItem = new InventoryItem(InventoryItem.Type.Block, blockHash);
|
||||||
getdata.addItem(inventoryItem);
|
getdata.addItem(inventoryItem);
|
||||||
GetDataFuture<Block> future = new GetDataFuture<Block>(inventoryItem);
|
GetDataFuture<Block> future = new GetDataFuture<Block>(inventoryItem);
|
||||||
|
|||||||
@@ -16,12 +16,9 @@
|
|||||||
|
|
||||||
package com.google.bitcoin.core;
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
public class Ping
|
public class Ping extends Message {
|
||||||
extends Message
|
|
||||||
{
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException
|
void parse() throws ProtocolException {
|
||||||
{
|
|
||||||
// nothing to parse
|
// nothing to parse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,4 +73,9 @@ public class Sha256Hash implements Serializable {
|
|||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Sha256Hash clone() {
|
||||||
|
return new Sha256Hash(bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,10 +104,6 @@ public class Transaction extends Message implements Serializable {
|
|||||||
return getHash().toString();
|
return getHash().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setFakeHashForTesting(Sha256Hash hash) {
|
|
||||||
this.hash = hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the sum of the outputs that are sending coins to a key in the wallet. The flag controls whether to
|
* Calculates the sum of the outputs that are sending coins to a key in the wallet. The flag controls whether to
|
||||||
* include spent outputs or not.
|
* include spent outputs or not.
|
||||||
@@ -170,6 +166,10 @@ public class Transaction extends Message implements Serializable {
|
|||||||
connected = input.getConnectedOutput(wallet.pending);
|
connected = input.getConnectedOutput(wallet.pending);
|
||||||
if (connected == null)
|
if (connected == null)
|
||||||
continue;
|
continue;
|
||||||
|
// The connected output may be the change to the sender of a previous input sent to this wallet. In this
|
||||||
|
// case we ignore it.
|
||||||
|
if (!connected.isMine(wallet))
|
||||||
|
continue;
|
||||||
v = v.add(connected.getValue());
|
v = v.add(connected.getValue());
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
@@ -232,9 +232,6 @@ public class Transaction extends Message implements Serializable {
|
|||||||
cursor += output.getMessageSize();
|
cursor += output.getMessageSize();
|
||||||
}
|
}
|
||||||
lockTime = readUint32();
|
lockTime = readUint32();
|
||||||
|
|
||||||
// Store a hash, it may come in useful later (want to avoid reserialization costs).
|
|
||||||
hash = new Sha256Hash(reverseBytes(doubleDigest(bytes, offset, cursor - offset)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public class VersionMessage extends Message {
|
|||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
throw new RuntimeException(e); // Cannot happen.
|
throw new RuntimeException(e); // Cannot happen.
|
||||||
}
|
}
|
||||||
subVer = "BitCoinJ 0.2";
|
subVer = "BitCoinJ 0.3-SNAPSHOT";
|
||||||
bestHeight = newBestHeight;
|
bestHeight = newBestHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -338,7 +338,10 @@ public class Wallet implements Serializable {
|
|||||||
// A -> spent by B [pending]
|
// A -> spent by B [pending]
|
||||||
// \-> spent by C [chain]
|
// \-> spent by C [chain]
|
||||||
Transaction doubleSpent = input.outpoint.fromTx; // == A
|
Transaction doubleSpent = input.outpoint.fromTx; // == A
|
||||||
Transaction connected = doubleSpent.outputs.get((int)input.outpoint.index).getSpentBy().parentTransaction;
|
int index = (int) input.outpoint.index;
|
||||||
|
TransactionOutput output = doubleSpent.outputs.get(index);
|
||||||
|
TransactionInput spentBy = output.getSpentBy();
|
||||||
|
Transaction connected = spentBy.parentTransaction;
|
||||||
if (pending.containsKey(connected.getHash())) {
|
if (pending.containsKey(connected.getHash())) {
|
||||||
log.info("Saw double spend from chain override pending tx {}", connected.getHashAsString());
|
log.info("Saw double spend from chain override pending tx {}", connected.getHashAsString());
|
||||||
log.info(" <-pending ->dead");
|
log.info(" <-pending ->dead");
|
||||||
@@ -382,6 +385,14 @@ public class Wallet implements Serializable {
|
|||||||
eventListeners.add(listener);
|
eventListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given event listener object. Returns true if the listener was removed,
|
||||||
|
* false if that listener was never added.
|
||||||
|
*/
|
||||||
|
public synchronized boolean removeEventListener(WalletEventListener listener) {
|
||||||
|
return eventListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this when we have successfully transmitted the send tx to the network, to update the wallet.
|
* Call this when we have successfully transmitted the send tx to the network, to update the wallet.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -38,7 +38,12 @@ public class DnsDiscovery implements PeerDiscovery {
|
|||||||
|
|
||||||
private String[] hostNames;
|
private String[] hostNames;
|
||||||
private NetworkParameters netParams;
|
private NetworkParameters netParams;
|
||||||
private static final String[] defaultHosts = new String[] {"bitseed.xf2.org","bitseed.bitcoin.org.uk"};
|
|
||||||
|
public static final String[] defaultHosts = new String[] {
|
||||||
|
"dnsseed.bluematt.me", // Auto generated
|
||||||
|
"bitseed.xf2.org", // Static
|
||||||
|
"bitseed.bitcoin.org.uk" // Static
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supports finding peers through DNS A records. Community run DNS entry points will be used.
|
* Supports finding peers through DNS A records. Community run DNS entry points will be used.
|
||||||
|
|||||||
46
src/com/google/bitcoin/examples/FetchBlock.java
Normal file
46
src/com/google/bitcoin/examples/FetchBlock.java
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.examples;
|
||||||
|
|
||||||
|
import com.google.bitcoin.core.*;
|
||||||
|
import com.google.bitcoin.store.BlockStore;
|
||||||
|
import com.google.bitcoin.store.MemoryBlockStore;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads the block given a block hash from the localhost node and prints it out.
|
||||||
|
*/
|
||||||
|
public class FetchBlock {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
System.out.println("Connecting to node");
|
||||||
|
final NetworkParameters params = NetworkParameters.prodNet();
|
||||||
|
NetworkConnection conn = new NetworkConnection(InetAddress.getLocalHost(), params, 0, 60000);
|
||||||
|
BlockStore blockStore = new MemoryBlockStore(params);
|
||||||
|
BlockChain chain = new BlockChain(params, blockStore);
|
||||||
|
Peer peer = new Peer(params, conn, chain);
|
||||||
|
peer.start();
|
||||||
|
|
||||||
|
Sha256Hash blockHash = new Sha256Hash(args[0]);
|
||||||
|
Future<Block> future = peer.getBlock(blockHash);
|
||||||
|
System.out.println("Waiting for node to send us the requested block: " + blockHash);
|
||||||
|
Block block = future.get();
|
||||||
|
System.out.println(block);
|
||||||
|
peer.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@ public class BoundedOverheadBlockStore implements BlockStore {
|
|||||||
private LinkedHashMap<Sha256Hash, StoredBlock> blockCache = new LinkedHashMap<Sha256Hash, StoredBlock>() {
|
private LinkedHashMap<Sha256Hash, StoredBlock> blockCache = new LinkedHashMap<Sha256Hash, StoredBlock>() {
|
||||||
@Override
|
@Override
|
||||||
protected boolean removeEldestEntry(Map.Entry<Sha256Hash, StoredBlock> entry) {
|
protected boolean removeEldestEntry(Map.Entry<Sha256Hash, StoredBlock> entry) {
|
||||||
return size() > 100; // This was chosen arbitrarily.
|
return size() > 2050; // Slightly more than the difficulty transition period.
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Use a separate cache to track get() misses. This is to efficiently handle the case of an unconnected block
|
// Use a separate cache to track get() misses. This is to efficiently handle the case of an unconnected block
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public class DiskBlockStore implements BlockStore {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Don't try to verify the genesis block to avoid upsetting the unit tests.
|
// Don't try to verify the genesis block to avoid upsetting the unit tests.
|
||||||
b.verify();
|
b.verifyHeader();
|
||||||
// Calculate its height and total chain work.
|
// Calculate its height and total chain work.
|
||||||
s = prev.build(b);
|
s = prev.build(b);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,16 +16,18 @@
|
|||||||
|
|
||||||
package com.google.bitcoin.core;
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
import com.google.bitcoin.bouncycastle.util.encoders.Hex;
|
import com.google.bitcoin.store.BlockStore;
|
||||||
import com.google.bitcoin.store.MemoryBlockStore;
|
import com.google.bitcoin.store.MemoryBlockStore;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import static com.google.bitcoin.core.TestUtils.createFakeBlock;
|
||||||
|
import static com.google.bitcoin.core.TestUtils.createFakeTx;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
// NOTE: Handling of chain splits/reorgs are in ChainSplitTests.
|
// Handling of chain splits/reorgs are in ChainSplitTests.
|
||||||
|
|
||||||
public class BlockChainTest {
|
public class BlockChainTest {
|
||||||
private static final NetworkParameters testNet = NetworkParameters.testNet();
|
private static final NetworkParameters testNet = NetworkParameters.testNet();
|
||||||
@@ -33,21 +35,27 @@ public class BlockChainTest {
|
|||||||
|
|
||||||
private Wallet wallet;
|
private Wallet wallet;
|
||||||
private BlockChain chain;
|
private BlockChain chain;
|
||||||
|
private BlockStore blockStore;
|
||||||
private Address coinbaseTo;
|
private Address coinbaseTo;
|
||||||
private NetworkParameters unitTestParams;
|
private NetworkParameters unitTestParams;
|
||||||
private Address someOtherGuy;
|
|
||||||
|
private void resetBlockStore() {
|
||||||
|
blockStore = new MemoryBlockStore(unitTestParams);
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
|
||||||
testNetChain = new BlockChain(testNet, new Wallet(testNet), new MemoryBlockStore(testNet));
|
testNetChain = new BlockChain(testNet, new Wallet(testNet), new MemoryBlockStore(testNet));
|
||||||
|
|
||||||
unitTestParams = NetworkParameters.unitTests();
|
unitTestParams = NetworkParameters.unitTests();
|
||||||
wallet = new Wallet(unitTestParams);
|
wallet = new Wallet(unitTestParams);
|
||||||
wallet.addKey(new ECKey());
|
wallet.addKey(new ECKey());
|
||||||
chain = new BlockChain(unitTestParams, wallet, new MemoryBlockStore(unitTestParams));
|
|
||||||
|
resetBlockStore();
|
||||||
|
chain = new BlockChain(unitTestParams, wallet, blockStore);
|
||||||
|
|
||||||
coinbaseTo = wallet.keychain.get(0).toAddress(unitTestParams);
|
coinbaseTo = wallet.keychain.get(0).toAddress(unitTestParams);
|
||||||
someOtherGuy = new ECKey().toAddress(unitTestParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -68,10 +76,40 @@ public class BlockChainTest {
|
|||||||
} catch (VerificationException e) {
|
} catch (VerificationException e) {
|
||||||
b2.setNonce(n);
|
b2.setNonce(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now it works because we reset the nonce.
|
// Now it works because we reset the nonce.
|
||||||
assertTrue(testNetChain.add(b2));
|
assertTrue(testNetChain.add(b2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void merkleRoots() throws Exception {
|
||||||
|
// Test that merkle root verification takes place when a relevant transaction is present and doesn't when
|
||||||
|
// there isn't any such tx present (as an optimization).
|
||||||
|
Transaction tx1 = createFakeTx(unitTestParams,
|
||||||
|
Utils.toNanoCoins(1, 0),
|
||||||
|
wallet.keychain.get(0).toAddress(unitTestParams));
|
||||||
|
Block b1 = createFakeBlock(unitTestParams, blockStore, tx1).block;
|
||||||
|
chain.add(b1);
|
||||||
|
resetBlockStore();
|
||||||
|
Sha256Hash hash = b1.getMerkleRoot();
|
||||||
|
b1.setMerkleRoot(Sha256Hash.ZERO_HASH);
|
||||||
|
try {
|
||||||
|
chain.add(b1);
|
||||||
|
fail();
|
||||||
|
} catch (VerificationException e) {
|
||||||
|
// Expected.
|
||||||
|
b1.setMerkleRoot(hash);
|
||||||
|
}
|
||||||
|
// Now add a second block with no relevant transactions and then break it.
|
||||||
|
Transaction tx2 = createFakeTx(unitTestParams, Utils.toNanoCoins(1, 0),
|
||||||
|
new ECKey().toAddress(unitTestParams));
|
||||||
|
Block b2 = createFakeBlock(unitTestParams, blockStore, tx2).block;
|
||||||
|
hash = b2.getMerkleRoot();
|
||||||
|
b2.setMerkleRoot(Sha256Hash.ZERO_HASH);
|
||||||
|
b2.solve();
|
||||||
|
chain.add(b2); // Broken block is accepted because its contents don't matter to us.
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnconnectedBlocks() throws Exception {
|
public void testUnconnectedBlocks() throws Exception {
|
||||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo);
|
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo);
|
||||||
@@ -163,7 +201,7 @@ public class BlockChainTest {
|
|||||||
b2.setTime(1296734343L);
|
b2.setTime(1296734343L);
|
||||||
b2.setPrevBlockHash(new Sha256Hash("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604"));
|
b2.setPrevBlockHash(new Sha256Hash("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604"));
|
||||||
assertEquals("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.getHashAsString());
|
assertEquals("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.getHashAsString());
|
||||||
b2.verify();
|
b2.verifyHeader();
|
||||||
return b2;
|
return b2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +212,7 @@ public class BlockChainTest {
|
|||||||
b1.setTime(1296734340);
|
b1.setTime(1296734340);
|
||||||
b1.setPrevBlockHash(new Sha256Hash("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"));
|
b1.setPrevBlockHash(new Sha256Hash("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"));
|
||||||
assertEquals("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.getHashAsString());
|
assertEquals("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.getHashAsString());
|
||||||
b1.verify();
|
b1.verifyHeader();
|
||||||
return b1;
|
return b1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
64
tests/com/google/bitcoin/core/TestUtils.java
Normal file
64
tests/com/google/bitcoin/core/TestUtils.java
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2011 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.core;
|
||||||
|
|
||||||
|
import com.google.bitcoin.store.BlockStore;
|
||||||
|
import com.google.bitcoin.store.BlockStoreException;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
public class TestUtils {
|
||||||
|
public static Transaction createFakeTx(NetworkParameters params, BigInteger nanocoins, Address to) {
|
||||||
|
Transaction t = new Transaction(params);
|
||||||
|
TransactionOutput o1 = new TransactionOutput(params, t, nanocoins, to);
|
||||||
|
t.addOutput(o1);
|
||||||
|
// Make a previous tx simply to send us sufficient coins. This prev tx is not really valid but it doesn't
|
||||||
|
// matter for our purposes.
|
||||||
|
Transaction prevTx = new Transaction(params);
|
||||||
|
TransactionOutput prevOut = new TransactionOutput(params, prevTx, nanocoins, to);
|
||||||
|
prevTx.addOutput(prevOut);
|
||||||
|
// Connect it.
|
||||||
|
t.addInput(prevOut);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BlockPair {
|
||||||
|
StoredBlock storedBlock;
|
||||||
|
Block block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emulates receiving a valid block that builds on top of the chain.
|
||||||
|
public static BlockPair createFakeBlock(NetworkParameters params, BlockStore blockStore,
|
||||||
|
Transaction... transactions) {
|
||||||
|
try {
|
||||||
|
Block b = blockStore.getChainHead().getHeader().createNextBlock(new ECKey().toAddress(params));
|
||||||
|
for (Transaction tx : transactions)
|
||||||
|
b.addTransaction(tx);
|
||||||
|
b.solve();
|
||||||
|
BlockPair pair = new BlockPair();
|
||||||
|
pair.block = b;
|
||||||
|
pair.storedBlock = blockStore.getChainHead().build(b);
|
||||||
|
blockStore.put(pair.storedBlock);
|
||||||
|
blockStore.setChainHead(pair.storedBlock);
|
||||||
|
return pair;
|
||||||
|
} catch (VerificationException e) {
|
||||||
|
throw new RuntimeException(e); // Cannot happen.
|
||||||
|
} catch (BlockStoreException e) {
|
||||||
|
throw new RuntimeException(e); // Cannot happen.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,8 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import static com.google.bitcoin.core.TestUtils.createFakeBlock;
|
||||||
|
import static com.google.bitcoin.core.TestUtils.createFakeTx;
|
||||||
import static com.google.bitcoin.core.Utils.*;
|
import static com.google.bitcoin.core.Utils.*;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -35,60 +37,22 @@ public class WalletTest {
|
|||||||
private Address myAddress;
|
private Address myAddress;
|
||||||
private Wallet wallet;
|
private Wallet wallet;
|
||||||
private BlockStore blockStore;
|
private BlockStore blockStore;
|
||||||
|
private ECKey myKey;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
ECKey myKey = new ECKey();
|
myKey = new ECKey();
|
||||||
myAddress = myKey.toAddress(params);
|
myAddress = myKey.toAddress(params);
|
||||||
wallet = new Wallet(params);
|
wallet = new Wallet(params);
|
||||||
wallet.addKey(myKey);
|
wallet.addKey(myKey);
|
||||||
blockStore = new MemoryBlockStore(params);
|
blockStore = new MemoryBlockStore(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Transaction createFakeTx(BigInteger nanocoins, Address to) {
|
|
||||||
Transaction t = new Transaction(params);
|
|
||||||
TransactionOutput o1 = new TransactionOutput(params, t, nanocoins, to);
|
|
||||||
t.addOutput(o1);
|
|
||||||
// Make a previous tx simply to send us sufficient coins. This prev tx is not really valid but it doesn't
|
|
||||||
// matter for our purposes.
|
|
||||||
Transaction prevTx = new Transaction(params);
|
|
||||||
TransactionOutput prevOut = new TransactionOutput(params, prevTx, nanocoins, to);
|
|
||||||
prevTx.addOutput(prevOut);
|
|
||||||
// Connect it.
|
|
||||||
t.addInput(prevOut);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
class BlockPair {
|
|
||||||
StoredBlock storedBlock;
|
|
||||||
Block block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emulates receiving a valid block that builds on top of the chain.
|
|
||||||
private BlockPair createFakeBlock(Transaction... transactions) {
|
|
||||||
try {
|
|
||||||
Block b = blockStore.getChainHead().getHeader().createNextBlock(new ECKey().toAddress(params));
|
|
||||||
for (Transaction tx : transactions)
|
|
||||||
b.addTransaction(tx);
|
|
||||||
b.solve();
|
|
||||||
BlockPair pair = new BlockPair();
|
|
||||||
pair.block = b;
|
|
||||||
pair.storedBlock = blockStore.getChainHead().build(b);
|
|
||||||
blockStore.put(pair.storedBlock);
|
|
||||||
blockStore.setChainHead(pair.storedBlock);
|
|
||||||
return pair;
|
|
||||||
} catch (VerificationException e) {
|
|
||||||
throw new RuntimeException(e); // Cannot happen.
|
|
||||||
} catch (BlockStoreException e) {
|
|
||||||
throw new RuntimeException(e); // Cannot happen.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBasicSpending() throws Exception {
|
public void basicSpending() throws Exception {
|
||||||
// We'll set up a wallet that receives a coin, then sends a coin of lesser value and keeps the change.
|
// We'll set up a wallet that receives a coin, then sends a coin of lesser value and keeps the change.
|
||||||
BigInteger v1 = Utils.toNanoCoins(1, 0);
|
BigInteger v1 = Utils.toNanoCoins(1, 0);
|
||||||
Transaction t1 = createFakeTx(v1, myAddress);
|
Transaction t1 = createFakeTx(params, v1, myAddress);
|
||||||
|
|
||||||
wallet.receive(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
wallet.receive(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||||
assertEquals(v1, wallet.getBalance());
|
assertEquals(v1, wallet.getBalance());
|
||||||
@@ -105,24 +69,24 @@ public class WalletTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSideChain() throws Exception {
|
public void sideChain() throws Exception {
|
||||||
// The wallet receives a coin on the main chain, then on a side chain. Only main chain counts towards balance.
|
// The wallet receives a coin on the main chain, then on a side chain. Only main chain counts towards balance.
|
||||||
BigInteger v1 = Utils.toNanoCoins(1, 0);
|
BigInteger v1 = Utils.toNanoCoins(1, 0);
|
||||||
Transaction t1 = createFakeTx(v1, myAddress);
|
Transaction t1 = createFakeTx(params, v1, myAddress);
|
||||||
|
|
||||||
wallet.receive(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
wallet.receive(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||||
assertEquals(v1, wallet.getBalance());
|
assertEquals(v1, wallet.getBalance());
|
||||||
|
|
||||||
BigInteger v2 = toNanoCoins(0, 50);
|
BigInteger v2 = toNanoCoins(0, 50);
|
||||||
Transaction t2 = createFakeTx(v2, myAddress);
|
Transaction t2 = createFakeTx(params, v2, myAddress);
|
||||||
wallet.receive(t2, null, BlockChain.NewBlockType.SIDE_CHAIN);
|
wallet.receive(t2, null, BlockChain.NewBlockType.SIDE_CHAIN);
|
||||||
|
|
||||||
assertEquals(v1, wallet.getBalance());
|
assertEquals(v1, wallet.getBalance());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testListener() throws Exception {
|
public void listeners() throws Exception {
|
||||||
final Transaction fakeTx = createFakeTx(Utils.toNanoCoins(1, 0), myAddress);
|
final Transaction fakeTx = createFakeTx(params, Utils.toNanoCoins(1, 0), myAddress);
|
||||||
final boolean[] didRun = new boolean[1];
|
final boolean[] didRun = new boolean[1];
|
||||||
WalletEventListener listener = new WalletEventListener() {
|
WalletEventListener listener = new WalletEventListener() {
|
||||||
public void onCoinsReceived(Wallet w, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
|
public void onCoinsReceived(Wallet w, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
|
||||||
@@ -139,14 +103,14 @@ public class WalletTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBalance() throws Exception {
|
public void balance() throws Exception {
|
||||||
// Receive 5 coins then half a coin.
|
// Receive 5 coins then half a coin.
|
||||||
BigInteger v1 = toNanoCoins(5, 0);
|
BigInteger v1 = toNanoCoins(5, 0);
|
||||||
BigInteger v2 = toNanoCoins(0, 50);
|
BigInteger v2 = toNanoCoins(0, 50);
|
||||||
Transaction t1 = createFakeTx(v1, myAddress);
|
Transaction t1 = createFakeTx(params, v1, myAddress);
|
||||||
Transaction t2 = createFakeTx(v2, myAddress);
|
Transaction t2 = createFakeTx(params, v2, myAddress);
|
||||||
StoredBlock b1 = createFakeBlock(t1).storedBlock;
|
StoredBlock b1 = createFakeBlock(params, blockStore, t1).storedBlock;
|
||||||
StoredBlock b2 = createFakeBlock(t2).storedBlock;
|
StoredBlock b2 = createFakeBlock(params, blockStore, t2).storedBlock;
|
||||||
BigInteger expected = toNanoCoins(5, 50);
|
BigInteger expected = toNanoCoins(5, 50);
|
||||||
wallet.receive(t1, b1, BlockChain.NewBlockType.BEST_CHAIN);
|
wallet.receive(t1, b1, BlockChain.NewBlockType.BEST_CHAIN);
|
||||||
wallet.receive(t2, b2, BlockChain.NewBlockType.BEST_CHAIN);
|
wallet.receive(t2, b2, BlockChain.NewBlockType.BEST_CHAIN);
|
||||||
@@ -164,7 +128,7 @@ public class WalletTest {
|
|||||||
wallet.getBalance(Wallet.BalanceType.ESTIMATED)));
|
wallet.getBalance(Wallet.BalanceType.ESTIMATED)));
|
||||||
|
|
||||||
// Now confirm the transaction by including it into a block.
|
// Now confirm the transaction by including it into a block.
|
||||||
StoredBlock b3 = createFakeBlock(spend).storedBlock;
|
StoredBlock b3 = createFakeBlock(params, blockStore, spend).storedBlock;
|
||||||
wallet.receive(spend, b3, BlockChain.NewBlockType.BEST_CHAIN);
|
wallet.receive(spend, b3, BlockChain.NewBlockType.BEST_CHAIN);
|
||||||
|
|
||||||
// Change is confirmed. We started with 5.50 so we should have 4.50 left.
|
// Change is confirmed. We started with 5.50 so we should have 4.50 left.
|
||||||
@@ -178,31 +142,31 @@ public class WalletTest {
|
|||||||
// suite.
|
// suite.
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBlockChainCatchup() throws Exception {
|
public void blockChainCatchup() throws Exception {
|
||||||
Transaction tx1 = createFakeTx(Utils.toNanoCoins(1, 0), myAddress);
|
Transaction tx1 = createFakeTx(params, Utils.toNanoCoins(1, 0), myAddress);
|
||||||
StoredBlock b1 = createFakeBlock(tx1).storedBlock;
|
StoredBlock b1 = createFakeBlock(params, blockStore, tx1).storedBlock;
|
||||||
wallet.receive(tx1, b1, BlockChain.NewBlockType.BEST_CHAIN);
|
wallet.receive(tx1, b1, BlockChain.NewBlockType.BEST_CHAIN);
|
||||||
// Send 0.10 to somebody else.
|
// Send 0.10 to somebody else.
|
||||||
Transaction send1 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 10), myAddress);
|
Transaction send1 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 10), myAddress);
|
||||||
// Pretend it makes it into the block chain, our wallet state is cleared but we still have the keys, and we
|
// Pretend it makes it into the block chain, our wallet state is cleared but we still have the keys, and we
|
||||||
// want to get back to our previous state. We can do this by just not confirming the transaction as
|
// want to get back to our previous state. We can do this by just not confirming the transaction as
|
||||||
// createSend is stateless.
|
// createSend is stateless.
|
||||||
StoredBlock b2 = createFakeBlock(send1).storedBlock;
|
StoredBlock b2 = createFakeBlock(params, blockStore, send1).storedBlock;
|
||||||
wallet.receive(send1, b2, BlockChain.NewBlockType.BEST_CHAIN);
|
wallet.receive(send1, b2, BlockChain.NewBlockType.BEST_CHAIN);
|
||||||
assertEquals(bitcoinValueToFriendlyString(wallet.getBalance()), "0.90");
|
assertEquals(bitcoinValueToFriendlyString(wallet.getBalance()), "0.90");
|
||||||
// And we do it again after the catchup.
|
// And we do it again after the catchup.
|
||||||
Transaction send2 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 10), myAddress);
|
Transaction send2 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 10), myAddress);
|
||||||
// What we'd really like to do is prove the official client would accept it .... no such luck unfortunately.
|
// What we'd really like to do is prove the official client would accept it .... no such luck unfortunately.
|
||||||
wallet.confirmSend(send2);
|
wallet.confirmSend(send2);
|
||||||
StoredBlock b3 = createFakeBlock(send2).storedBlock;
|
StoredBlock b3 = createFakeBlock(params, blockStore, send2).storedBlock;
|
||||||
wallet.receive(send2, b3, BlockChain.NewBlockType.BEST_CHAIN);
|
wallet.receive(send2, b3, BlockChain.NewBlockType.BEST_CHAIN);
|
||||||
assertEquals(bitcoinValueToFriendlyString(wallet.getBalance()), "0.80");
|
assertEquals(bitcoinValueToFriendlyString(wallet.getBalance()), "0.80");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBalances() throws Exception {
|
public void balances() throws Exception {
|
||||||
BigInteger nanos = Utils.toNanoCoins(1, 0);
|
BigInteger nanos = Utils.toNanoCoins(1, 0);
|
||||||
Transaction tx1 = createFakeTx(nanos, myAddress);
|
Transaction tx1 = createFakeTx(params, nanos, myAddress);
|
||||||
wallet.receive(tx1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
wallet.receive(tx1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||||
assertEquals(nanos, tx1.getValueSentToMe(wallet, true));
|
assertEquals(nanos, tx1.getValueSentToMe(wallet, true));
|
||||||
// Send 0.10 to somebody else.
|
// Send 0.10 to somebody else.
|
||||||
@@ -213,7 +177,24 @@ public class WalletTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFinneyAttack() throws Exception {
|
public void transactions() throws Exception {
|
||||||
|
// This test covers a bug in which Transaction.getValueSentFromMe was calculating incorrectly.
|
||||||
|
Transaction tx = createFakeTx(params, Utils.toNanoCoins(1, 0), myAddress);
|
||||||
|
// Now add another output (ie, change) that goes to some other address.
|
||||||
|
Address someOtherGuy = new ECKey().toAddress(params);
|
||||||
|
TransactionOutput output = new TransactionOutput(params, tx, Utils.toNanoCoins(0, 5), someOtherGuy);
|
||||||
|
tx.addOutput(output);
|
||||||
|
wallet.receive(tx, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||||
|
// Now the other guy creates a transaction which spends that change.
|
||||||
|
Transaction tx2 = new Transaction(params);
|
||||||
|
tx2.addInput(output);
|
||||||
|
tx2.addOutput(new TransactionOutput(params, tx2, Utils.toNanoCoins(0, 5), myAddress));
|
||||||
|
// tx2 doesn't send any coins from us, even though the output is in the wallet.
|
||||||
|
assertEquals(Utils.toNanoCoins(0, 0), tx2.getValueSentFromMe(wallet));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void finneyAttack() throws Exception {
|
||||||
// A Finney attack is where a miner includes a transaction spending coins to themselves but does not
|
// A Finney attack is where a miner includes a transaction spending coins to themselves but does not
|
||||||
// broadcast it. When they find a solved block, they hold it back temporarily whilst they buy something with
|
// broadcast it. When they find a solved block, they hold it back temporarily whilst they buy something with
|
||||||
// those same coins. After purchasing, they broadcast the block thus reversing the transaction. It can be
|
// those same coins. After purchasing, they broadcast the block thus reversing the transaction. It can be
|
||||||
@@ -237,7 +218,7 @@ public class WalletTest {
|
|||||||
|
|
||||||
// Receive 1 BTC.
|
// Receive 1 BTC.
|
||||||
BigInteger nanos = Utils.toNanoCoins(1, 0);
|
BigInteger nanos = Utils.toNanoCoins(1, 0);
|
||||||
Transaction t1 = createFakeTx(nanos, myAddress);
|
Transaction t1 = createFakeTx(params, nanos, myAddress);
|
||||||
wallet.receive(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
wallet.receive(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||||
// Create a send to a merchant.
|
// Create a send to a merchant.
|
||||||
Transaction send1 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 50));
|
Transaction send1 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 50));
|
||||||
|
|||||||
Reference in New Issue
Block a user