Cleanup and finish merge with 0.3

This commit is contained in:
Miron Cuperman
2011-12-13 12:04:04 -08:00
committed by Miron Cuperman
parent f731c8591d
commit b7b1c039dc
4 changed files with 73 additions and 46 deletions

View File

@@ -245,6 +245,12 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.8.2.2</version>
</dependency>
<!-- In Android these libraries are incomplete, <!-- In Android these libraries are incomplete,
so use the dedicated artifact (bitcoinj-<version>-android.jar) --> so use the dedicated artifact (bitcoinj-<version>-android.jar) -->
<dependency> <dependency>
@@ -263,4 +269,4 @@
<slf4j.version>1.6.2</slf4j.version> <slf4j.version>1.6.2</slf4j.version>
</properties> </properties>
</project> </project>

View File

@@ -16,14 +16,26 @@
package com.google.bitcoin.examples; package com.google.bitcoin.examples;
import com.google.bitcoin.core.*; import com.google.bitcoin.core.AbstractWalletEventListener;
import com.google.bitcoin.core.Address;
import com.google.bitcoin.core.BlockChain;
import com.google.bitcoin.core.ECKey;
import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.core.PeerAddress;
import com.google.bitcoin.core.PeerGroup;
import com.google.bitcoin.core.ScriptException;
import com.google.bitcoin.core.StoredBlock;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.TransactionInput;
import com.google.bitcoin.core.Utils;
import com.google.bitcoin.core.Wallet;
import com.google.bitcoin.store.BlockStoreException;
import com.google.bitcoin.store.DerbyBlockStore;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/** /**
* PingService demonstrates basic usage of the library. It sits on the network and when it receives coins, simply * PingService demonstrates basic usage of the library. It sits on the network and when it receives coins, simply
@@ -61,14 +73,14 @@ public class DerbyPingService {
// Connect to the localhost node. One minute timeout since we won't try any other peers // Connect to the localhost node. One minute timeout since we won't try any other peers
System.out.println("Connecting ..."); System.out.println("Connecting ...");
NetworkConnection conn = new NetworkConnection(InetAddress.getLocalHost(), params,
blockStore.getChainHead().getHeight(), 60000);
BlockChain chain = new BlockChain(params, wallet, blockStore); BlockChain chain = new BlockChain(params, wallet, blockStore);
final Peer peer = new Peer(params, conn, chain); final PeerGroup peerGroup = new PeerGroup(blockStore, params, chain);
peer.start(); peerGroup.addAddress(new PeerAddress(InetAddress.getLocalHost()));
peerGroup.start();
// We want to know when the balance changes. // We want to know when the balance changes.
wallet.addEventListener(new WalletEventListener() { wallet.addEventListener(new AbstractWalletEventListener() {
@Override
public void onCoinsReceived(Wallet w, Transaction tx, BigInteger prevBalance, BigInteger newBalance) { public void onCoinsReceived(Wallet w, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
// Running on a peer thread. // Running on a peer thread.
assert !newBalance.equals(BigInteger.ZERO); assert !newBalance.equals(BigInteger.ZERO);
@@ -81,7 +93,7 @@ public class DerbyPingService {
BigInteger value = tx.getValueSentToMe(w); BigInteger value = tx.getValueSentToMe(w);
System.out.println("Received " + Utils.bitcoinValueToFriendlyString(value) + " from " + from.toString()); System.out.println("Received " + Utils.bitcoinValueToFriendlyString(value) + " from " + from.toString());
// Now send the coins back! // Now send the coins back!
Transaction sendTx = w.sendCoins(peer, from, value); Transaction sendTx = w.sendCoins(peerGroup, from, value);
assert sendTx != null; // We should never try to send more coins than we have! assert sendTx != null; // We should never try to send more coins than we have!
System.out.println("Sent coins back! Transaction hash is " + sendTx.getHashAsString()); System.out.println("Sent coins back! Transaction hash is " + sendTx.getHashAsString());
w.saveToFile(walletFile); w.saveToFile(walletFile);
@@ -96,18 +108,8 @@ public class DerbyPingService {
} }
}); });
CountDownLatch progress = peer.startBlockChainDownload(); peerGroup.downloadBlockChain();
long max = progress.getCount(); // Racy but no big deal.
if (max > 0) {
System.out.println("Downloading block chain. " + (max > 1000 ? "This may take a while." : ""));
long current = max;
while (current > 0) {
double pct = 100.0 - (100.0 * (current / (double)max));
System.out.println(String.format("Chain download %d%% done", (int)pct));
progress.await(1, TimeUnit.SECONDS);
current = progress.getCount();
}
}
System.out.println("Send coins to: " + key.toAddress(params).toString()); System.out.println("Send coins to: " + key.toAddress(params).toString());
System.out.println("Waiting for coins to arrive. Press Ctrl-C to quit."); System.out.println("Waiting for coins to arrive. Press Ctrl-C to quit.");
// The peer thread keeps us alive until something kills the process. // The peer thread keeps us alive until something kills the process.
@@ -117,7 +119,7 @@ public class DerbyPingService {
* @param blockStore * @param blockStore
* @throws BlockStoreException * @throws BlockStoreException
*/ */
private static void iterateAll(DerbyBlockStore blockStore) throws BlockStoreException { static void iterateAll(DerbyBlockStore blockStore) throws BlockStoreException {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
StoredBlock block = blockStore.getChainHead(); StoredBlock block = blockStore.getChainHead();
int count = 0; int count = 0;

View File

@@ -1,4 +1,4 @@
package com.google.bitcoin.core; package com.google.bitcoin.store;
/** /**
* Copyright 2011 Google Inc. * Copyright 2011 Google Inc.
@@ -16,6 +16,14 @@ package com.google.bitcoin.core;
* limitations under the License. * limitations under the License.
*/ */
import com.google.bitcoin.core.Block;
import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.core.ProtocolException;
import com.google.bitcoin.core.Sha256Hash;
import com.google.bitcoin.core.StoredBlock;
import com.google.bitcoin.core.Utils;
import com.google.bitcoin.core.VerificationException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -33,9 +41,6 @@ import java.sql.Statement;
* @author miron@google.com (Miron Cuperman) * @author miron@google.com (Miron Cuperman)
*/ */
public class DerbyBlockStore implements BlockStore { public class DerbyBlockStore implements BlockStore {
/**
*
*/
private static final int COMMIT_INTERVAL = 2 * 1000; private static final int COMMIT_INTERVAL = 2 * 1000;
private static final Logger log = LoggerFactory.getLogger(DerbyBlockStore.class); private static final Logger log = LoggerFactory.getLogger(DerbyBlockStore.class);
@@ -161,13 +166,13 @@ public class DerbyBlockStore implements BlockStore {
if (!rs.next()) { if (!rs.next()) {
throw new BlockStoreException("corrupt Derby block store - no chain head pointer"); throw new BlockStoreException("corrupt Derby block store - no chain head pointer");
} }
byte[] hash = rs.getBytes(1); Sha256Hash hash = new Sha256Hash(rs.getBytes(1));
this.chainHeadBlock = get(hash); this.chainHeadBlock = get(hash);
if (this.chainHeadBlock == null) if (this.chainHeadBlock == null)
{ {
throw new BlockStoreException("corrupt Derby block store - head block not found"); throw new BlockStoreException("corrupt Derby block store - head block not found");
} }
this.chainHeadHash = new Sha256Hash(hash); this.chainHeadHash = hash;
} }
private void createNewStore(NetworkParameters params) throws BlockStoreException { private void createNewStore(NetworkParameters params) throws BlockStoreException {
@@ -178,7 +183,7 @@ public class DerbyBlockStore implements BlockStore {
StoredBlock storedGenesis = new StoredBlock(genesis, StoredBlock storedGenesis = new StoredBlock(genesis,
genesis.getWork(), 0); genesis.getWork(), 0);
this.chainHeadBlock = storedGenesis; this.chainHeadBlock = storedGenesis;
this.chainHeadHash = new Sha256Hash(storedGenesis.getHeader().getHash()); this.chainHeadHash = storedGenesis.getHeader().getHash();
setChainHead(storedGenesis); setChainHead(storedGenesis);
put(storedGenesis); put(storedGenesis);
} catch (VerificationException e1) { } catch (VerificationException e1) {
@@ -199,13 +204,12 @@ public class DerbyBlockStore implements BlockStore {
} }
} }
@Override
public void put(StoredBlock stored) throws BlockStoreException { public void put(StoredBlock stored) throws BlockStoreException {
try { try {
PreparedStatement s = PreparedStatement s =
conn.prepareStatement("INSERT INTO blocks(hash, chainWork, height, header)" conn.prepareStatement("INSERT INTO blocks(hash, chainWork, height, header)"
+ " VALUES(?, ?, ?, ?)"); + " VALUES(?, ?, ?, ?)");
s.setBytes(1, stored.getHeader().getHash()); s.setBytes(1, stored.getHeader().getHash().getBytes());
s.setBytes(2, stored.getChainWork().toByteArray()); s.setBytes(2, stored.getChainWork().toByteArray());
s.setLong(3, stored.getHeight()); s.setLong(3, stored.getHeight());
s.setBytes(4, stored.getHeader().bitcoinSerialize()); s.setBytes(4, stored.getHeader().bitcoinSerialize());
@@ -217,15 +221,14 @@ public class DerbyBlockStore implements BlockStore {
} }
} }
@Override public StoredBlock get(Sha256Hash hash) throws BlockStoreException {
public StoredBlock get(byte[] hash) throws BlockStoreException {
// Optimize for chain head // Optimize for chain head
if (chainHeadHash != null && chainHeadHash.equals(new Sha256Hash(hash))) if (chainHeadHash != null && chainHeadHash.equals(hash))
return chainHeadBlock; return chainHeadBlock;
try { try {
PreparedStatement s = conn PreparedStatement s = conn
.prepareStatement("SELECT chainWork, height, header FROM blocks WHERE hash = ?"); .prepareStatement("SELECT chainWork, height, header FROM blocks WHERE hash = ?");
s.setBytes(1, hash); s.setBytes(1, hash.getBytes());
ResultSet results = s.executeQuery(); ResultSet results = s.executeQuery();
if (!results.next()) { if (!results.next()) {
return null; return null;
@@ -240,7 +243,7 @@ public class DerbyBlockStore implements BlockStore {
stored = new StoredBlock(params.genesisBlock.cloneAsHeader(), stored = new StoredBlock(params.genesisBlock.cloneAsHeader(),
params.genesisBlock.getWork(), 0); params.genesisBlock.getWork(), 0);
} else { } else {
b.verify(); b.verifyHeader();
stored = new StoredBlock(b, chainWork, height); stored = new StoredBlock(b, chainWork, height);
} }
return stored; return stored;
@@ -257,21 +260,19 @@ public class DerbyBlockStore implements BlockStore {
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Override
public StoredBlock getChainHead() throws BlockStoreException { public StoredBlock getChainHead() throws BlockStoreException {
return chainHeadBlock; return chainHeadBlock;
} }
@Override
public void setChainHead(StoredBlock chainHead) throws BlockStoreException { public void setChainHead(StoredBlock chainHead) throws BlockStoreException {
byte[] hash = chainHead.getHeader().getHash(); Sha256Hash hash = chainHead.getHeader().getHash();
this.chainHeadHash = new Sha256Hash(hash); this.chainHeadHash = hash;
this.chainHeadBlock = chainHead; this.chainHeadBlock = chainHead;
try { try {
PreparedStatement s = conn PreparedStatement s = conn
.prepareStatement("UPDATE settings SET value = ? WHERE name = ?"); .prepareStatement("UPDATE settings SET value = ? WHERE name = ?");
s.setString(2, CHAIN_HEAD_SETTING); s.setString(2, CHAIN_HEAD_SETTING);
s.setBytes(1, hash); s.setBytes(1, hash.getBytes());
s.executeUpdate(); s.executeUpdate();
s.close(); s.close();
startCommitter(); startCommitter();
@@ -316,7 +317,6 @@ public class DerbyBlockStore implements BlockStore {
// A thread that is guaranteed to try a commit as long as // A thread that is guaranteed to try a commit as long as
// committerThread is not null // committerThread is not null
Runnable committer = new Runnable() { Runnable committer = new Runnable() {
@Override
public void run() { public void run() {
try { try {
log.info("commit scheduled"); log.info("commit scheduled");

View File

@@ -13,20 +13,30 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.bitcoin.core; package com.google.bitcoin.store;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import com.google.bitcoin.core.Address;
import com.google.bitcoin.core.ECKey;
import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.core.StoredBlock;
import org.junit.Test; import org.junit.Test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
public class DerbyBlockStoreTest { public class DerbyBlockStoreTest {
/** /**
* * This path will be deleted recursively!
*/ */
private static final String DB_NAME = ".bitcoinj.unittest.derby"; private static final String DB_NAME = ".bitcoinj.unittest.derby";
@Test @Test
public void testStorage() throws Exception { public void testStorage() throws Exception {
deleteRecursively(new File(DB_NAME));
NetworkParameters params = NetworkParameters.unitTests(); NetworkParameters params = NetworkParameters.unitTests();
Address to = new ECKey().toAddress(params); Address to = new ECKey().toAddress(params);
DerbyBlockStore store = new DerbyBlockStore(params, DB_NAME); DerbyBlockStore store = new DerbyBlockStore(params, DB_NAME);
@@ -49,4 +59,13 @@ public class DerbyBlockStoreTest {
assertEquals(b1, store.getChainHead()); assertEquals(b1, store.getChainHead());
store.dump(); store.dump();
} }
void deleteRecursively(File f) throws IOException {
if (f.isDirectory()) {
for (File c : f.listFiles())
deleteRecursively(c);
}
if (!f.delete())
throw new FileNotFoundException("Failed to delete file: " + f);
}
} }