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 Name | Type (generic) |
- * name | string |
- * value | binary |
- *
- *
- *
- * headers table
- * Field Name | Type (generic) |
- * hash | binary |
- * chainwork | binary |
- * height | integer |
- * header | binary |
- * wasundoable | boolean |
- *
- *
- *
- * undoableblocks table
- * Field Name | Type (generic) |
- * hash | binary |
- * height | integer |
- * txoutchanges | binary |
- * transactions | binary |
- *
- *
- *
- * openoutputs table
- * Field Name | Type (generic) |
- * hash | binary |
- * index | integer |
- * height | integer |
- * value | integer |
- * scriptbytes | binary |
- * toaddress | string |
- * addresstargetable | integer |
- * coinbase | boolean |
- *
- */
-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.