diff --git a/pom.xml b/pom.xml index 8bfd8ebd..e9aa3f16 100644 --- a/pom.xml +++ b/pom.xml @@ -245,6 +245,12 @@ true + + org.apache.derby + derby + 10.8.2.2 + + @@ -263,4 +269,4 @@ 1.6.2 - \ No newline at end of file + diff --git a/src/com/google/bitcoin/examples/DerbyPingService.java b/src/com/google/bitcoin/examples/DerbyPingService.java index a168facc..8808b38f 100644 --- a/src/com/google/bitcoin/examples/DerbyPingService.java +++ b/src/com/google/bitcoin/examples/DerbyPingService.java @@ -16,14 +16,26 @@ 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.IOException; import java.math.BigInteger; 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 @@ -61,14 +73,14 @@ public class DerbyPingService { // Connect to the localhost node. One minute timeout since we won't try any other peers System.out.println("Connecting ..."); - NetworkConnection conn = new NetworkConnection(InetAddress.getLocalHost(), params, - blockStore.getChainHead().getHeight(), 60000); BlockChain chain = new BlockChain(params, wallet, blockStore); - final Peer peer = new Peer(params, conn, chain); - peer.start(); + final PeerGroup peerGroup = new PeerGroup(blockStore, params, chain); + peerGroup.addAddress(new PeerAddress(InetAddress.getLocalHost())); + peerGroup.start(); // 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) { // Running on a peer thread. assert !newBalance.equals(BigInteger.ZERO); @@ -81,7 +93,7 @@ public class DerbyPingService { BigInteger value = tx.getValueSentToMe(w); System.out.println("Received " + Utils.bitcoinValueToFriendlyString(value) + " from " + from.toString()); // 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! System.out.println("Sent coins back! Transaction hash is " + sendTx.getHashAsString()); w.saveToFile(walletFile); @@ -96,18 +108,8 @@ public class DerbyPingService { } }); - CountDownLatch progress = peer.startBlockChainDownload(); - 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(); - } - } + peerGroup.downloadBlockChain(); + System.out.println("Send coins to: " + key.toAddress(params).toString()); System.out.println("Waiting for coins to arrive. Press Ctrl-C to quit."); // The peer thread keeps us alive until something kills the process. @@ -117,7 +119,7 @@ public class DerbyPingService { * @param blockStore * @throws BlockStoreException */ - private static void iterateAll(DerbyBlockStore blockStore) throws BlockStoreException { + static void iterateAll(DerbyBlockStore blockStore) throws BlockStoreException { long time = System.currentTimeMillis(); StoredBlock block = blockStore.getChainHead(); int count = 0; diff --git a/src/com/google/bitcoin/core/DerbyBlockStore.java b/src/com/google/bitcoin/store/DerbyBlockStore.java similarity index 91% rename from src/com/google/bitcoin/core/DerbyBlockStore.java rename to src/com/google/bitcoin/store/DerbyBlockStore.java index 4c22aea2..0d20ea17 100644 --- a/src/com/google/bitcoin/core/DerbyBlockStore.java +++ b/src/com/google/bitcoin/store/DerbyBlockStore.java @@ -1,4 +1,4 @@ -package com.google.bitcoin.core; +package com.google.bitcoin.store; /** * Copyright 2011 Google Inc. @@ -16,6 +16,14 @@ package com.google.bitcoin.core; * 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.LoggerFactory; @@ -33,9 +41,6 @@ import java.sql.Statement; * @author miron@google.com (Miron Cuperman) */ public class DerbyBlockStore implements BlockStore { - /** - * - */ private static final int COMMIT_INTERVAL = 2 * 1000; private static final Logger log = LoggerFactory.getLogger(DerbyBlockStore.class); @@ -161,13 +166,13 @@ public class DerbyBlockStore implements BlockStore { if (!rs.next()) { 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); if (this.chainHeadBlock == null) { 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 { @@ -178,7 +183,7 @@ public class DerbyBlockStore implements BlockStore { StoredBlock storedGenesis = new StoredBlock(genesis, genesis.getWork(), 0); this.chainHeadBlock = storedGenesis; - this.chainHeadHash = new Sha256Hash(storedGenesis.getHeader().getHash()); + this.chainHeadHash = storedGenesis.getHeader().getHash(); setChainHead(storedGenesis); put(storedGenesis); } catch (VerificationException e1) { @@ -199,13 +204,12 @@ public class DerbyBlockStore implements BlockStore { } } - @Override public void put(StoredBlock stored) throws BlockStoreException { try { PreparedStatement s = conn.prepareStatement("INSERT INTO blocks(hash, chainWork, height, header)" + " VALUES(?, ?, ?, ?)"); - s.setBytes(1, stored.getHeader().getHash()); + s.setBytes(1, stored.getHeader().getHash().getBytes()); s.setBytes(2, stored.getChainWork().toByteArray()); s.setLong(3, stored.getHeight()); s.setBytes(4, stored.getHeader().bitcoinSerialize()); @@ -217,15 +221,14 @@ public class DerbyBlockStore implements BlockStore { } } - @Override - public StoredBlock get(byte[] hash) throws BlockStoreException { + public StoredBlock get(Sha256Hash hash) throws BlockStoreException { // Optimize for chain head - if (chainHeadHash != null && chainHeadHash.equals(new Sha256Hash(hash))) + if (chainHeadHash != null && chainHeadHash.equals(hash)) return chainHeadBlock; try { PreparedStatement s = conn - .prepareStatement("SELECT chainWork, height, header FROM blocks WHERE hash = ?"); - s.setBytes(1, hash); + .prepareStatement("SELECT chainWork, height, header FROM blocks WHERE hash = ?"); + s.setBytes(1, hash.getBytes()); ResultSet results = s.executeQuery(); if (!results.next()) { return null; @@ -240,7 +243,7 @@ public class DerbyBlockStore implements BlockStore { stored = new StoredBlock(params.genesisBlock.cloneAsHeader(), params.genesisBlock.getWork(), 0); } else { - b.verify(); + b.verifyHeader(); stored = new StoredBlock(b, chainWork, height); } return stored; @@ -257,21 +260,19 @@ public class DerbyBlockStore implements BlockStore { } @SuppressWarnings("unused") - @Override public StoredBlock getChainHead() throws BlockStoreException { return chainHeadBlock; } - @Override public void setChainHead(StoredBlock chainHead) throws BlockStoreException { - byte[] hash = chainHead.getHeader().getHash(); - this.chainHeadHash = new Sha256Hash(hash); + Sha256Hash hash = chainHead.getHeader().getHash(); + this.chainHeadHash = hash; this.chainHeadBlock = chainHead; try { PreparedStatement s = conn .prepareStatement("UPDATE settings SET value = ? WHERE name = ?"); s.setString(2, CHAIN_HEAD_SETTING); - s.setBytes(1, hash); + s.setBytes(1, hash.getBytes()); s.executeUpdate(); s.close(); startCommitter(); @@ -316,7 +317,6 @@ public class DerbyBlockStore implements BlockStore { // A thread that is guaranteed to try a commit as long as // committerThread is not null Runnable committer = new Runnable() { - @Override public void run() { try { log.info("commit scheduled"); diff --git a/tests/com/google/bitcoin/core/DerbyBlockStoreTest.java b/tests/com/google/bitcoin/store/DerbyBlockStoreTest.java similarity index 73% rename from tests/com/google/bitcoin/core/DerbyBlockStoreTest.java rename to tests/com/google/bitcoin/store/DerbyBlockStoreTest.java index a7645fce..e8519a95 100644 --- a/tests/com/google/bitcoin/core/DerbyBlockStoreTest.java +++ b/tests/com/google/bitcoin/store/DerbyBlockStoreTest.java @@ -13,20 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.bitcoin.core; +package com.google.bitcoin.store; 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 java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + public class DerbyBlockStoreTest { /** - * + * This path will be deleted recursively! */ private static final String DB_NAME = ".bitcoinj.unittest.derby"; @Test public void testStorage() throws Exception { + deleteRecursively(new File(DB_NAME)); NetworkParameters params = NetworkParameters.unitTests(); Address to = new ECKey().toAddress(params); DerbyBlockStore store = new DerbyBlockStore(params, DB_NAME); @@ -49,4 +59,13 @@ public class DerbyBlockStoreTest { assertEquals(b1, store.getChainHead()); 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); + } }