diff --git a/core/pom.xml b/core/pom.xml index 625f89e2..15395d57 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -158,11 +158,6 @@ test - - org.apache.derby - derby - - com.h2database h2 diff --git a/core/src/main/java/com/google/bitcoin/store/DerbyBlockStore.java b/core/src/main/java/com/google/bitcoin/store/DerbyBlockStore.java deleted file mode 100644 index 6a40dfbb..00000000 --- a/core/src/main/java/com/google/bitcoin/store/DerbyBlockStore.java +++ /dev/null @@ -1,331 +0,0 @@ -package com.google.bitcoin.store; - -/** - * 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. - */ - -import com.google.bitcoin.core.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.math.BigInteger; -import java.sql.*; - -/** - * A block store using the Apache Derby pure-java embedded database. - * - * @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); - - private StoredBlock chainHeadBlock; - private Sha256Hash chainHeadHash; - private NetworkParameters params; - private Connection conn; - - private String dbName; - - private Thread committerThread; - - static final String driver = "org.apache.derby.jdbc.EmbeddedDriver"; - static final String CREATE_SETTINGS_TABLE = "CREATE TABLE settings ( " - + "name VARCHAR(32) NOT NULL CONSTRAINT settings_pk PRIMARY KEY," - + "value BLOB" - + ")"; - static final String CREATE_BLOCKS_TABLE = "CREATE TABLE blocks ( " - + "hash CHAR(32) FOR BIT DATA NOT NULL CONSTRAINT blocks_pk PRIMARY KEY," - + "chainWork BLOB NOT NULL," - + "height BIGINT NOT NULL," - + "header BLOB NOT NULL" - + ")"; - static final String CHAIN_HEAD_SETTING = "chainhead"; - - public static void main(String[] args) throws Exception { - DerbyBlockStore store = new DerbyBlockStore(NetworkParameters.testNet(), ".bitcoinj-test.derby"); - store.resetStore(); - } - - public synchronized void close() { - String connectionURL = "jdbc:derby:" + dbName + ";shutdown=true"; - try { - if (conn != null) { - conn.commit(); - conn = null; - } - if (committerThread != null) - committerThread.interrupt(); - DriverManager.getConnection(connectionURL); - } catch (SQLException ex) { - if (( (ex.getErrorCode() == 45000) - && ("08006".equals(ex.getSQLState()) ))) { - // we got the expected exception - log.info("Derby shut down normally"); - // Note that for single database shutdown, the expected - // SQL state is "08006", and the error code is 45000. - } - else { - throw new RuntimeException(ex); - } - } - } - - private synchronized void commit() throws BlockStoreException { - try { - if (conn != null) - conn.commit(); - } catch (SQLException ex) { - log.error("commit failed", ex); - throw new BlockStoreException(ex); - } - } - - public DerbyBlockStore(NetworkParameters params, String dbName) throws BlockStoreException { - this.params = params; - this.dbName = dbName; - String connectionURL = "jdbc:derby:" + dbName + ";create=true"; - - try { - Class.forName(driver); - log.info(driver + " loaded. "); - } catch (java.lang.ClassNotFoundException e) { - log.error("check CLASSPATH for Derby jar ", e); - } - - try { - conn = DriverManager.getConnection(connectionURL); - conn.setAutoCommit(false); - log.info("Connected to database " + connectionURL); - - // Create tables if needed - if (!isTableExists("settings")) { - createTables(); - } - initFromDatabase(); - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } - } - - public void resetStore() throws BlockStoreException { - Statement s; - try { - s = conn.createStatement(); - s.executeUpdate("DROP TABLE settings"); - s.executeUpdate("DROP TABLE blocks"); - s.close(); - createTables(); - initFromDatabase(); - startCommitter(); - } catch (SQLException ex) { - throw new RuntimeException(ex); - } - } - - private void createTables() throws SQLException, BlockStoreException { - Statement s = conn.createStatement(); - log.debug("DerbyBlockStore : CREATE blocks table"); - s.executeUpdate(CREATE_BLOCKS_TABLE); - - log.debug("DerbyBlockStore : CREATE settings table"); - s.executeUpdate(CREATE_SETTINGS_TABLE); - - s.executeUpdate("INSERT INTO settings(name, value) VALUES('chainhead', NULL)"); - createNewStore(params); - } - - private void initFromDatabase() throws SQLException, BlockStoreException { - Statement s = conn.createStatement(); - ResultSet rs = s.executeQuery("SELECT value FROM settings WHERE name = 'chainhead'"); - if (!rs.next()) { - throw new BlockStoreException("corrupt Derby block store - no chain head pointer"); - } - 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 = hash; - } - - private void createNewStore(NetworkParameters params) throws BlockStoreException { - try { - // Set up the genesis block. When we start out fresh, it is by - // definition the top of the chain. - Block genesis = params.genesisBlock.cloneAsHeader(); - StoredBlock storedGenesis = new StoredBlock(genesis, - genesis.getWork(), 0); - this.chainHeadBlock = storedGenesis; - this.chainHeadHash = storedGenesis.getHeader().getHash(); - setChainHead(storedGenesis); - put(storedGenesis); - } catch (VerificationException e1) { - throw new RuntimeException(e1); // Cannot happen. - } - } - - private boolean isTableExists(String table) throws SQLException { - Statement s = conn.createStatement(); - try { - ResultSet results = s.executeQuery("SELECT * FROM " + table + " WHERE 1 = 2"); - results.close(); - return true; - } catch (SQLException ex) { - return false; - } finally { - s.close(); - } - } - - 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().getBytes()); - s.setBytes(2, stored.getChainWork().toByteArray()); - s.setLong(3, stored.getHeight()); - s.setBytes(4, stored.getHeader().unsafeBitcoinSerialize()); - s.executeUpdate(); - s.close(); - startCommitter(); - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } - } - - public StoredBlock get(Sha256Hash hash) throws BlockStoreException { - // Optimize for chain head - 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.getBytes()); - ResultSet results = s.executeQuery(); - if (!results.next()) { - return null; - } - // Parse it. - - BigInteger chainWork = new BigInteger(results.getBytes(1)); - int height = results.getInt(2); - Block b = new Block(params, results.getBytes(3)); - StoredBlock stored; - b.verifyHeader(); - stored = new StoredBlock(b, chainWork, height); - return stored; - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } catch (ProtocolException e) { - // Corrupted database. - throw new BlockStoreException(e); - } catch (VerificationException e) { - // Should not be able to happen unless the database contains bad - // blocks. - throw new BlockStoreException(e); - } - } - - public StoredBlock getChainHead() throws BlockStoreException { - return chainHeadBlock; - } - - public void setChainHead(StoredBlock chainHead) throws BlockStoreException { - 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.getBytes()); - s.executeUpdate(); - s.close(); - startCommitter(); - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } - } - - public void dump() throws SQLException { - Statement s = conn.createStatement(); - System.out.println("settings"); - ResultSet rs = s.executeQuery("SELECT name, value FROM settings"); - while (rs.next()) { - System.out.print(rs.getString(1)); - System.out.print(" "); - System.out.println(Utils.bytesToHexString(rs.getBytes(2))); - } - rs.close(); - System.out.println("blocks"); - rs = s.executeQuery("SELECT hash, chainWork, height, header FROM blocks"); - while (rs.next()) { - System.out.print(Utils.bytesToHexString(rs.getBytes(1))); - System.out.print(" "); - System.out.print(Utils.bytesToHexString(rs.getBytes(2))); - System.out.print(" "); - System.out.print(rs.getInt(3)); - System.out.print(" "); - //System.out.print(Utils.bytesToHexString(rs.getBytes(4))); - System.out.println(); - } - rs.close(); - System.out.println("end"); - s.close(); - } - - protected synchronized void startCommitter() { - if (committerThread != null) - return; - - // A thread that is guaranteed to try a commit as long as - // committerThread is not null - Runnable committer = new Runnable() { - public void run() { - try { - log.info("commit scheduled"); - Thread.sleep(COMMIT_INTERVAL); - } catch (InterruptedException ex) { - // ignore - } - synchronized (DerbyBlockStore.this) { - try { - if (conn != null) { - commit(); - log.info("commit success"); - } - else { - log.info("committer noticed that we are shutting down"); - } - } - catch (BlockStoreException e) { - log.warn("commit failed"); - // ignore - } - finally { - committerThread = null; - } - } - } - }; - - committerThread = new Thread(committer, "DerbyBlockStore committer"); - committerThread.start(); - } -} diff --git a/core/src/test/java/com/google/bitcoin/store/DerbyBlockStoreTest.java b/core/src/test/java/com/google/bitcoin/store/DerbyBlockStoreTest.java deleted file mode 100644 index 9a3d152e..00000000 --- a/core/src/test/java/com/google/bitcoin/store/DerbyBlockStoreTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * 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.store; - -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.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import java.io.File; - -import static org.junit.Assert.assertEquals; - -public class DerbyBlockStoreTest { - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - @Before - public void shutUp() { - // Prevent Derby writing a useless error log file. - System.getProperties().setProperty("derby.stream.error.file", ""); - } - - @Test - public void testStorage() throws Exception { - File file = new File(folder.getRoot(), "derby"); - String path = file.getAbsolutePath(); - NetworkParameters params = NetworkParameters.unitTests(); - Address to = new ECKey().toAddress(params); - DerbyBlockStore store = new DerbyBlockStore(params, path); - store.resetStore(); - store.dump(); - // Check the first block in a new store is the genesis block. - StoredBlock genesis = store.getChainHead(); - assertEquals(params.genesisBlock, genesis.getHeader()); - - // Build a new block. - StoredBlock b1 = genesis.build(genesis.getHeader().createNextBlock(to).cloneAsHeader()); - store.put(b1); - store.setChainHead(b1); - store.dump(); - // Check we can get it back out again if we rebuild the store object. - store = new DerbyBlockStore(params, path); - StoredBlock b2 = store.get(b1.getHeader().getHash()); - assertEquals(b1, b2); - // Check the chain head was stored correctly also. - assertEquals(b1, store.getChainHead()); - - StoredBlock g1 = store.get(params.genesisBlock.getHash()); - assertEquals(params.genesisBlock, g1.getHeader()); - store.dump(); - store.close(); - } -} diff --git a/examples/src/main/java/com/google/bitcoin/examples/DerbyPingService.java b/examples/src/main/java/com/google/bitcoin/examples/DerbyPingService.java deleted file mode 100644 index 6e45eb75..00000000 --- a/examples/src/main/java/com/google/bitcoin/examples/DerbyPingService.java +++ /dev/null @@ -1,130 +0,0 @@ -/** - * 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.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.ExecutionException; - -/** - * PingService demonstrates basic usage of the library. It sits on the network and when it receives coins, simply - * sends them right back to the previous owner, determined rather arbitrarily by the address of the first input. - */ -public class DerbyPingService { - public static void main(String[] args) throws Exception { - boolean testNet = args.length > 0 && args[0].equalsIgnoreCase("testnet"); - final NetworkParameters params = testNet ? NetworkParameters.testNet() : NetworkParameters.prodNet(); - String suffix = testNet ? "testnet" : "prodnet"; - String filePrefix = "pingservice-" + suffix; - - // Try to read the wallet from storage, create a new one if not possible. - Wallet wallet; - final File walletFile = new File(filePrefix + ".wallet"); - try { - wallet = Wallet.loadFromFile(walletFile); - } catch (IOException e) { - wallet = new Wallet(params); - wallet.keychain.add(new ECKey()); - wallet.saveToFile(walletFile); - } - // Fetch the first key in the wallet (should be the only key). - ECKey key = wallet.keychain.get(0); - - // Load the block chain, if there is one stored locally. - System.out.println("Reading block store from disk"); - long time = System.currentTimeMillis(); - DerbyBlockStore blockStore = new DerbyBlockStore(params, ".bitcoinj-" + suffix); - System.out.println("Opened block store in " + (System.currentTimeMillis() - time) + " ms"); - - //iterateAll(blockStore); - //blockStore.close(); - //System.exit(1); - - // Connect to the localhost node. One minute timeout since we won't try any other peers - System.out.println("Connecting ..."); - BlockChain chain = new BlockChain(params, wallet, blockStore); - final PeerGroup peerGroup = new PeerGroup(params, chain); - peerGroup.addAddress(new PeerAddress(InetAddress.getLocalHost())); - peerGroup.addWallet(wallet); - peerGroup.start(); - - // We want to know when the balance changes. - 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); - // It's impossible to pick one specific identity that you receive coins from in BitCoin as there - // could be inputs from many addresses. So instead we just pick the first and assume they were all - // owned by the same person. - try { - TransactionInput input = tx.getInputs().get(0); - Address from = input.getFromAddress(); - BigInteger value = tx.getValueSentToMe(w); - System.out.println("Received " + Utils.bitcoinValueToFriendlyString(value) + " from " + from.toString()); - // Now send the coins back! - Wallet.SendResult sendTx = w.sendCoins(peerGroup, from, value); - assert sendTx.tx != null; // We should never try to send more coins than we have! - System.out.println("Sent coins back! Transaction hash is " + sendTx.tx.getHashAsString()); - sendTx.broadcastComplete.get(); - w.saveToFile(walletFile); - } catch (ScriptException e) { - // If we didn't understand the scriptSig, just crash. - e.printStackTrace(); - throw new RuntimeException(e); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throw new RuntimeException(e); - } - } - }); - - 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. - } - - /** - * @param blockStore - * @throws BlockStoreException - */ - static void iterateAll(DerbyBlockStore blockStore) throws BlockStoreException { - long time = System.currentTimeMillis(); - StoredBlock block = blockStore.getChainHead(); - int count = 0; - while (block != null) { - count++; - if (count % 1000 == 0) - System.out.println("iterated " + count); - block = block.getPrev(blockStore); - } - System.out.println("iterated " + count); - System.out.println("Iterated block store in " + (System.currentTimeMillis() - time) + " ms"); - } -}