diff --git a/core/build.gradle b/core/build.gradle index 07cee6a6b..a4d73cf19 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -19,7 +19,6 @@ dependencies { testImplementation 'org.easymock:easymock:4.3' testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2.1' testImplementation 'org.slf4j:slf4j-jdk14:1.7.36' - testImplementation 'com.h2database:h2:1.3.176' testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.10' testImplementation 'org.hamcrest:hamcrest-library:2.2' } diff --git a/core/src/main/java/org/bitcoinj/core/AbstractBlockChain.java b/core/src/main/java/org/bitcoinj/core/AbstractBlockChain.java index fd5f98d4f..c388b9708 100644 --- a/core/src/main/java/org/bitcoinj/core/AbstractBlockChain.java +++ b/core/src/main/java/org/bitcoinj/core/AbstractBlockChain.java @@ -25,7 +25,6 @@ import org.bitcoinj.core.listeners.TransactionReceivedInBlockListener; import org.bitcoinj.script.ScriptException; import org.bitcoinj.store.BlockStore; import org.bitcoinj.store.BlockStoreException; -import org.bitcoinj.store.H2FullPrunedBlockStore; import org.bitcoinj.store.SPVBlockStore; import org.bitcoinj.utils.ListenableCompletableFuture; import org.bitcoinj.utils.ListenerRegistration; @@ -74,7 +73,7 @@ import static com.google.common.base.Preconditions.checkState; *

There are two subclasses of AbstractBlockChain that are useful: {@link BlockChain}, which is the simplest * class and implements simplified payment verification. This is a lightweight and efficient mode that does * not verify the contents of blocks, just their headers. A {@link FullPrunedBlockChain} paired with a - * {@link H2FullPrunedBlockStore} implements full verification, which is equivalent to + * {@link org.bitcoinj.store.MemoryFullPrunedBlockStore} implements full verification, which is equivalent to * Bitcoin Core. To learn more about the alternative security models, please consult the articles on the * website.

* diff --git a/core/src/main/java/org/bitcoinj/store/DatabaseFullPrunedBlockStore.java b/core/src/main/java/org/bitcoinj/store/DatabaseFullPrunedBlockStore.java deleted file mode 100644 index e75a0daf6..000000000 --- a/core/src/main/java/org/bitcoinj/store/DatabaseFullPrunedBlockStore.java +++ /dev/null @@ -1,1263 +0,0 @@ -/* - * Copyright 2014 BitPOS Pty Ltd. - * Copyright 2014 Andreas Schildbach. - * Copyright 2014 Kalpesh Parmar. - * - * 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 org.bitcoinj.store; - -import org.bitcoinj.base.utils.ByteUtils; -import org.bitcoinj.core.Address; -import org.bitcoinj.core.Block; -import org.bitcoinj.base.Coin; -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.ProtocolException; -import org.bitcoinj.base.Sha256Hash; -import org.bitcoinj.core.StoredBlock; -import org.bitcoinj.core.StoredUndoableBlock; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.core.TransactionOutputChanges; -import org.bitcoinj.core.UTXO; -import org.bitcoinj.core.UTXOProviderException; -import org.bitcoinj.core.VerificationException; -import org.bitcoinj.script.Script; -import org.bitcoinj.base.ScriptType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigInteger; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.sql.Types; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Properties; - -/** - *

A generic full pruned block store for a relational database. This generic class requires - * certain table structures for the block store.

- * - *

The following are the tables and field names/types that are assumed:

- * - * - * - * - * - * - *
setting table
Field NameType (generic)
namestring
valuebinary
- * - * - * - * - * - * - * - * - * - *
headers table
Field NameType (generic)
hashbinary
chainworkbinary
heightinteger
headerbinary
wasundoableboolean
- * - * - * - * - * - * - * - * - *
undoableblocks table
Field NameType (generic)
hashbinary
heightinteger
txoutchangesbinary
transactionsbinary
- * - * - * - * - * - * - * - * - * - * - * - * - *
openoutputs table
Field NameType (generic)
hashbinary
indexinteger
heightinteger
valueinteger
scriptbytesbinary
toaddressstring
addresstargetableinteger
coinbaseboolean
- */ -public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockStore { - private static final Logger log = LoggerFactory.getLogger(DatabaseFullPrunedBlockStore.class); - - private static final String CHAIN_HEAD_SETTING = "chainhead"; - private static final String VERIFIED_CHAIN_HEAD_SETTING = "verifiedchainhead"; - private static final String VERSION_SETTING = "version"; - - // Drop table SQL. - private static final String DROP_SETTINGS_TABLE = "DROP TABLE settings"; - private static final String DROP_HEADERS_TABLE = "DROP TABLE headers"; - private static final String DROP_UNDOABLE_TABLE = "DROP TABLE undoableblocks"; - private static final String DROP_OPEN_OUTPUT_TABLE = "DROP TABLE openoutputs"; - - // Queries SQL. - private static final String SELECT_SETTINGS_SQL = "SELECT value FROM settings WHERE name = ?"; - private static final String INSERT_SETTINGS_SQL = "INSERT INTO settings(name, value) VALUES(?, ?)"; - private static final String UPDATE_SETTINGS_SQL = "UPDATE settings SET value = ? WHERE name = ?"; - - private static final String SELECT_HEADERS_SQL = "SELECT chainwork, height, header, wasundoable FROM headers WHERE hash = ?"; - private static final String INSERT_HEADERS_SQL = "INSERT INTO headers(hash, chainwork, height, header, wasundoable) VALUES(?, ?, ?, ?, ?)"; - private static final String UPDATE_HEADERS_SQL = "UPDATE headers SET wasundoable=? WHERE hash=?"; - - private static final String SELECT_UNDOABLEBLOCKS_SQL = "SELECT txoutchanges, transactions FROM undoableblocks WHERE hash = ?"; - private static final String INSERT_UNDOABLEBLOCKS_SQL = "INSERT INTO undoableblocks(hash, height, txoutchanges, transactions) VALUES(?, ?, ?, ?)"; - private static final String UPDATE_UNDOABLEBLOCKS_SQL = "UPDATE undoableblocks SET txoutchanges=?, transactions=? WHERE hash = ?"; - private static final String DELETE_UNDOABLEBLOCKS_SQL = "DELETE FROM undoableblocks WHERE height <= ?"; - - private static final String SELECT_OPENOUTPUTS_SQL = "SELECT height, value, scriptbytes, coinbase, toaddress, addresstargetable FROM openoutputs WHERE hash = ? AND index = ?"; - private static final String SELECT_OPENOUTPUTS_COUNT_SQL = "SELECT COUNT(*) FROM openoutputs WHERE hash = ?"; - private static final String INSERT_OPENOUTPUTS_SQL = "INSERT INTO openoutputs (hash, index, height, value, scriptbytes, toaddress, addresstargetable, coinbase) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; - private static final String DELETE_OPENOUTPUTS_SQL = "DELETE FROM openoutputs WHERE hash = ? AND index = ?"; - - // Dump table SQL (this is just for data sizing statistics). - private static final String SELECT_DUMP_SETTINGS_SQL = "SELECT name, value FROM settings"; - private static final String SELECT_DUMP_HEADERS_SQL = "SELECT chainwork, header FROM headers"; - private static final String SELECT_DUMP_UNDOABLEBLOCKS_SQL = "SELECT txoutchanges, transactions FROM undoableblocks"; - private static final String SELECT_DUMP_OPENOUTPUTS_SQL = "SELECT value, scriptbytes FROM openoutputs"; - - private static final String SELECT_TRANSACTION_OUTPUTS_SQL = "SELECT hash, value, scriptbytes, height, index, coinbase, toaddress, addresstargetable FROM openoutputs where toaddress = ?"; - - // Select the balance of an address SQL. - private static final String SELECT_BALANCE_SQL = "select sum(value) from openoutputs where toaddress = ?"; - - // Tables exist SQL. - private static final String SELECT_CHECK_TABLES_EXIST_SQL = "SELECT * FROM settings WHERE 1 = 2"; - - // Compatibility SQL. - private static final String SELECT_COMPATIBILITY_COINBASE_SQL = "SELECT coinbase FROM openoutputs WHERE 1 = 2"; - - protected Sha256Hash chainHeadHash; - protected StoredBlock chainHeadBlock; - protected Sha256Hash verifiedChainHeadHash; - protected StoredBlock verifiedChainHeadBlock; - protected NetworkParameters params; - protected ThreadLocal conn; - protected List allConnections; - protected String connectionURL; - protected int fullStoreDepth; - protected String username; - protected String password; - protected String schemaName; - - /** - *

Create a new DatabaseFullPrunedBlockStore, using the full connection URL instead of a hostname and password, - * and optionally allowing a schema to be specified.

- * - * @param params A copy of the NetworkParameters used. - * @param connectionURL The jdbc url to connect to the database. - * @param fullStoreDepth The number of blocks of history stored in full (something like 1000 is pretty safe). - * @param username The database username. - * @param password The password to the database. - * @param schemaName The name of the schema to put the tables in. May be null if no schema is being used. - * @throws BlockStoreException If there is a failure to connect and/or initialise the database. - */ - public DatabaseFullPrunedBlockStore(NetworkParameters params, String connectionURL, int fullStoreDepth, - @Nullable String username, @Nullable String password, @Nullable String schemaName) throws BlockStoreException { - this.params = params; - this.fullStoreDepth = fullStoreDepth; - this.connectionURL = connectionURL; - this.schemaName = schemaName; - this.username = username; - this.password = password; - this.conn = new ThreadLocal<>(); - this.allConnections = new LinkedList<>(); - - try { - Class.forName(getDatabaseDriverClass()); - log.info(getDatabaseDriverClass() + " loaded. "); - } catch (ClassNotFoundException e) { - log.error("check CLASSPATH for database driver jar ", e); - } - - maybeConnect(); - - try { - // Create tables if needed - if (!tablesExists()) { - createTables(); - } else { - checkCompatibility(); - } - initFromDatabase(); - } catch (SQLException e) { - throw new BlockStoreException(e); - } - } - - /** - * Get the database driver class, - *

i.e org.postgresql.Driver.

- * @return The fully qualified database driver class. - */ - protected abstract String getDatabaseDriverClass(); - - /** - * Get the SQL statements that create the schema (DDL). - * @return The list of SQL statements. - */ - protected abstract List getCreateSchemeSQL(); - - /** - * Get the SQL statements that create the tables (DDL). - * @return The list of SQL statements. - */ - protected abstract List getCreateTablesSQL(); - - /** - * Get the SQL statements that create the indexes (DDL). - * @return The list of SQL statements. - */ - protected abstract List getCreateIndexesSQL(); - - /** - * Get the database specific error code that indicated a duplicate key error when inserting a record. - *

This is the code returned by {@link SQLException#getSQLState()}

- * @return The database duplicate error code. - */ - protected abstract String getDuplicateKeyErrorCode(); - - /** - * Get the SQL to select the total balance for a given address. - * @return The SQL prepared statement. - */ - protected String getBalanceSelectSQL() { - return SELECT_BALANCE_SQL; - } - - /** - * Get the SQL statement that checks if tables exist. - * @return The SQL prepared statement. - */ - protected String getTablesExistSQL() { - return SELECT_CHECK_TABLES_EXIST_SQL; - } - - /** - * Get the SQL statements to check if the database is compatible. - * @return The SQL prepared statements. - */ - protected List getCompatibilitySQL() { - List sqlStatements = new ArrayList<>(); - sqlStatements.add(SELECT_COMPATIBILITY_COINBASE_SQL); - return sqlStatements; - } - - /** - * Get the SQL to select the transaction outputs for a given address. - * @return The SQL prepared statement. - */ - protected String getTransactionOutputSelectSQL() { - return SELECT_TRANSACTION_OUTPUTS_SQL; - } - - /** - * Get the SQL to drop all the tables (DDL). - * @return The SQL drop statements. - */ - protected List getDropTablesSQL() { - List sqlStatements = new ArrayList<>(); - sqlStatements.add(DROP_SETTINGS_TABLE); - sqlStatements.add(DROP_HEADERS_TABLE); - sqlStatements.add(DROP_UNDOABLE_TABLE); - sqlStatements.add(DROP_OPEN_OUTPUT_TABLE); - return sqlStatements; - } - - /** - * Get the SQL to select a setting value. - * @return The SQL select statement. - */ - protected String getSelectSettingsSQL() { - return SELECT_SETTINGS_SQL; - } - - /** - * Get the SQL to insert a settings record. - * @return The SQL insert statement. - */ - protected String getInsertSettingsSQL() { - return INSERT_SETTINGS_SQL; - } - - /** - * Get the SQL to update a setting value. - * @return The SQL update statement. - */ - protected String getUpdateSettingsSLQ() { - return UPDATE_SETTINGS_SQL; - } - - /** - * Get the SQL to select a headers record. - * @return The SQL select statement. - */ - protected String getSelectHeadersSQL() { - return SELECT_HEADERS_SQL; - } - - /** - * Get the SQL to insert a headers record. - * @return The SQL insert statement. - */ - protected String getInsertHeadersSQL() { - return INSERT_HEADERS_SQL; - } - - /** - * Get the SQL to update a headers record. - * @return The SQL update statement. - */ - protected String getUpdateHeadersSQL() { - return UPDATE_HEADERS_SQL; - } - - /** - * Get the SQL to select an undoableblocks record. - * @return The SQL select statement. - */ - protected String getSelectUndoableBlocksSQL() { - return SELECT_UNDOABLEBLOCKS_SQL; - } - - /** - * Get the SQL to insert a undoableblocks record. - * @return The SQL insert statement. - */ - protected String getInsertUndoableBlocksSQL() { - return INSERT_UNDOABLEBLOCKS_SQL; - } - - /** - * Get the SQL to update a undoableblocks record. - * @return The SQL update statement. - */ - protected String getUpdateUndoableBlocksSQL() { - return UPDATE_UNDOABLEBLOCKS_SQL; - } - - /** - * Get the SQL to delete a undoableblocks record. - * @return The SQL delete statement. - */ - protected String getDeleteUndoableBlocksSQL() { - return DELETE_UNDOABLEBLOCKS_SQL; - } - - /** - * Get the SQL to select a openoutputs record. - * @return The SQL select statement. - */ - protected String getSelectOpenoutputsSQL() { - return SELECT_OPENOUTPUTS_SQL; - } - - /** - * Get the SQL to select count of openoutputs. - * @return The SQL select statement. - */ - protected String getSelectOpenoutputsCountSQL() { - return SELECT_OPENOUTPUTS_COUNT_SQL; - } - - /** - * Get the SQL to insert a openoutputs record. - * @return The SQL insert statement. - */ - protected String getInsertOpenoutputsSQL() { - return INSERT_OPENOUTPUTS_SQL; - } - - /** - * Get the SQL to delete a openoutputs record. - * @return The SQL delete statement. - */ - protected String getDeleteOpenoutputsSQL() { - return DELETE_OPENOUTPUTS_SQL; - } - - /** - * Get the SQL to select the setting dump fields for sizing/statistics. - * @return The SQL select statement. - */ - protected String getSelectSettingsDumpSQL() { - return SELECT_DUMP_SETTINGS_SQL; - } - - /** - * Get the SQL to select the headers dump fields for sizing/statistics. - * @return The SQL select statement. - */ - protected String getSelectHeadersDumpSQL() { - return SELECT_DUMP_HEADERS_SQL; - } - - /** - * Get the SQL to select the undoableblocks dump fields for sizing/statistics. - * @return The SQL select statement. - */ - protected String getSelectUndoableblocksDumpSQL() { - return SELECT_DUMP_UNDOABLEBLOCKS_SQL; - } - - /** - * Get the SQL to select the openoutouts dump fields for sizing/statistics. - * @return The SQL select statement. - */ - protected String getSelectopenoutputsDumpSQL() { - return SELECT_DUMP_OPENOUTPUTS_SQL; - } - - /** - *

If there isn't a connection on the {@link ThreadLocal} then create and store it.

- *

This will also automatically set up the schema if it does not exist within the DB.

- * @throws BlockStoreException if successful connection to the DB couldn't be made. - */ - protected synchronized final void maybeConnect() throws BlockStoreException { - try { - if (conn.get() != null && !conn.get().isClosed()) - return; - - if (username == null || password == null) { - conn.set(DriverManager.getConnection(connectionURL)); - } else { - Properties props = new Properties(); - props.setProperty("user", this.username); - props.setProperty("password", this.password); - conn.set(DriverManager.getConnection(connectionURL, props)); - } - allConnections.add(conn.get()); - Connection connection = conn.get(); - // set the schema if one is needed - if (schemaName != null) { - Statement s = connection.createStatement(); - for (String sql : getCreateSchemeSQL()) { - s.execute(sql); - } - } - log.info("Made a new connection to database " + connectionURL); - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } - } - - @Override - public synchronized void close() { - for (Connection conn : allConnections) { - try { - if (!conn.getAutoCommit()) { - conn.rollback(); - } - conn.close(); - if (conn == this.conn.get()) { - this.conn.set(null); - } - } catch (SQLException ex) { - throw new RuntimeException(ex); - } - } - allConnections.clear(); - } - - /** - *

Check if a tables exists within the database.

- * - *

This specifically checks for the 'settings' table and - * if it exists makes an assumption that the rest of the data - * structures are present.

- * - * @return If the tables exists. - * @throws java.sql.SQLException - */ - private boolean tablesExists() throws SQLException { - PreparedStatement ps = null; - try { - ps = conn.get().prepareStatement(getTablesExistSQL()); - ResultSet results = ps.executeQuery(); - results.close(); - return true; - } catch (SQLException ex) { - return false; - } finally { - if(ps != null && !ps.isClosed()) { - ps.close(); - } - } - } - - /** - * Check that the database is compatible with this version of the {@link DatabaseFullPrunedBlockStore}. - * @throws BlockStoreException If the database is not compatible. - */ - private void checkCompatibility() throws SQLException, BlockStoreException { - for(String sql : getCompatibilitySQL()) { - PreparedStatement ps = null; - try { - ps = conn.get().prepareStatement(sql); - ResultSet results = ps.executeQuery(); - results.close(); - } catch (SQLException ex) { - throw new BlockStoreException("Database block store is not compatible with the current release. " + - "See bitcoinj release notes for further information: " + ex.getMessage()); - } finally { - if (ps != null && !ps.isClosed()) { - ps.close(); - } - } - } - } - - /** - * Create the tables/block store in the database and - * @throws java.sql.SQLException If there is a database error. - * @throws BlockStoreException If the block store could not be created. - */ - private void createTables() throws SQLException, BlockStoreException { - Statement s = conn.get().createStatement(); - // create all the database tables - for (String sql : getCreateTablesSQL()) { - if (log.isDebugEnabled()) { - log.debug("DatabaseFullPrunedBlockStore : CREATE table [SQL= {0}]", sql); - } - s.executeUpdate(sql); - } - // create all the database indexes - for (String sql : getCreateIndexesSQL()) { - if (log.isDebugEnabled()) { - log.debug("DatabaseFullPrunedBlockStore : CREATE index [SQL= {0}]", sql); - } - s.executeUpdate(sql); - } - s.close(); - - // insert the initial settings for this store - PreparedStatement ps = conn.get().prepareStatement(getInsertSettingsSQL()); - ps.setString(1, CHAIN_HEAD_SETTING); - ps.setNull(2, Types.BINARY); - ps.execute(); - ps.setString(1, VERIFIED_CHAIN_HEAD_SETTING); - ps.setNull(2, Types.BINARY); - ps.execute(); - ps.setString(1, VERSION_SETTING); - ps.setBytes(2, "03".getBytes()); - ps.execute(); - ps.close(); - createNewStore(params); - } - - /** - * Create a new store for the given {@link NetworkParameters}. - * @param params The network. - * @throws BlockStoreException If the store couldn't be created. - */ - 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. - StoredBlock storedGenesisHeader = new StoredBlock(params.getGenesisBlock().cloneAsHeader(), params.getGenesisBlock().getWork(), 0); - // The coinbase in the genesis block is not spendable. This is because of how Bitcoin Core inits - // its database - the genesis transaction isn't actually in the db so its spent flags can never be updated. - List genesisTransactions = new LinkedList<>(); - StoredUndoableBlock storedGenesis = new StoredUndoableBlock(params.getGenesisBlock().getHash(), genesisTransactions); - put(storedGenesisHeader, storedGenesis); - setChainHead(storedGenesisHeader); - setVerifiedChainHead(storedGenesisHeader); - } catch (VerificationException e) { - throw new RuntimeException(e); // Cannot happen. - } - } - - /** - * Initialise the store state from the database. - * @throws java.sql.SQLException If there is a database error. - * @throws BlockStoreException If there is a block store error. - */ - private void initFromDatabase() throws SQLException, BlockStoreException { - PreparedStatement ps = conn.get().prepareStatement(getSelectSettingsSQL()); - ResultSet rs; - ps.setString(1, CHAIN_HEAD_SETTING); - rs = ps.executeQuery(); - if (!rs.next()) { - throw new BlockStoreException("corrupt database block store - no chain head pointer"); - } - Sha256Hash hash = Sha256Hash.wrap(rs.getBytes(1)); - rs.close(); - this.chainHeadBlock = get(hash); - this.chainHeadHash = hash; - if (this.chainHeadBlock == null) { - throw new BlockStoreException("corrupt database block store - head block not found"); - } - ps.setString(1, VERIFIED_CHAIN_HEAD_SETTING); - rs = ps.executeQuery(); - if (!rs.next()) { - throw new BlockStoreException("corrupt database block store - no verified chain head pointer"); - } - hash = Sha256Hash.wrap(rs.getBytes(1)); - rs.close(); - ps.close(); - this.verifiedChainHeadBlock = get(hash); - this.verifiedChainHeadHash = hash; - if (this.verifiedChainHeadBlock == null) { - throw new BlockStoreException("corrupt database block store - verified head block not found"); - } - } - - protected void putUpdateStoredBlock(StoredBlock storedBlock, boolean wasUndoable) throws SQLException { - try { - PreparedStatement s = - conn.get().prepareStatement(getInsertHeadersSQL()); - // We skip the first 4 bytes because (on mainnet) the minimum target has 4 0-bytes - byte[] hashBytes = new byte[28]; - System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 4, hashBytes, 0, 28); - s.setBytes(1, hashBytes); - s.setBytes(2, storedBlock.getChainWork().toByteArray()); - s.setInt(3, storedBlock.getHeight()); - s.setBytes(4, storedBlock.getHeader().cloneAsHeader().unsafeBitcoinSerialize()); - s.setBoolean(5, wasUndoable); - s.executeUpdate(); - s.close(); - } catch (SQLException e) { - // It is possible we try to add a duplicate StoredBlock if we upgraded - // In that case, we just update the entry to mark it wasUndoable - if (!(e.getSQLState().equals(getDuplicateKeyErrorCode())) || !wasUndoable) - throw e; - - PreparedStatement s = conn.get().prepareStatement(getUpdateHeadersSQL()); - s.setBoolean(1, true); - // We skip the first 4 bytes because (on mainnet) the minimum target has 4 0-bytes - byte[] hashBytes = new byte[28]; - System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 4, hashBytes, 0, 28); - s.setBytes(2, hashBytes); - s.executeUpdate(); - s.close(); - } - } - - @Override - public void put(StoredBlock storedBlock) throws BlockStoreException { - maybeConnect(); - try { - putUpdateStoredBlock(storedBlock, false); - } catch (SQLException e) { - throw new BlockStoreException(e); - } - } - - - @Override - public void put(StoredBlock storedBlock, StoredUndoableBlock undoableBlock) throws BlockStoreException { - maybeConnect(); - // We skip the first 4 bytes because (on mainnet) the minimum target has 4 0-bytes - byte[] hashBytes = new byte[28]; - System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 4, hashBytes, 0, 28); - int height = storedBlock.getHeight(); - byte[] transactions = null; - byte[] txOutChanges = null; - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - if (undoableBlock.getTxOutChanges() != null) { - undoableBlock.getTxOutChanges().serializeToStream(bos); - txOutChanges = bos.toByteArray(); - } else { - int numTxn = undoableBlock.getTransactions().size(); - ByteUtils.uint32ToByteStreamLE(numTxn, bos); - for (Transaction tx : undoableBlock.getTransactions()) - tx.bitcoinSerialize(bos); - transactions = bos.toByteArray(); - } - bos.close(); - } catch (IOException e) { - throw new BlockStoreException(e); - } - - try { - try { - PreparedStatement s = - conn.get().prepareStatement(getInsertUndoableBlocksSQL()); - s.setBytes(1, hashBytes); - s.setInt(2, height); - if (transactions == null) { - s.setBytes(3, txOutChanges); - s.setNull(4, Types.BINARY); - } else { - s.setNull(3, Types.BINARY); - s.setBytes(4, transactions); - } - s.executeUpdate(); - s.close(); - try { - putUpdateStoredBlock(storedBlock, true); - } catch (SQLException e) { - throw new BlockStoreException(e); - } - } catch (SQLException e) { - if (!e.getSQLState().equals(getDuplicateKeyErrorCode())) - throw new BlockStoreException(e); - - // There is probably an update-or-insert statement, but it wasn't obvious from the docs - PreparedStatement s = - conn.get().prepareStatement(getUpdateUndoableBlocksSQL()); - s.setBytes(3, hashBytes); - if (transactions == null) { - s.setBytes(1, txOutChanges); - s.setNull(2, Types.BINARY); - } else { - s.setNull(1, Types.BINARY); - s.setBytes(2, transactions); - } - s.executeUpdate(); - s.close(); - } - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } - } - - public StoredBlock get(Sha256Hash hash, boolean wasUndoableOnly) throws BlockStoreException { - // Optimize for chain head - if (chainHeadHash != null && chainHeadHash.equals(hash)) - return chainHeadBlock; - if (verifiedChainHeadHash != null && verifiedChainHeadHash.equals(hash)) - return verifiedChainHeadBlock; - maybeConnect(); - PreparedStatement s = null; - try { - s = conn.get() - .prepareStatement(getSelectHeadersSQL()); - // We skip the first 4 bytes because (on mainnet) the minimum target has 4 0-bytes - byte[] hashBytes = new byte[28]; - System.arraycopy(hash.getBytes(), 4, hashBytes, 0, 28); - s.setBytes(1, hashBytes); - ResultSet results = s.executeQuery(); - if (!results.next()) { - return null; - } - // Parse it. - - if (wasUndoableOnly && !results.getBoolean(4)) - return null; - - BigInteger chainWork = new BigInteger(results.getBytes(1)); - int height = results.getInt(2); - Block b = params.getDefaultSerializer().makeBlock(results.getBytes(3)); - b.verifyHeader(); - StoredBlock stored = new StoredBlock(b, chainWork, height); - return stored; - } catch (SQLException | VerificationException e) { - // VerificationException: Should not be able to happen unless the database contains bad blocks. - throw new BlockStoreException(e); - } finally { - if (s != null) { - try { - s.close(); - } catch (SQLException e) { - throw new BlockStoreException("Failed to close PreparedStatement"); - } - } - } - } - - @Override - public StoredBlock get(Sha256Hash hash) throws BlockStoreException { - return get(hash, false); - } - - @Override - public StoredBlock getOnceUndoableStoredBlock(Sha256Hash hash) throws BlockStoreException { - return get(hash, true); - } - - @Override - public StoredUndoableBlock getUndoBlock(Sha256Hash hash) throws BlockStoreException { - maybeConnect(); - PreparedStatement s = null; - try { - s = conn.get() - .prepareStatement(getSelectUndoableBlocksSQL()); - // We skip the first 4 bytes because (on mainnet) the minimum target has 4 0-bytes - - byte[] hashBytes = new byte[28]; - System.arraycopy(hash.getBytes(), 4, hashBytes, 0, 28); - s.setBytes(1, hashBytes); - ResultSet results = s.executeQuery(); - if (!results.next()) { - return null; - } - // Parse it. - byte[] txOutChanges = results.getBytes(1); - byte[] transactions = results.getBytes(2); - StoredUndoableBlock block; - if (txOutChanges == null) { - int numTxn = (int) ByteUtils.readUint32(transactions, 0); - int offset = 4; - List transactionList = new LinkedList<>(); - for (int i = 0; i < numTxn; i++) { - Transaction tx = params.getDefaultSerializer().makeTransaction(transactions, offset); - transactionList.add(tx); - offset += tx.getMessageSize(); - } - block = new StoredUndoableBlock(hash, transactionList); - } else { - TransactionOutputChanges outChangesObject = - new TransactionOutputChanges(new ByteArrayInputStream(txOutChanges)); - block = new StoredUndoableBlock(hash, outChangesObject); - } - return block; - } catch (SQLException | IOException | ProtocolException | ClassCastException | NullPointerException e) { - // IOException, ProtocolException, ClassCastException, NullPointerException: Corrupted database. - throw new BlockStoreException(e); - } finally { - if (s != null) { - try { - s.close(); - } catch (SQLException e) { - throw new BlockStoreException("Failed to close PreparedStatement"); - } - } - } - } - - @Override - public StoredBlock getChainHead() throws BlockStoreException { - return chainHeadBlock; - } - - @Override - public void setChainHead(StoredBlock chainHead) throws BlockStoreException { - Sha256Hash hash = chainHead.getHeader().getHash(); - this.chainHeadHash = hash; - this.chainHeadBlock = chainHead; - maybeConnect(); - try { - PreparedStatement s = conn.get() - .prepareStatement(getUpdateSettingsSLQ()); - s.setString(2, CHAIN_HEAD_SETTING); - s.setBytes(1, hash.getBytes()); - s.executeUpdate(); - s.close(); - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } - } - - @Override - public StoredBlock getVerifiedChainHead() throws BlockStoreException { - return verifiedChainHeadBlock; - } - - @Override - public void setVerifiedChainHead(StoredBlock chainHead) throws BlockStoreException { - Sha256Hash hash = chainHead.getHeader().getHash(); - this.verifiedChainHeadHash = hash; - this.verifiedChainHeadBlock = chainHead; - maybeConnect(); - try { - PreparedStatement s = conn.get() - .prepareStatement(getUpdateSettingsSLQ()); - s.setString(2, VERIFIED_CHAIN_HEAD_SETTING); - s.setBytes(1, hash.getBytes()); - s.executeUpdate(); - s.close(); - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } - if (this.chainHeadBlock.getHeight() < chainHead.getHeight()) - setChainHead(chainHead); - removeUndoableBlocksWhereHeightIsLessThan(chainHead.getHeight() - fullStoreDepth); - } - - private void removeUndoableBlocksWhereHeightIsLessThan(int height) throws BlockStoreException { - try { - PreparedStatement s = conn.get() - .prepareStatement(getDeleteUndoableBlocksSQL()); - s.setInt(1, height); - if (log.isDebugEnabled()) - log.debug("Deleting undoable undoable block with height <= " + height); - s.executeUpdate(); - s.close(); - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } - } - - @Override - public UTXO getTransactionOutput(Sha256Hash hash, long index) throws BlockStoreException { - maybeConnect(); - PreparedStatement s = null; - try { - s = conn.get() - .prepareStatement(getSelectOpenoutputsSQL()); - s.setBytes(1, hash.getBytes()); - // index is actually an unsigned int - s.setInt(2, (int) index); - ResultSet results = s.executeQuery(); - if (!results.next()) { - return null; - } - // Parse it. - int height = results.getInt(1); - Coin value = Coin.valueOf(results.getLong(2)); - byte[] scriptBytes = results.getBytes(3); - boolean coinbase = results.getBoolean(4); - String address = results.getString(5); - UTXO txout = new UTXO(hash, - index, - value, - height, - coinbase, - new Script(scriptBytes), - address); - return txout; - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } finally { - if (s != null) { - try { - s.close(); - } catch (SQLException e) { - throw new BlockStoreException("Failed to close PreparedStatement"); - } - } - } - } - - @Override - public void addUnspentTransactionOutput(UTXO out) throws BlockStoreException { - maybeConnect(); - PreparedStatement s = null; - try { - s = conn.get().prepareStatement(getInsertOpenoutputsSQL()); - s.setBytes(1, out.getHash().getBytes()); - // index is actually an unsigned int - s.setInt(2, (int) out.getIndex()); - s.setInt(3, out.getHeight()); - s.setLong(4, out.getValue().value); - s.setBytes(5, out.getScript().getProgram()); - s.setString(6, out.getAddress()); - ScriptType scriptType = out.getScript().getScriptType(); - s.setInt(7, scriptType != null ? scriptType.numericId() : 0); - s.setBoolean(8, out.isCoinbase()); - s.executeUpdate(); - s.close(); - } catch (SQLException e) { - if (!(e.getSQLState().equals(getDuplicateKeyErrorCode()))) - throw new BlockStoreException(e); - } finally { - if (s != null) { - try { - s.close(); - } catch (SQLException e) { - throw new BlockStoreException(e); - } - } - } - } - - @Override - public void removeUnspentTransactionOutput(UTXO out) throws BlockStoreException { - maybeConnect(); - // TODO: This should only need one query (maybe a stored procedure) - if (getTransactionOutput(out.getHash(), out.getIndex()) == null) - throw new BlockStoreException("Tried to remove a UTXO from DatabaseFullPrunedBlockStore that it didn't have!"); - try { - PreparedStatement s = conn.get() - .prepareStatement(getDeleteOpenoutputsSQL()); - s.setBytes(1, out.getHash().getBytes()); - // index is actually an unsigned int - s.setInt(2, (int)out.getIndex()); - s.executeUpdate(); - s.close(); - } catch (SQLException e) { - throw new BlockStoreException(e); - } - } - - @Override - public void beginDatabaseBatchWrite() throws BlockStoreException { - maybeConnect(); - if (log.isDebugEnabled()) - log.debug("Starting database batch write with connection: " + conn.get().toString()); - try { - conn.get().setAutoCommit(false); - } catch (SQLException e) { - throw new BlockStoreException(e); - } - } - - @Override - public void commitDatabaseBatchWrite() throws BlockStoreException { - maybeConnect(); - if (log.isDebugEnabled()) - log.debug("Committing database batch write with connection: " + conn.get().toString()); - try { - conn.get().commit(); - conn.get().setAutoCommit(true); - } catch (SQLException e) { - throw new BlockStoreException(e); - } - } - - @Override - public void abortDatabaseBatchWrite() throws BlockStoreException { - maybeConnect(); - if (log.isDebugEnabled()) - log.debug("Rollback database batch write with connection: " + conn.get().toString()); - try { - if (!conn.get().getAutoCommit()) { - conn.get().rollback(); - conn.get().setAutoCommit(true); - } else { - log.warn("Warning: Rollback attempt without transaction"); - } - } catch (SQLException e) { - throw new BlockStoreException(e); - } - } - - @Override - public boolean hasUnspentOutputs(Sha256Hash hash, int numOutputs) throws BlockStoreException { - maybeConnect(); - PreparedStatement s = null; - try { - s = conn.get().prepareStatement(getSelectOpenoutputsCountSQL()); - s.setBytes(1, hash.getBytes()); - ResultSet results = s.executeQuery(); - if (!results.next()) { - throw new BlockStoreException("Got no results from a COUNT(*) query"); - } - int count = results.getInt(1); - return count != 0; - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } finally { - if (s != null) { - try { - s.close(); - } catch (SQLException e) { - throw new BlockStoreException("Failed to close PreparedStatement"); - } - } - } - } - - @Override - public NetworkParameters getParams() { - return params; - } - - @Override - public int getChainHeadHeight() throws UTXOProviderException { - try { - return getVerifiedChainHead().getHeight(); - } catch (BlockStoreException e) { - throw new UTXOProviderException(e); - } - } - - /** - * Resets the store by deleting the contents of the tables and reinitialising them. - * @throws BlockStoreException If the tables couldn't be cleared and initialised. - */ - public void resetStore() throws BlockStoreException { - maybeConnect(); - try { - deleteStore(); - createTables(); - initFromDatabase(); - } catch (SQLException ex) { - throw new RuntimeException(ex); - } - } - - /** - * Deletes the store by deleting the tables within the database. - * @throws BlockStoreException If tables couldn't be deleted. - */ - public void deleteStore() throws BlockStoreException { - maybeConnect(); - try { - Statement s = conn.get().createStatement(); - for(String sql : getDropTablesSQL()) { - s.execute(sql); - } - s.close(); - } catch (SQLException ex) { - throw new RuntimeException(ex); - } - } - - /** - * Calculate the balance for a coinbase, to-address, or p2sh address. - * - *

The balance {@link DatabaseFullPrunedBlockStore#getBalanceSelectSQL()} returns - * the balance (summed) as an number, then use calculateClientSide=false

- * - *

The balance {@link DatabaseFullPrunedBlockStore#getBalanceSelectSQL()} returns - * the all the openoutputs as stored in the DB (binary), then use calculateClientSide=true

- * - * @param address The address to calculate the balance of - * @return The balance of the address supplied. If the address has not been seen, or there are no outputs open for this - * address, the return value is 0. - * @throws BlockStoreException If there is an error getting the balance. - */ - public BigInteger calculateBalanceForAddress(Address address) throws BlockStoreException { - maybeConnect(); - PreparedStatement s = null; - try { - s = conn.get().prepareStatement(getBalanceSelectSQL()); - s.setString(1, address.toString()); - ResultSet rs = s.executeQuery(); - BigInteger balance = BigInteger.ZERO; - if (rs.next()) { - return BigInteger.valueOf(rs.getLong(1)); - } - return balance; - } catch (SQLException ex) { - throw new BlockStoreException(ex); - } finally { - if (s != null) { - try { - s.close(); - } catch (SQLException e) { - throw new BlockStoreException("Could not close statement"); - } - } - } - } - - @Override - public List getOpenTransactionOutputs(List keys) throws UTXOProviderException { - PreparedStatement s = null; - List outputs = new ArrayList<>(); - try { - maybeConnect(); - s = conn.get().prepareStatement(getTransactionOutputSelectSQL()); - for (ECKey key : keys) { - // TODO switch to pubKeyHash in order to support native segwit addresses - s.setString(1, key.toAddress(ScriptType.P2PKH, params.network()).toString()); - ResultSet rs = s.executeQuery(); - while (rs.next()) { - Sha256Hash hash = Sha256Hash.wrap(rs.getBytes(1)); - Coin amount = Coin.valueOf(rs.getLong(2)); - byte[] scriptBytes = rs.getBytes(3); - int height = rs.getInt(4); - int index = rs.getInt(5); - boolean coinbase = rs.getBoolean(6); - String toAddress = rs.getString(7); - UTXO output = new UTXO(hash, - index, - amount, - height, - coinbase, - new Script(scriptBytes), - toAddress); - outputs.add(output); - } - } - return outputs; - } catch (SQLException | BlockStoreException ex) { - throw new UTXOProviderException(ex); - } finally { - if (s != null) - try { - s.close(); - } catch (SQLException e) { - throw new UTXOProviderException("Could not close statement", e); - } - } - } - - /** - * Dumps information about the size of actual data in the database to standard output - * The only truly useless data counted is printed in the form "N in id indexes" - * This does not take database indexes into account. - */ - public void dumpSizes() throws SQLException, BlockStoreException { - maybeConnect(); - Statement s = conn.get().createStatement(); - long size = 0; - long totalSize = 0; - int count = 0; - ResultSet rs = s.executeQuery(getSelectSettingsDumpSQL()); - while (rs.next()) { - size += rs.getString(1).length(); - size += rs.getBytes(2).length; - count++; - } - rs.close(); - System.out.printf(Locale.US, "Settings size: %d, count: %d, average size: %f%n", size, count, (double)size/count); - - totalSize += size; size = 0; count = 0; - rs = s.executeQuery(getSelectHeadersDumpSQL()); - while (rs.next()) { - size += 28; // hash - size += rs.getBytes(1).length; - size += 4; // height - size += rs.getBytes(2).length; - count++; - } - rs.close(); - System.out.printf(Locale.US, "Headers size: %d, count: %d, average size: %f%n", size, count, (double)size/count); - - totalSize += size; size = 0; count = 0; - rs = s.executeQuery(getSelectUndoableblocksDumpSQL()); - while (rs.next()) { - size += 28; // hash - size += 4; // height - byte[] txOutChanges = rs.getBytes(1); - byte[] transactions = rs.getBytes(2); - if (txOutChanges == null) - size += transactions.length; - else - size += txOutChanges.length; - // size += the space to represent NULL - count++; - } - rs.close(); - System.out.printf(Locale.US, "Undoable Blocks size: %d, count: %d, average size: %f%n", size, count, (double)size/count); - - totalSize += size; size = 0; count = 0; - long scriptSize = 0; - rs = s.executeQuery(getSelectopenoutputsDumpSQL()); - while (rs.next()) { - size += 32; // hash - size += 4; // index - size += 4; // height - size += rs.getBytes(1).length; - size += rs.getBytes(2).length; - scriptSize += rs.getBytes(2).length; - count++; - } - rs.close(); - System.out.printf(Locale.US, "Open Outputs size: %d, count: %d, average size: %f, average script size: %f (%d in id indexes)%n", - size, count, (double)size/count, (double)scriptSize/count, count * 8); - - totalSize += size; - System.out.println("Total Size: " + totalSize); - - s.close(); - } -} diff --git a/core/src/main/java/org/bitcoinj/store/H2FullPrunedBlockStore.java b/core/src/main/java/org/bitcoinj/store/H2FullPrunedBlockStore.java deleted file mode 100644 index a2aae6b84..000000000 --- a/core/src/main/java/org/bitcoinj/store/H2FullPrunedBlockStore.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2012 Matt Corallo. - * Copyright 2014 Kalpesh Parmar. - * Copyright 2019 Andreas Schildbach - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoinj.store; - -import org.bitcoinj.core.NetworkParameters; - -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -// Originally written for Apache Derby, but its DELETE (and general) performance was awful -/** - * A full pruned block store using the H2 pure-java embedded database. - * - * Note that because of the heavy delete load on the database, during IBD, - * you may see the database files grow quite large (around 1.5G). - * H2 automatically frees some space at shutdown, so close()ing the database - * decreases the space usage somewhat (to only around 1.3G). - */ -public class H2FullPrunedBlockStore extends DatabaseFullPrunedBlockStore { - private static final String H2_DUPLICATE_KEY_ERROR_CODE = "23505"; - private static final String DATABASE_DRIVER_CLASS = "org.h2.Driver"; - private static final String DATABASE_CONNECTION_URL_PREFIX = "jdbc:h2:"; - - // create table SQL - private static final String CREATE_SETTINGS_TABLE = "CREATE TABLE settings ( " - + "name VARCHAR(32) NOT NULL CONSTRAINT settings_pk PRIMARY KEY," - + "value BLOB" - + ")"; - - private static final String CREATE_HEADERS_TABLE = "CREATE TABLE headers ( " - + "hash BINARY(28) NOT NULL CONSTRAINT headers_pk PRIMARY KEY," - + "chainwork BLOB NOT NULL," - + "height INT NOT NULL," - + "header BLOB NOT NULL," - + "wasundoable BOOL NOT NULL" - + ")"; - - private static final String CREATE_UNDOABLE_TABLE = "CREATE TABLE undoableblocks ( " - + "hash BINARY(28) NOT NULL CONSTRAINT undoableblocks_pk PRIMARY KEY," - + "height INT NOT NULL," - + "txoutchanges BLOB," - + "transactions BLOB" - + ")"; - - private static final String CREATE_OPEN_OUTPUT_TABLE = "CREATE TABLE openoutputs (" - + "hash BINARY(32) NOT NULL," - + "index INT NOT NULL," - + "height INT NOT NULL," - + "value BIGINT NOT NULL," - + "scriptbytes BLOB NOT NULL," - + "toaddress VARCHAR(74)," - + "addresstargetable TINYINT," - + "coinbase BOOLEAN," - + "PRIMARY KEY (hash, index)," - + ")"; - - // Some indexes to speed up inserts - private static final String CREATE_OUTPUTS_ADDRESS_MULTI_INDEX = "CREATE INDEX openoutputs_hash_index_height_toaddress_idx ON openoutputs (hash, index, height, toaddress)"; - private static final String CREATE_OUTPUTS_TOADDRESS_INDEX = "CREATE INDEX openoutputs_toaddress_idx ON openoutputs (toaddress)"; - private static final String CREATE_OUTPUTS_ADDRESSTARGETABLE_INDEX = "CREATE INDEX openoutputs_addresstargetable_idx ON openoutputs (addresstargetable)"; - private static final String CREATE_OUTPUTS_HASH_INDEX = "CREATE INDEX openoutputs_hash_idx ON openoutputs (hash)"; - private static final String CREATE_UNDOABLE_TABLE_INDEX = "CREATE INDEX undoableblocks_height_idx ON undoableblocks (height)"; - - /** - * Creates a new H2FullPrunedBlockStore, with given credentials for H2 database - * @param params A copy of the NetworkParameters used - * @param dbName The path to the database on disk - * @param username The username to use in the database - * @param password The username's password to use in the database - * @param fullStoreDepth The number of blocks of history stored in full (something like 1000 is pretty safe) - * @throws BlockStoreException if the database fails to open for any reason - */ - public H2FullPrunedBlockStore(NetworkParameters params, String dbName, String username, String password, - int fullStoreDepth) throws BlockStoreException { - super(params, DATABASE_CONNECTION_URL_PREFIX + dbName + ";create=true;LOCK_TIMEOUT=60000;DB_CLOSE_ON_EXIT=FALSE", fullStoreDepth, username, password, null); - } - - /** - * Creates a new H2FullPrunedBlockStore - * @param params A copy of the NetworkParameters used - * @param dbName The path to the database on disk - * @param fullStoreDepth The number of blocks of history stored in full (something like 1000 is pretty safe) - * @throws BlockStoreException if the database fails to open for any reason - */ - public H2FullPrunedBlockStore(NetworkParameters params, String dbName, int fullStoreDepth) - throws BlockStoreException { - this(params, dbName, null, null, fullStoreDepth); - } - - /** - * Creates a new H2FullPrunedBlockStore with the given cache size - * @param params A copy of the NetworkParameters used - * @param dbName The path to the database on disk - * @param fullStoreDepth The number of blocks of history stored in full (something like 1000 is pretty safe) - * @param cacheSize The number of kilobytes to dedicate to H2 Cache (the default value of 16MB (16384) is a safe bet - * to achieve good performance/cost when importing blocks from disk, past 32MB makes little sense, - * and below 4MB sees a sharp drop in performance) - * @throws BlockStoreException if the database fails to open for any reason - */ - public H2FullPrunedBlockStore(NetworkParameters params, String dbName, int fullStoreDepth, int cacheSize) - throws BlockStoreException { - this(params, dbName, fullStoreDepth); - try { - Statement s = conn.get().createStatement(); - s.executeUpdate("SET CACHE_SIZE " + cacheSize); - s.close(); - } catch (SQLException e) { - throw new BlockStoreException(e); - } - } - - @Override - protected String getDuplicateKeyErrorCode() { - return H2_DUPLICATE_KEY_ERROR_CODE; - } - - @Override - protected List getCreateTablesSQL() { - List sqlStatements = new ArrayList<>(); - sqlStatements.add(CREATE_SETTINGS_TABLE); - sqlStatements.add(CREATE_HEADERS_TABLE); - sqlStatements.add(CREATE_UNDOABLE_TABLE); - sqlStatements.add(CREATE_OPEN_OUTPUT_TABLE); - return sqlStatements; - } - - @Override - protected List getCreateIndexesSQL() { - List sqlStatements = new ArrayList<>(); - sqlStatements.add(CREATE_UNDOABLE_TABLE_INDEX); - sqlStatements.add(CREATE_OUTPUTS_ADDRESS_MULTI_INDEX); - sqlStatements.add(CREATE_OUTPUTS_ADDRESSTARGETABLE_INDEX); - sqlStatements.add(CREATE_OUTPUTS_HASH_INDEX); - sqlStatements.add(CREATE_OUTPUTS_TOADDRESS_INDEX); - return sqlStatements; - } - - @Override - protected List getCreateSchemeSQL() { - // do nothing - return Collections.emptyList(); - } - - @Override - protected String getDatabaseDriverClass() { - return DATABASE_DRIVER_CLASS; - } -} diff --git a/core/src/main/java/org/bitcoinj/store/MySQLFullPrunedBlockStore.java b/core/src/main/java/org/bitcoinj/store/MySQLFullPrunedBlockStore.java deleted file mode 100644 index c7cd5600c..000000000 --- a/core/src/main/java/org/bitcoinj/store/MySQLFullPrunedBlockStore.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2014 Kalpesh Parmar - * Copyright 2019 Andreas Schildbach - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoinj.store; - -import org.bitcoinj.core.Address; -import org.bitcoinj.core.NetworkParameters; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - *

A full pruned block store using the MySQL database engine. As an added bonus an address index is calculated, - * so you can use {@link #calculateBalanceForAddress(Address)} to quickly look up - * the quantity of bitcoins controlled by that address.

- */ -public class MySQLFullPrunedBlockStore extends DatabaseFullPrunedBlockStore { - private static final String MYSQL_DUPLICATE_KEY_ERROR_CODE = "23000"; - private static final String DATABASE_DRIVER_CLASS = "com.mysql.jdbc.Driver"; - private static final String DATABASE_CONNECTION_URL_PREFIX = "jdbc:mysql://"; - - // create table SQL - private static final String CREATE_SETTINGS_TABLE = "CREATE TABLE settings (\n" + - " name varchar(32) NOT NULL,\n" + - " value blob,\n" + - " CONSTRAINT setting_pk PRIMARY KEY (name) \n" + - ")\n"; - - private static final String CREATE_HEADERS_TABLE = "CREATE TABLE headers (\n" + - " hash varbinary(28) NOT NULL,\n" + - " chainwork varbinary(12) NOT NULL,\n" + - " height integer NOT NULL,\n" + - " header varbinary(80) NOT NULL,\n" + - " wasundoable tinyint(1) NOT NULL,\n" + - " CONSTRAINT headers_pk PRIMARY KEY (hash) USING BTREE \n" + - ")"; - - private static final String CREATE_UNDOABLE_TABLE = "CREATE TABLE undoableblocks (\n" + - " hash varbinary(28) NOT NULL,\n" + - " height integer NOT NULL,\n" + - " txoutchanges mediumblob,\n" + - " transactions mediumblob,\n" + - " CONSTRAINT undoableblocks_pk PRIMARY KEY (hash) USING BTREE \n" + - ")\n"; - - private static final String CREATE_OPEN_OUTPUT_TABLE = "CREATE TABLE openoutputs (\n" + - " hash varbinary(32) NOT NULL,\n" + - " `index` integer NOT NULL,\n" + - " height integer NOT NULL,\n" + - " value bigint NOT NULL,\n" + - " scriptbytes mediumblob NOT NULL,\n" + - " toaddress varchar(74),\n" + - " addresstargetable tinyint(1),\n" + - " coinbase boolean,\n" + - " CONSTRAINT openoutputs_pk PRIMARY KEY (hash, `index`) USING BTREE \n" + - ")\n"; - - // Some indexes to speed up inserts - private static final String CREATE_OUTPUTS_ADDRESS_MULTI_INDEX = "CREATE INDEX openoutputs_hash_index_height_toaddress_idx ON openoutputs (hash, `index`, height, toaddress) USING btree"; - private static final String CREATE_OUTPUTS_TOADDRESS_INDEX = "CREATE INDEX openoutputs_toaddress_idx ON openoutputs (toaddress) USING btree"; - private static final String CREATE_OUTPUTS_ADDRESSTARGETABLE_INDEX = "CREATE INDEX openoutputs_addresstargetable_idx ON openoutputs (addresstargetable) USING btree"; - private static final String CREATE_OUTPUTS_HASH_INDEX = "CREATE INDEX openoutputs_hash_idx ON openoutputs (hash) USING btree"; - private static final String CREATE_UNDOABLE_TABLE_INDEX = "CREATE INDEX undoableblocks_height_idx ON undoableblocks (height) USING btree"; - - // SQL involving index column (table openOutputs) overridden as it is a reserved word and must be back ticked in MySQL. - private static final String SELECT_OPENOUTPUTS_SQL = "SELECT height, value, scriptbytes, coinbase, toaddress, addresstargetable FROM openoutputs WHERE hash = ? AND `index` = ?"; - private static final String INSERT_OPENOUTPUTS_SQL = "INSERT INTO openoutputs (hash, `index`, height, value, scriptbytes, toaddress, addresstargetable, coinbase) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; - private static final String DELETE_OPENOUTPUTS_SQL = "DELETE FROM openoutputs WHERE hash = ? AND `index`= ?"; - - private static final String SELECT_TRANSACTION_OUTPUTS_SQL = "SELECT hash, value, scriptbytes, height, `index`, coinbase, toaddress, addresstargetable FROM openoutputs where toaddress = ?"; - - /** - * Creates a new MySQLFullPrunedBlockStore. - * - * @param params A copy of the NetworkParameters used - * @param fullStoreDepth The number of blocks of history stored in full (something like 1000 is pretty safe) - * @param hostname The hostname of the database to connect to - * @param dbName The database to connect to - * @param username The database username - * @param password The password to the database - * @throws BlockStoreException if the database fails to open for any reason - */ - public MySQLFullPrunedBlockStore(NetworkParameters params, int fullStoreDepth, String hostname, String dbName, - String username, String password) throws BlockStoreException { - super(params, DATABASE_CONNECTION_URL_PREFIX + hostname + "/" + dbName, fullStoreDepth, username, password, null); - } - - @Override - protected String getDuplicateKeyErrorCode() { - return MYSQL_DUPLICATE_KEY_ERROR_CODE; - } - - @Override - protected String getSelectOpenoutputsSQL() { - return SELECT_OPENOUTPUTS_SQL; - } - - @Override - protected String getInsertOpenoutputsSQL() { - return INSERT_OPENOUTPUTS_SQL; - } - - @Override - protected String getDeleteOpenoutputsSQL() { - return DELETE_OPENOUTPUTS_SQL; - } - - @Override - protected String getTransactionOutputSelectSQL() { - return SELECT_TRANSACTION_OUTPUTS_SQL; - } - - @Override - protected List getCreateTablesSQL() { - List sqlStatements = new ArrayList<>(); - sqlStatements.add(CREATE_SETTINGS_TABLE); - sqlStatements.add(CREATE_HEADERS_TABLE); - sqlStatements.add(CREATE_UNDOABLE_TABLE); - sqlStatements.add(CREATE_OPEN_OUTPUT_TABLE); - return sqlStatements; - } - - @Override - protected List getCreateIndexesSQL() { - List sqlStatements = new ArrayList<>(); - sqlStatements.add(CREATE_UNDOABLE_TABLE_INDEX); - sqlStatements.add(CREATE_OUTPUTS_ADDRESS_MULTI_INDEX); - sqlStatements.add(CREATE_OUTPUTS_ADDRESSTARGETABLE_INDEX); - sqlStatements.add(CREATE_OUTPUTS_HASH_INDEX); - sqlStatements.add(CREATE_OUTPUTS_TOADDRESS_INDEX); - return sqlStatements; - } - - @Override - protected List getCreateSchemeSQL() { - // do nothing - return Collections.emptyList(); - } - - @Override - protected String getDatabaseDriverClass() { - return DATABASE_DRIVER_CLASS; - } -} diff --git a/core/src/main/java/org/bitcoinj/store/PostgresFullPrunedBlockStore.java b/core/src/main/java/org/bitcoinj/store/PostgresFullPrunedBlockStore.java deleted file mode 100644 index 3ea796fcc..000000000 --- a/core/src/main/java/org/bitcoinj/store/PostgresFullPrunedBlockStore.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright 2014 BitPOS Pty Ltd. - * Copyright 2014 Andreas Schildbach - * Copyright 2014 Kalpesh Parmar - * - * 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 org.bitcoinj.store; - -import org.bitcoinj.base.utils.ByteUtils; -import org.bitcoinj.core.Address; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.StoredBlock; -import org.bitcoinj.core.StoredUndoableBlock; -import org.bitcoinj.core.Transaction; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; -import java.util.ArrayList; -import java.util.List; - -/** - *

A full pruned block store using the Postgres database engine. As an added bonus an address index is calculated, - * so you can use {@link #calculateBalanceForAddress(Address)} to quickly look up - * the quantity of bitcoins controlled by that address.

- */ -public class PostgresFullPrunedBlockStore extends DatabaseFullPrunedBlockStore { - private static final Logger log = LoggerFactory.getLogger(PostgresFullPrunedBlockStore.class); - - private static final String POSTGRES_DUPLICATE_KEY_ERROR_CODE = "23505"; - private static final String DATABASE_DRIVER_CLASS = "org.postgresql.Driver"; - private static final String DATABASE_CONNECTION_URL_PREFIX = "jdbc:postgresql://"; - - // create table SQL - private static final String CREATE_SETTINGS_TABLE = "CREATE TABLE settings (\n" + - " name character varying(32) NOT NULL,\n" + - " value bytea,\n" + - " CONSTRAINT setting_pk PRIMARY KEY (name)\n" + - ")\n"; - - private static final String CREATE_HEADERS_TABLE = "CREATE TABLE headers (\n" + - " hash bytea NOT NULL,\n" + - " chainwork bytea NOT NULL,\n" + - " height integer NOT NULL,\n" + - " header bytea NOT NULL,\n" + - " wasundoable boolean NOT NULL,\n" + - " CONSTRAINT headers_pk PRIMARY KEY (hash)\n" + - ")\n"; - - private static final String CREATE_UNDOABLE_TABLE = "CREATE TABLE undoableblocks (\n" + - " hash bytea NOT NULL,\n" + - " height integer NOT NULL,\n" + - " txoutchanges bytea,\n" + - " transactions bytea,\n" + - " CONSTRAINT undoableblocks_pk PRIMARY KEY (hash)\n" + - ")\n"; - - private static final String CREATE_OPEN_OUTPUT_TABLE = "CREATE TABLE openoutputs (\n" + - " hash bytea NOT NULL,\n" + - " \"index\" integer NOT NULL,\n" + - " height integer NOT NULL,\n" + - " value bigint NOT NULL,\n" + - " scriptbytes bytea NOT NULL,\n" + - " toaddress character varying(74),\n" + - " addresstargetable smallint,\n" + - " coinbase boolean,\n" + - " CONSTRAINT openoutputs_pk PRIMARY KEY (hash,\"index\")\n" + - ")\n"; - - // Some indexes to speed up inserts - private static final String CREATE_OUTPUTS_ADDRESS_MULTI_INDEX = "CREATE INDEX openoutputs_hash_index_num_height_toaddress_idx ON openoutputs USING btree (hash, \"index\", height, toaddress)"; - private static final String CREATE_OUTPUTS_TOADDRESS_INDEX = "CREATE INDEX openoutputs_toaddress_idx ON openoutputs USING btree (toaddress)"; - private static final String CREATE_OUTPUTS_ADDRESSTARGETABLE_INDEX = "CREATE INDEX openoutputs_addresstargetable_idx ON openoutputs USING btree (addresstargetable)"; - private static final String CREATE_OUTPUTS_HASH_INDEX = "CREATE INDEX openoutputs_hash_idx ON openoutputs USING btree (hash)"; - private static final String CREATE_UNDOABLE_TABLE_INDEX = "CREATE INDEX undoableblocks_height_idx ON undoableBlocks USING btree (height)"; - - private static final String SELECT_UNDOABLEBLOCKS_EXISTS_SQL = "select 1 from undoableblocks where hash = ?"; - - /** - * Creates a new PostgresFullPrunedBlockStore. - * - * @param params A copy of the NetworkParameters used - * @param fullStoreDepth The number of blocks of history stored in full (something like 1000 is pretty safe) - * @param hostname The hostname of the database to connect to - * @param dbName The database to connect to - * @param username The database username - * @param password The password to the database - * @throws BlockStoreException if the database fails to open for any reason - */ - public PostgresFullPrunedBlockStore(NetworkParameters params, int fullStoreDepth, String hostname, String dbName, - String username, String password) throws BlockStoreException { - super(params, DATABASE_CONNECTION_URL_PREFIX + hostname + "/" + dbName, fullStoreDepth, username, password, null); - } - - /** - *

Create a new PostgresFullPrunedBlockStore, storing the tables in the schema specified. You may want to - * specify a schema to avoid name collisions, or just to keep the database better organized. The schema is not - * required, and if one is not provided than the default schema for the username will be used. See - * the postgres schema docs for more on - * schemas.

- * - * @param params A copy of the NetworkParameters used. - * @param fullStoreDepth The number of blocks of history stored in full (something like 1000 is pretty safe). - * @param hostname The hostname of the database to connect to. - * @param dbName The database to connect to. - * @param username The database username. - * @param password The password to the database. - * @param schemaName The name of the schema to put the tables in. May be null if no schema is being used. - * @throws BlockStoreException If the database fails to open for any reason. - */ - public PostgresFullPrunedBlockStore(NetworkParameters params, int fullStoreDepth, String hostname, String dbName, - String username, String password, @Nullable String schemaName) throws BlockStoreException { - super(params, DATABASE_CONNECTION_URL_PREFIX + hostname + "/" + dbName, fullStoreDepth, username, password, schemaName); - } - - @Override - protected String getDuplicateKeyErrorCode() { - return POSTGRES_DUPLICATE_KEY_ERROR_CODE; - } - - @Override - protected List getCreateTablesSQL() { - List sqlStatements = new ArrayList<>(); - sqlStatements.add(CREATE_SETTINGS_TABLE); - sqlStatements.add(CREATE_HEADERS_TABLE); - sqlStatements.add(CREATE_UNDOABLE_TABLE); - sqlStatements.add(CREATE_OPEN_OUTPUT_TABLE); - return sqlStatements; - } - - @Override - protected List getCreateIndexesSQL() { - List sqlStatements = new ArrayList<>(); - sqlStatements.add(CREATE_UNDOABLE_TABLE_INDEX); - sqlStatements.add(CREATE_OUTPUTS_ADDRESS_MULTI_INDEX); - sqlStatements.add(CREATE_OUTPUTS_ADDRESSTARGETABLE_INDEX); - sqlStatements.add(CREATE_OUTPUTS_HASH_INDEX); - sqlStatements.add(CREATE_OUTPUTS_TOADDRESS_INDEX); - return sqlStatements; - } - - @Override - protected List getCreateSchemeSQL() { - List sqlStatements = new ArrayList<>(); - sqlStatements.add("CREATE SCHEMA IF NOT EXISTS " + schemaName); - sqlStatements.add("set search_path to '" + schemaName +"'"); - return sqlStatements; - } - - @Override - protected String getDatabaseDriverClass() { - return DATABASE_DRIVER_CLASS; - } - - @Override - public void put(StoredBlock storedBlock, StoredUndoableBlock undoableBlock) throws BlockStoreException { - maybeConnect(); - // We skip the first 4 bytes because (on mainnet) the minimum target has 4 0-bytes - byte[] hashBytes = new byte[28]; - System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 4, hashBytes, 0, 28); - int height = storedBlock.getHeight(); - byte[] transactions = null; - byte[] txOutChanges = null; - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - if (undoableBlock.getTxOutChanges() != null) { - undoableBlock.getTxOutChanges().serializeToStream(bos); - txOutChanges = bos.toByteArray(); - } else { - int numTxn = undoableBlock.getTransactions().size(); - ByteUtils.uint32ToByteStreamLE(numTxn, bos); - for (Transaction tx : undoableBlock.getTransactions()) - tx.bitcoinSerialize(bos); - transactions = bos.toByteArray(); - } - bos.close(); - } catch (IOException e) { - throw new BlockStoreException(e); - } - - - try { - if (log.isDebugEnabled()) - log.debug("Looking for undoable block with hash: " + ByteUtils.HEX.encode(hashBytes)); - - PreparedStatement findS = conn.get().prepareStatement(SELECT_UNDOABLEBLOCKS_EXISTS_SQL); - findS.setBytes(1, hashBytes); - - ResultSet rs = findS.executeQuery(); - if (rs.next()) - { - // We already have this output, update it. - findS.close(); - - // Postgres insert-or-updates are very complex (and finnicky). This level of transaction isolation - // seems to work for bitcoinj - PreparedStatement s = - conn.get().prepareStatement(getUpdateUndoableBlocksSQL()); - s.setBytes(3, hashBytes); - - if (log.isDebugEnabled()) - log.debug("Updating undoable block with hash: " + ByteUtils.HEX.encode(hashBytes)); - - if (transactions == null) { - s.setBytes(1, txOutChanges); - s.setNull(2, Types.BINARY); - } else { - s.setNull(1, Types.BINARY); - s.setBytes(2, transactions); - } - s.executeUpdate(); - s.close(); - - return; - } - - PreparedStatement s = - conn.get().prepareStatement(getInsertUndoableBlocksSQL()); - s.setBytes(1, hashBytes); - s.setInt(2, height); - - if (log.isDebugEnabled()) - log.debug("Inserting undoable block with hash: " + ByteUtils.HEX.encode(hashBytes) + " at height " + height); - - if (transactions == null) { - s.setBytes(3, txOutChanges); - s.setNull(4, Types.BINARY); - } else { - s.setNull(3, Types.BINARY); - s.setBytes(4, transactions); - } - s.executeUpdate(); - s.close(); - try { - putUpdateStoredBlock(storedBlock, true); - } catch (SQLException e) { - throw new BlockStoreException(e); - } - } catch (SQLException e) { - if (!e.getSQLState().equals(POSTGRES_DUPLICATE_KEY_ERROR_CODE)) - throw new BlockStoreException(e); - } - - } -} diff --git a/core/src/main/java/org/bitcoinj/store/package-info.java b/core/src/main/java/org/bitcoinj/store/package-info.java index d96820179..5d4a8c335 100644 --- a/core/src/main/java/org/bitcoinj/store/package-info.java +++ b/core/src/main/java/org/bitcoinj/store/package-info.java @@ -16,8 +16,7 @@ /** * Block stores persist blockchain data downloaded from remote peers. There is an SPV block store which preserves a ring - * buffer of headers on disk and is suitable for lightweight user wallets, a store that's backed by Postgres and which - * can calculate a full indexed UTXO set (i.e. it can query address balances), a store that's backed by the embedded H2 - * database, and a memory only store useful for unit tests. + * buffer of headers on disk and is suitable for lightweight user wallets, a store that can calculate a full indexed + * UTXO set (i.e. it can query address balances), and a memory only store useful for unit tests. */ -package org.bitcoinj.store; \ No newline at end of file +package org.bitcoinj.store; diff --git a/core/src/test/java/org/bitcoinj/core/BitcoindComparisonTool.java b/core/src/test/java/org/bitcoinj/core/BitcoindComparisonTool.java index 8eba6d1a2..258553980 100644 --- a/core/src/test/java/org/bitcoinj/core/BitcoindComparisonTool.java +++ b/core/src/test/java/org/bitcoinj/core/BitcoindComparisonTool.java @@ -23,8 +23,9 @@ import org.bitcoinj.base.Sha256Hash; import org.bitcoinj.net.NioClient; import org.bitcoinj.params.RegTestParams; import org.bitcoinj.store.BlockStoreException; -import org.bitcoinj.store.H2FullPrunedBlockStore; +import org.bitcoinj.store.FullPrunedBlockStore; import org.bitcoinj.store.MemoryBlockStore; +import org.bitcoinj.store.MemoryFullPrunedBlockStore; import org.bitcoinj.utils.BlockFileLoader; import org.bitcoinj.utils.BriefLogFormatter; import org.bitcoinj.utils.Threading; @@ -77,8 +78,7 @@ public class BitcoindComparisonTool { final Iterator blocks = new BlockFileLoader(PARAMS, Arrays.asList(blockFile)); try { - H2FullPrunedBlockStore store = new H2FullPrunedBlockStore(PARAMS, args.length > 0 ? args[0] : "BitcoindComparisonTool", blockList.maximumReorgBlockCount); - store.resetStore(); + FullPrunedBlockStore store = new MemoryFullPrunedBlockStore(PARAMS, blockList.maximumReorgBlockCount); //store = new MemoryFullPrunedBlockStore(params, blockList.maximumReorgBlockCount); chain = new FullPrunedBlockChain(PARAMS, store); } catch (BlockStoreException e) { diff --git a/core/src/test/java/org/bitcoinj/core/H2FullPrunedBlockChainTest.java b/core/src/test/java/org/bitcoinj/core/H2FullPrunedBlockChainTest.java deleted file mode 100644 index 12ec36f7e..000000000 --- a/core/src/test/java/org/bitcoinj/core/H2FullPrunedBlockChainTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright by the original author or authors. - * - * 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 org.bitcoinj.core; - -import org.bitcoinj.store.BlockStoreException; -import org.bitcoinj.store.FullPrunedBlockStore; -import org.bitcoinj.store.H2FullPrunedBlockStore; -import org.junit.After; - -import java.io.File; - -/** - * An H2 implementation of the FullPrunedBlockStoreTest - */ -public class H2FullPrunedBlockChainTest extends AbstractFullPrunedBlockChainTest { - @After - public void tearDown() { - deleteFiles(); - } - - @Override - public FullPrunedBlockStore createStore(NetworkParameters params, int blockCount) throws BlockStoreException { - deleteFiles(); - return new H2FullPrunedBlockStore(params, "test", "sa", "sa", blockCount); - } - - private void deleteFiles() { - maybeDelete("test.h2.db"); - maybeDelete("test.trace.db"); - maybeDelete("test.lock.db"); - } - - private void maybeDelete(String s) { - new File(s).delete(); - } - - @Override - public void resetStore(FullPrunedBlockStore store) throws BlockStoreException { - ((H2FullPrunedBlockStore)store).resetStore(); - } -} diff --git a/core/src/test/java/org/bitcoinj/core/MySQLFullPrunedBlockChainTest.java b/core/src/test/java/org/bitcoinj/core/MySQLFullPrunedBlockChainTest.java deleted file mode 100644 index e26ddcb14..000000000 --- a/core/src/test/java/org/bitcoinj/core/MySQLFullPrunedBlockChainTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014 Kalpesh Parmar. - * - * 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 org.bitcoinj.core; - -import org.bitcoinj.store.BlockStoreException; -import org.bitcoinj.store.FullPrunedBlockStore; -import org.bitcoinj.store.MySQLFullPrunedBlockStore; -import org.junit.After; -import org.junit.Ignore; - -/** - * A MySQL implementation of the {@link AbstractFullPrunedBlockChainTest} - */ -@Ignore("enable the mysql driver dependency in the maven POM") -public class MySQLFullPrunedBlockChainTest extends AbstractFullPrunedBlockChainTest { - - @After - public void tearDown() throws Exception { - ((MySQLFullPrunedBlockStore)store).deleteStore(); - } - - // Replace these with your mysql location/credentials and remove @Ignore to test - private static final String DB_HOSTNAME = "localhost"; - private static final String DB_NAME = "bitcoinj_test"; - private static final String DB_USERNAME = "bitcoinj"; - private static final String DB_PASSWORD = "password"; - - @Override - public FullPrunedBlockStore createStore(NetworkParameters params, int blockCount) - throws BlockStoreException { - return new MySQLFullPrunedBlockStore(params, blockCount, DB_HOSTNAME, DB_NAME, DB_USERNAME, DB_PASSWORD); - } - - @Override - public void resetStore(FullPrunedBlockStore store) throws BlockStoreException { - ((MySQLFullPrunedBlockStore)store).resetStore(); - } -} \ No newline at end of file diff --git a/core/src/test/java/org/bitcoinj/core/PostgresFullPrunedBlockChainTest.java b/core/src/test/java/org/bitcoinj/core/PostgresFullPrunedBlockChainTest.java deleted file mode 100644 index 47755eca2..000000000 --- a/core/src/test/java/org/bitcoinj/core/PostgresFullPrunedBlockChainTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright by the original author or authors. - * - * 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 org.bitcoinj.core; - -import org.bitcoinj.store.BlockStoreException; -import org.bitcoinj.store.FullPrunedBlockStore; -import org.bitcoinj.store.PostgresFullPrunedBlockStore; -import org.junit.After; -import org.junit.Ignore; -import org.junit.Test; - -/** - * A Postgres implementation of the {@link AbstractFullPrunedBlockChainTest} - */ -@Ignore("enable the postgres driver dependency in the maven POM") -public class PostgresFullPrunedBlockChainTest extends AbstractFullPrunedBlockChainTest -{ - // Replace these with your postgres location/credentials and remove @Ignore to test - // You can set up a fresh postgres with the command: create user bitcoinj superuser password 'password'; - private static final String DB_HOSTNAME = "localhost"; - private static final String DB_NAME = "bitcoinj_test"; - private static final String DB_USERNAME = "bitcoinj"; - private static final String DB_PASSWORD = "password"; - private static final String DB_SCHEMA = "blockstore_schema"; - - // whether to run the test with a schema name - private boolean useSchema = false; - - @After - public void tearDown() throws Exception { - ((PostgresFullPrunedBlockStore)store).deleteStore(); - } - - @Override - public FullPrunedBlockStore createStore(NetworkParameters params, int blockCount) - throws BlockStoreException { - if(useSchema) { - return new PostgresFullPrunedBlockStore(params, blockCount, DB_HOSTNAME, DB_NAME, DB_USERNAME, DB_PASSWORD, DB_SCHEMA); - } - else { - return new PostgresFullPrunedBlockStore(params, blockCount, DB_HOSTNAME, DB_NAME, DB_USERNAME, DB_PASSWORD); - } - } - - @Override - public void resetStore(FullPrunedBlockStore store) throws BlockStoreException { - ((PostgresFullPrunedBlockStore)store).resetStore(); - } - - @Test - public void testFirst100kBlocksWithCustomSchema() throws Exception { - boolean oldSchema = useSchema; - useSchema = true; - try { - super.testFirst100KBlocks(); - } finally { - useSchema = oldSchema; - } - } -} diff --git a/tools/src/main/java/org/bitcoinj/tools/BlockImporter.java b/tools/src/main/java/org/bitcoinj/tools/BlockImporter.java index 824fd21b1..84a39595a 100644 --- a/tools/src/main/java/org/bitcoinj/tools/BlockImporter.java +++ b/tools/src/main/java/org/bitcoinj/tools/BlockImporter.java @@ -28,9 +28,8 @@ import java.io.File; /** Very thin wrapper around {@link BlockFileLoader} */ public class BlockImporter { public static void main(String[] args) throws BlockStoreException, VerificationException, PrunedException { - System.out.println("USAGE: BlockImporter (prod|test) (H2|Disk|MemFull|Mem|SPV) [blockStore]"); + System.out.println("USAGE: BlockImporter (prod|test) (Disk|MemFull|Mem|SPV) [blockStore]"); System.out.println(" blockStore is required unless type is Mem or MemFull"); - System.out.println(" eg BlockImporter prod H2 /home/user/bitcoinj.h2store"); System.out.println(" Does full verification if the store supports it"); Preconditions.checkArgument(args.length == 2 || args.length == 3); @@ -41,10 +40,7 @@ public class BlockImporter { params = MainNetParams.get(); BlockStore store; - if (args[1].equals("H2")) { - Preconditions.checkArgument(args.length == 3); - store = new H2FullPrunedBlockStore(params, args[2], 100); - } else if (args[1].equals("MemFull")) { + if (args[1].equals("MemFull")) { Preconditions.checkArgument(args.length == 2); store = new MemoryFullPrunedBlockStore(params, 100); } else if (args[1].equals("Mem")) { diff --git a/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java b/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java index 09d937faa..2e01c112b 100644 --- a/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java +++ b/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java @@ -1001,7 +1001,7 @@ public class WalletTool implements Callable { } chain = new BlockChain(params, wallet, store); } else if (mode == ValidationMode.FULL) { - store = new H2FullPrunedBlockStore(params, chainFile.getAbsolutePath(), 5000); + store = new MemoryFullPrunedBlockStore(params, 5000); chain = new FullPrunedBlockChain(params, wallet, (FullPrunedBlockStore) store); } // This will ensure the wallet is saved when it changes.