mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-03 14:07:14 +00:00
1) Fixed the height field to store the height for both coinbase/non coinbase open outputs.
2) Thanks to Ed Lee - Fixed Issue 447 : H2 store: block header hash code is off by 4 bytes. This fix also applies to Postgres and MySQL stores. 3) Added a coinbase field to the openoutputs table and updated code to use this value. 4) Updated field type of ‘value’ in the openoutputs table from bytes to long. 5) Updated the stores (DatabaseFullPrunedBlockStore) with a compatibility check. 6) Updated field type of ‘addresstargetable’ in the openoutputs table from int to tinyint/smallint.
This commit is contained in:
committed by
Mike Hearn
parent
fbf62614b4
commit
9004166122
@@ -442,6 +442,7 @@
|
|||||||
<version>1.4.0</version>
|
<version>1.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- Note this is an optional dependency: Postgres blockstore -->
|
<!-- Note this is an optional dependency: Postgres blockstore -->
|
||||||
|
<!-- To Test remove optional -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>postgresql</groupId>
|
<groupId>postgresql</groupId>
|
||||||
<artifactId>postgresql</artifactId>
|
<artifactId>postgresql</artifactId>
|
||||||
@@ -449,6 +450,7 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- Note this is an optional dependency: MySQL blockstore -->
|
<!-- Note this is an optional dependency: MySQL blockstore -->
|
||||||
|
<!-- To Test remove optional -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>mysql</groupId>
|
<groupId>mysql</groupId>
|
||||||
<artifactId>mysql-connector-java</artifactId>
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
|||||||
@@ -206,8 +206,11 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
|||||||
// Coinbases can't be spent until they mature, to avoid re-orgs destroying entire transaction
|
// Coinbases can't be spent until they mature, to avoid re-orgs destroying entire transaction
|
||||||
// chains. The assumption is there will ~never be re-orgs deeper than the spendable coinbase
|
// chains. The assumption is there will ~never be re-orgs deeper than the spendable coinbase
|
||||||
// chain depth.
|
// chain depth.
|
||||||
if (height - prevOut.getHeight() < params.getSpendableCoinbaseDepth())
|
if (prevOut.isCoinbase()) {
|
||||||
throw new VerificationException("Tried to spend coinbase at depth " + (height - prevOut.getHeight()));
|
if (height - prevOut.getHeight() < params.getSpendableCoinbaseDepth()) {
|
||||||
|
throw new VerificationException("Tried to spend coinbase at depth " + (height - prevOut.getHeight()));
|
||||||
|
}
|
||||||
|
}
|
||||||
// TODO: Check we're not spending the genesis transaction here. Satoshis code won't allow it.
|
// TODO: Check we're not spending the genesis transaction here. Satoshis code won't allow it.
|
||||||
valueIn = valueIn.add(prevOut.getValue());
|
valueIn = valueIn.add(prevOut.getValue());
|
||||||
if (verifyFlags.contains(VerifyFlag.P2SH)) {
|
if (verifyFlags.contains(VerifyFlag.P2SH)) {
|
||||||
@@ -332,7 +335,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
|||||||
in.getOutpoint().getIndex());
|
in.getOutpoint().getIndex());
|
||||||
if (prevOut == null)
|
if (prevOut == null)
|
||||||
throw new VerificationException("Attempted spend of a non-existent or already spent output!");
|
throw new VerificationException("Attempted spend of a non-existent or already spent output!");
|
||||||
if (newBlock.getHeight() - prevOut.getHeight() < params.getSpendableCoinbaseDepth())
|
if (prevOut.isCoinbase() && newBlock.getHeight() - prevOut.getHeight() < params.getSpendableCoinbaseDepth())
|
||||||
throw new VerificationException("Tried to spend coinbase at depth " + (newBlock.getHeight() - prevOut.getHeight()));
|
throw new VerificationException("Tried to spend coinbase at depth " + (newBlock.getHeight() - prevOut.getHeight()));
|
||||||
valueIn = valueIn.add(prevOut.getValue());
|
valueIn = valueIn.add(prevOut.getValue());
|
||||||
if (verifyFlags.contains(VerifyFlag.P2SH)) {
|
if (verifyFlags.contains(VerifyFlag.P2SH)) {
|
||||||
|
|||||||
@@ -38,35 +38,36 @@ public class StoredTransactionOutput implements Serializable {
|
|||||||
private Sha256Hash hash;
|
private Sha256Hash hash;
|
||||||
/** Which output of that transaction we are talking about. */
|
/** Which output of that transaction we are talking about. */
|
||||||
private long index;
|
private long index;
|
||||||
|
/** The height of the tx of this output */
|
||||||
/** arbitrary value lower than -{@link NetworkParameters#spendableCoinbaseDepth}
|
|
||||||
* (not too low to get overflows when we do blockHeight - NONCOINBASE_HEIGHT, though) */
|
|
||||||
private static final int NONCOINBASE_HEIGHT = -200;
|
|
||||||
/** The height of the creating block (for coinbases, NONCOINBASE_HEIGHT otherwise) */
|
|
||||||
private int height;
|
private int height;
|
||||||
|
/** If this output is from a coinbase tx */
|
||||||
|
private boolean coinbase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a stored transaction output
|
* Creates a stored transaction output.
|
||||||
* @param hash the hash of the containing transaction
|
* @param hash The hash of the containing transaction
|
||||||
* @param index the outpoint
|
* @param index The outpoint.
|
||||||
* @param value the value available
|
* @param value The value available.
|
||||||
* @param height the height this output was created in
|
* @param height The height this output was created in.
|
||||||
* @param scriptBytes
|
* @param coinbase The coinbase flag.
|
||||||
|
* @param scriptBytes The script bytes.
|
||||||
*/
|
*/
|
||||||
public StoredTransactionOutput(Sha256Hash hash, long index, Coin value, int height, boolean isCoinbase, byte[] scriptBytes) {
|
public StoredTransactionOutput(Sha256Hash hash, long index, Coin value, int height, boolean coinbase, byte[] scriptBytes) {
|
||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.height = isCoinbase ? height : NONCOINBASE_HEIGHT;
|
this.height = height;
|
||||||
this.scriptBytes = scriptBytes;
|
this.scriptBytes = scriptBytes;
|
||||||
|
this.coinbase = coinbase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StoredTransactionOutput(Sha256Hash hash, TransactionOutput out, int height, boolean isCoinbase) {
|
public StoredTransactionOutput(Sha256Hash hash, TransactionOutput out, int height, boolean coinbase) {
|
||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
this.index = out.getIndex();
|
this.index = out.getIndex();
|
||||||
this.value = out.getValue();
|
this.value = out.getValue();
|
||||||
this.height = isCoinbase ? height : NONCOINBASE_HEIGHT;
|
this.height = height;
|
||||||
this.scriptBytes = out.getScriptBytes();
|
this.scriptBytes = out.getScriptBytes();
|
||||||
|
this.coinbase = coinbase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StoredTransactionOutput(InputStream in) throws IOException {
|
public StoredTransactionOutput(InputStream in) throws IOException {
|
||||||
@@ -97,11 +98,19 @@ public class StoredTransactionOutput implements Serializable {
|
|||||||
((in.read() & 0xFF) << 8) |
|
((in.read() & 0xFF) << 8) |
|
||||||
((in.read() & 0xFF) << 16) |
|
((in.read() & 0xFF) << 16) |
|
||||||
((in.read() & 0xFF) << 24);
|
((in.read() & 0xFF) << 24);
|
||||||
|
|
||||||
|
byte[] coinbaseByte = new byte[1];
|
||||||
|
in.read(coinbaseByte);
|
||||||
|
if (coinbaseByte[0] == 1) {
|
||||||
|
coinbase = true;
|
||||||
|
} else {
|
||||||
|
coinbase = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The value which this Transaction output holds
|
* The value which this Transaction output holds.
|
||||||
* @return the value
|
* @return the value.
|
||||||
*/
|
*/
|
||||||
public Coin getValue() {
|
public Coin getValue() {
|
||||||
return value;
|
return value;
|
||||||
@@ -109,35 +118,44 @@ public class StoredTransactionOutput implements Serializable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The backing script bytes which can be turned into a Script object.
|
* The backing script bytes which can be turned into a Script object.
|
||||||
* @return the scriptBytes
|
* @return the scriptBytes.
|
||||||
*/
|
*/
|
||||||
public byte[] getScriptBytes() {
|
public byte[] getScriptBytes() {
|
||||||
return scriptBytes;
|
return scriptBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The hash of the transaction which holds this output
|
* The hash of the transaction which holds this output.
|
||||||
* @return the hash
|
* @return the hash.
|
||||||
*/
|
*/
|
||||||
public Sha256Hash getHash() {
|
public Sha256Hash getHash() {
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The index of this output in the transaction which holds it
|
* The index of this output in the transaction which holds it.
|
||||||
* @return the index
|
* @return the index.
|
||||||
*/
|
*/
|
||||||
public long getIndex() {
|
public long getIndex() {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the height of the block that created this output (or -1 if this output was not created by a coinbase)
|
* Gets the height of the block that created this output.
|
||||||
|
* @return The height.
|
||||||
*/
|
*/
|
||||||
public int getHeight() {
|
public int getHeight() {
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the flag of whether this was created by a coinbase tx.
|
||||||
|
* @return The coinbase flag.
|
||||||
|
*/
|
||||||
|
public boolean isCoinbase() {
|
||||||
|
return coinbase;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("Stored TxOut of %s (%s:%d)", value.toFriendlyString(), hash.toString(), index);
|
return String.format("Stored TxOut of %s (%s:%d)", value.toFriendlyString(), hash.toString(), index);
|
||||||
@@ -173,5 +191,13 @@ public class StoredTransactionOutput implements Serializable {
|
|||||||
bos.write(0xFF & (height >> 8));
|
bos.write(0xFF & (height >> 8));
|
||||||
bos.write(0xFF & (height >> 16));
|
bos.write(0xFF & (height >> 16));
|
||||||
bos.write(0xFF & (height >> 24));
|
bos.write(0xFF & (height >> 24));
|
||||||
|
|
||||||
|
byte[] coinbaseByte = new byte[1];
|
||||||
|
if(coinbase) {
|
||||||
|
coinbaseByte[0] = 1;
|
||||||
|
} else {
|
||||||
|
coinbaseByte[0] = 0;
|
||||||
|
}
|
||||||
|
bos.write(coinbaseByte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,7 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>A generic full pruned block store for a relational database. This generic class requires
|
* <p>A generic full pruned block store for a relational database. This generic class requires
|
||||||
@@ -79,10 +76,11 @@ import java.util.Properties;
|
|||||||
* <tr><td>hash</td><td>binary</td></tr>
|
* <tr><td>hash</td><td>binary</td></tr>
|
||||||
* <tr><td>index</td><td>integer</td></tr>
|
* <tr><td>index</td><td>integer</td></tr>
|
||||||
* <tr><td>height</td><td>integer</td></tr>
|
* <tr><td>height</td><td>integer</td></tr>
|
||||||
* <tr><td>value</td><td>binary</td></tr>
|
* <tr><td>value</td><td>integer</td></tr>
|
||||||
* <tr><td>scriptbytes</td><td>binary</td></tr>
|
* <tr><td>scriptbytes</td><td>binary</td></tr>
|
||||||
* <tr><td>toaddress</td><td>string</td></tr>
|
* <tr><td>toaddress</td><td>string</td></tr>
|
||||||
* <tr><td>addresstargetable</td><td>integer</td></tr>
|
* <tr><td>addresstargetable</td><td>integer</td></tr>
|
||||||
|
* <tr><td>coinbase</td><td>boolean</td></tr>
|
||||||
* </table>
|
* </table>
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
@@ -90,43 +88,52 @@ import java.util.Properties;
|
|||||||
public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockStore {
|
public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockStore {
|
||||||
private static final Logger log = LoggerFactory.getLogger(DatabaseFullPrunedBlockStore.class);
|
private static final Logger log = LoggerFactory.getLogger(DatabaseFullPrunedBlockStore.class);
|
||||||
|
|
||||||
private static final String CHAIN_HEAD_SETTING = "chainhead";
|
private static final String CHAIN_HEAD_SETTING = "chainhead";
|
||||||
private static final String VERIFIED_CHAIN_HEAD_SETTING = "verifiedchainhead";
|
private static final String VERIFIED_CHAIN_HEAD_SETTING = "verifiedchainhead";
|
||||||
private static final String VERSION_SETTING = "version";
|
private static final String VERSION_SETTING = "version";
|
||||||
|
|
||||||
// drop table SQL
|
// Drop table SQL.
|
||||||
private static final String DROP_SETTINGS_TABLE = "DROP TABLE settings";
|
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_HEADERS_TABLE = "DROP TABLE headers";
|
||||||
private static final String DROP_UNDOABLE_TABLE = "DROP TABLE undoableblocks";
|
private static final String DROP_UNDOABLE_TABLE = "DROP TABLE undoableblocks";
|
||||||
private static final String DROP_OPEN_OUTPUT_TABLE = "DROP TABLE openoutputs";
|
private static final String DROP_OPEN_OUTPUT_TABLE = "DROP TABLE openoutputs";
|
||||||
|
|
||||||
// SQL Queries
|
// Queries SQL.
|
||||||
private static final String SELECT_SETTINGS_SQL = "SELECT value FROM settings WHERE name = ?";
|
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 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 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 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 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 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 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 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 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 DELETE_UNDOABLEBLOCKS_SQL = "DELETE FROM undoableBlocks WHERE height <= ?";
|
||||||
|
|
||||||
private static final String SELECT_OPENOUTPUTS_SQL = "SELECT height, value, scriptBytes FROM openOutputs WHERE hash = ? AND index = ?";
|
private static final String SELECT_OPENOUTPUTS_SQL = "SELECT height, value, scriptBytes, coinbase FROM openOutputs WHERE hash = ? AND index = ?";
|
||||||
private static final String SELECT_OPENOUTPUTS_COUNT_SQL = "SELECT COUNT(*) FROM openOutputs WHERE hash = ?";
|
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) VALUES (?, ?, ?, ?, ?, ?, ?)";
|
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 DELETE_OPENOUTPUTS_SQL = "DELETE FROM openOutputs WHERE hash = ? AND index = ?";
|
||||||
|
|
||||||
// dump table SQL (this is just for data sizing statistics).
|
// 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_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_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_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_DUMP_OPENOUTPUTS_SQL = "SELECT value, scriptBytes FROM openOutputs";
|
||||||
|
|
||||||
private static final String SELECT_TRANSACTION_OUTPUTS_SQL = "SELECT value, scriptBytes FROM openOutputs where toaddress = ?";
|
private static final String SELECT_TRANSACTION_OUTPUTS_SQL = "SELECT value, scriptBytes, height FROM openOutputs where toaddress = ?";
|
||||||
private static final String SELECT_TRANSACTION_OUTPUTS_WITH_HEIGHT_SQL = "SELECT value, scriptBytes FROM openOutputs where toaddress = ? AND height <= ?";
|
private static final String SELECT_TRANSACTION_OUTPUTS_WITH_HEIGHT_SQL = "SELECT value, scriptBytes, height FROM openOutputs where toaddress = ? AND height <= ?";
|
||||||
|
|
||||||
|
// 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 Sha256Hash chainHeadHash;
|
||||||
protected StoredBlock chainHeadBlock;
|
protected StoredBlock chainHeadBlock;
|
||||||
@@ -175,8 +182,10 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Create tables if needed
|
// Create tables if needed
|
||||||
if (!tableExists("settings")) {
|
if (!tablesExists()) {
|
||||||
createTables();
|
createTables();
|
||||||
|
} else {
|
||||||
|
checkCompatibility();
|
||||||
}
|
}
|
||||||
initFromDatabase();
|
initFromDatabase();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -191,13 +200,6 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
*/
|
*/
|
||||||
protected abstract String getDatabaseDriverClass();
|
protected abstract String getDatabaseDriverClass();
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the SQL statement that checks is a particular table exists.
|
|
||||||
* @param tableName The name of the table.
|
|
||||||
* @return The SQL select statement.
|
|
||||||
*/
|
|
||||||
protected abstract String getTableExistSQL(String tableName);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the SQL statements that create the schema (DDL).
|
* Get the SQL statements that create the schema (DDL).
|
||||||
* @return The list of SQL statements.
|
* @return The list of SQL statements.
|
||||||
@@ -224,12 +226,30 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
protected abstract String getDuplicateKeyErrorCode();
|
protected abstract String getDuplicateKeyErrorCode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the SQL to select the total balance for a given address. This has been abstracted as
|
* Get the SQL to select the total balance for a given address.
|
||||||
* difference databases will handle casting of return values differently (since values/balances
|
|
||||||
* are stored as bytes within the database for space reasons).
|
|
||||||
* @return The SQL prepared statement.
|
* @return The SQL prepared statement.
|
||||||
*/
|
*/
|
||||||
protected abstract String getBalanceSelectSQL();
|
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<String> getCompatibilitySQL() {
|
||||||
|
List<String> sqlStatements = new ArrayList<String>();
|
||||||
|
sqlStatements.add(SELECT_COMPATIBILITY_COINBASE_SQL);
|
||||||
|
return sqlStatements;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the SQL to select the transaction outputs for a given address.
|
* Get the SQL to select the transaction outputs for a given address.
|
||||||
@@ -456,21 +476,51 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a table exists within the database.
|
* <p>Check if a tables exists within the database.</p>
|
||||||
* @param table The name of the table.
|
*
|
||||||
* @return If the table exists.
|
* <p>This specifically checks for the 'settings' table and
|
||||||
|
* if it exists makes an assumption that the rest of the data
|
||||||
|
* structures are present.</p>
|
||||||
|
*
|
||||||
|
* @return If the tables exists.
|
||||||
* @throws java.sql.SQLException
|
* @throws java.sql.SQLException
|
||||||
*/
|
*/
|
||||||
private boolean tableExists(String table) throws SQLException {
|
private boolean tablesExists() throws SQLException {
|
||||||
Statement s = conn.get().createStatement();
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
ResultSet results = s.executeQuery(getTableExistSQL(table));
|
ps = conn.get().prepareStatement(getTablesExistSQL());
|
||||||
|
ResultSet results = ps.executeQuery();
|
||||||
results.close();
|
results.close();
|
||||||
return true;
|
return true;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
s.close();
|
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) {
|
||||||
|
String message = "Database block store is not compatible with the current release. " +
|
||||||
|
"See bitcoinj release notes for further information.";
|
||||||
|
throw new BlockStoreException(message);
|
||||||
|
} finally {
|
||||||
|
if (ps != null && !ps.isClosed()) {
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,7 +597,6 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
if (!rs.next()) {
|
if (!rs.next()) {
|
||||||
throw new BlockStoreException("corrupt database block store - no chain head pointer");
|
throw new BlockStoreException("corrupt database block store - no chain head pointer");
|
||||||
}
|
}
|
||||||
//Sha256Hash hash = new Sha256Hash(rs.getBytes(1));
|
|
||||||
Sha256Hash hash = new Sha256Hash(rs.getBytes(1));
|
Sha256Hash hash = new Sha256Hash(rs.getBytes(1));
|
||||||
rs.close();
|
rs.close();
|
||||||
this.chainHeadBlock = get(hash);
|
this.chainHeadBlock = get(hash);
|
||||||
@@ -576,7 +625,7 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
conn.get().prepareStatement(getInsertHeadersSQL());
|
conn.get().prepareStatement(getInsertHeadersSQL());
|
||||||
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
||||||
byte[] hashBytes = new byte[28];
|
byte[] hashBytes = new byte[28];
|
||||||
System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 3, hashBytes, 0, 28);
|
System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 4, hashBytes, 0, 28);
|
||||||
s.setBytes(1, hashBytes);
|
s.setBytes(1, hashBytes);
|
||||||
s.setBytes(2, storedBlock.getChainWork().toByteArray());
|
s.setBytes(2, storedBlock.getChainWork().toByteArray());
|
||||||
s.setInt(3, storedBlock.getHeight());
|
s.setInt(3, storedBlock.getHeight());
|
||||||
@@ -594,7 +643,7 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
s.setBoolean(1, true);
|
s.setBoolean(1, true);
|
||||||
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
||||||
byte[] hashBytes = new byte[28];
|
byte[] hashBytes = new byte[28];
|
||||||
System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 3, hashBytes, 0, 28);
|
System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 4, hashBytes, 0, 28);
|
||||||
s.setBytes(2, hashBytes);
|
s.setBytes(2, hashBytes);
|
||||||
s.executeUpdate();
|
s.executeUpdate();
|
||||||
s.close();
|
s.close();
|
||||||
@@ -617,7 +666,7 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
maybeConnect();
|
maybeConnect();
|
||||||
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
||||||
byte[] hashBytes = new byte[28];
|
byte[] hashBytes = new byte[28];
|
||||||
System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 3, hashBytes, 0, 28);
|
System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 4, hashBytes, 0, 28);
|
||||||
int height = storedBlock.getHeight();
|
int height = storedBlock.getHeight();
|
||||||
byte[] transactions = null;
|
byte[] transactions = null;
|
||||||
byte[] txOutChanges = null;
|
byte[] txOutChanges = null;
|
||||||
@@ -697,7 +746,7 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
.prepareStatement(getSelectHeadersSQL());
|
.prepareStatement(getSelectHeadersSQL());
|
||||||
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
||||||
byte[] hashBytes = new byte[28];
|
byte[] hashBytes = new byte[28];
|
||||||
System.arraycopy(hash.getBytes(), 3, hashBytes, 0, 28);
|
System.arraycopy(hash.getBytes(), 4, hashBytes, 0, 28);
|
||||||
s.setBytes(1, hashBytes);
|
s.setBytes(1, hashBytes);
|
||||||
ResultSet results = s.executeQuery();
|
ResultSet results = s.executeQuery();
|
||||||
if (!results.next()) {
|
if (!results.next()) {
|
||||||
@@ -754,7 +803,7 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
||||||
|
|
||||||
byte[] hashBytes = new byte[28];
|
byte[] hashBytes = new byte[28];
|
||||||
System.arraycopy(hash.getBytes(), 3, hashBytes, 0, 28);
|
System.arraycopy(hash.getBytes(), 4, hashBytes, 0, 28);
|
||||||
s.setBytes(1, hashBytes);
|
s.setBytes(1, hashBytes);
|
||||||
ResultSet results = s.executeQuery();
|
ResultSet results = s.executeQuery();
|
||||||
if (!results.next()) {
|
if (!results.next()) {
|
||||||
@@ -887,9 +936,10 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
}
|
}
|
||||||
// Parse it.
|
// Parse it.
|
||||||
int height = results.getInt(1);
|
int height = results.getInt(1);
|
||||||
Coin value = Coin.valueOf(new BigInteger(results.getBytes(2)).longValue());
|
Coin value = Coin.valueOf(results.getLong(2));
|
||||||
// Tell the StoredTransactionOutput that we are a coinbase, as that is encoded in height
|
byte[] scriptBytes = results.getBytes(3);
|
||||||
StoredTransactionOutput txout = new StoredTransactionOutput(hash, index, value, height, true, results.getBytes(3));
|
boolean coinbase = results.getBoolean(4);
|
||||||
|
StoredTransactionOutput txout = new StoredTransactionOutput(hash, index, value, height, coinbase, scriptBytes);
|
||||||
return txout;
|
return txout;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new BlockStoreException(ex);
|
throw new BlockStoreException(ex);
|
||||||
@@ -947,10 +997,11 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
// index is actually an unsigned int
|
// index is actually an unsigned int
|
||||||
s.setInt(2, (int) out.getIndex());
|
s.setInt(2, (int) out.getIndex());
|
||||||
s.setInt(3, out.getHeight());
|
s.setInt(3, out.getHeight());
|
||||||
s.setBytes(4, BigInteger.valueOf(out.getValue().value).toByteArray());
|
s.setLong(4, out.getValue().value);
|
||||||
s.setBytes(5, out.getScriptBytes());
|
s.setBytes(5, out.getScriptBytes());
|
||||||
s.setString(6, dbAddress);
|
s.setString(6, dbAddress);
|
||||||
s.setInt(7, type);
|
s.setInt(7, type);
|
||||||
|
s.setBoolean(8, out.isCoinbase());
|
||||||
s.executeUpdate();
|
s.executeUpdate();
|
||||||
s.close();
|
s.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -1166,9 +1217,35 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
* @throws BlockStoreException If there is an error getting the list of outputs.
|
* @throws BlockStoreException If there is an error getting the list of outputs.
|
||||||
*/
|
*/
|
||||||
public List<TransactionOutput> getOpenTransactionOutputs(Address address, @Nullable Integer maxHeight) throws BlockStoreException {
|
public List<TransactionOutput> getOpenTransactionOutputs(Address address, @Nullable Integer maxHeight) throws BlockStoreException {
|
||||||
|
List<TransactionOutput> outputs = new ArrayList<TransactionOutput>();
|
||||||
|
for(List<TransactionOutput> outputsSubList : getOpenTransactionOutputsHeightMap(address, maxHeight).values()) {
|
||||||
|
outputs.addAll(outputsSubList);
|
||||||
|
}
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the map of the {@link org.bitcoinj.core.TransactionOutput}'s keyed on their height for a given address.
|
||||||
|
* @param address The address.
|
||||||
|
* @return The map of transaction outputs (list) keyed by height
|
||||||
|
* @throws BlockStoreException If there is an error getting the list out outputs.
|
||||||
|
*/
|
||||||
|
public Map<Integer, List<TransactionOutput>> getOpenTransactionOutputsHeightMap(Address address) throws BlockStoreException {
|
||||||
|
return getOpenTransactionOutputsHeightMap(address, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the map of the {@link org.bitcoinj.core.TransactionOutput}'s keyed on their height for a given address
|
||||||
|
* and a specified height.
|
||||||
|
* @param address The address.
|
||||||
|
* @param maxHeight The minimum block height of this tx output (inclusive).
|
||||||
|
* @return The map of transaction outputs (list) keyed by height
|
||||||
|
* @throws BlockStoreException If there is an error getting the list out outputs.
|
||||||
|
*/
|
||||||
|
public Map<Integer, List<TransactionOutput>> getOpenTransactionOutputsHeightMap(Address address, @Nullable Integer maxHeight) throws BlockStoreException {
|
||||||
maybeConnect();
|
maybeConnect();
|
||||||
PreparedStatement s = null;
|
PreparedStatement s = null;
|
||||||
List<TransactionOutput> outputs = new ArrayList<TransactionOutput>();
|
HashMap<Integer, List<TransactionOutput>> outputsMap = new HashMap<Integer, List<TransactionOutput>>();
|
||||||
try {
|
try {
|
||||||
if(maxHeight != null) {
|
if(maxHeight != null) {
|
||||||
s = conn.get().prepareStatement(getTrasactionOutputWithHeightSelectSQL());
|
s = conn.get().prepareStatement(getTrasactionOutputWithHeightSelectSQL());
|
||||||
@@ -1181,19 +1258,25 @@ public abstract class DatabaseFullPrunedBlockStore implements FullPrunedBlockSto
|
|||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
Coin amount = Coin.valueOf((new BigInteger(rs.getBytes(1)).longValue()));
|
Coin amount = Coin.valueOf((new BigInteger(rs.getBytes(1)).longValue()));
|
||||||
byte[] scriptBytes = rs.getBytes(2);
|
byte[] scriptBytes = rs.getBytes(2);
|
||||||
|
int height = rs.getInt(3);
|
||||||
TransactionOutput output = new TransactionOutput(params, null, amount, scriptBytes);
|
TransactionOutput output = new TransactionOutput(params, null, amount, scriptBytes);
|
||||||
outputs.add(output);
|
if (outputsMap.containsKey(height)) {
|
||||||
|
outputsMap.get(height).add(output);
|
||||||
|
} else {
|
||||||
|
List outputs = new ArrayList<TransactionOutput>();
|
||||||
|
outputs.add(output);
|
||||||
|
outputsMap.put(height, outputs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return outputs;
|
return outputsMap;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new BlockStoreException(ex);
|
throw new BlockStoreException(ex);
|
||||||
} finally {
|
} finally {
|
||||||
if (s != null) {
|
if (s != null)
|
||||||
try {
|
try {
|
||||||
s.close();
|
s.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new BlockStoreException("Could not close statement");
|
throw new BlockStoreException("Could not close statement");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,10 +68,11 @@ public class H2FullPrunedBlockStore extends DatabaseFullPrunedBlockStore {
|
|||||||
+ "hash BINARY(32) NOT NULL,"
|
+ "hash BINARY(32) NOT NULL,"
|
||||||
+ "index INT NOT NULL,"
|
+ "index INT NOT NULL,"
|
||||||
+ "height INT NOT NULL,"
|
+ "height INT NOT NULL,"
|
||||||
+ "value BLOB NOT NULL,"
|
+ "value BIGINT NOT NULL,"
|
||||||
+ "scriptBytes BLOB NOT NULL,"
|
+ "scriptBytes BLOB NOT NULL,"
|
||||||
+ "toaddress VARCHAR(35),"
|
+ "toaddress VARCHAR(35),"
|
||||||
+ "addresstargetable INT,"
|
+ "addresstargetable TINYINT,"
|
||||||
|
+ "coinbase BOOLEAN,"
|
||||||
+ "PRIMARY KEY (hash, index),"
|
+ "PRIMARY KEY (hash, index),"
|
||||||
+ ")";
|
+ ")";
|
||||||
|
|
||||||
@@ -82,8 +83,6 @@ public class H2FullPrunedBlockStore extends DatabaseFullPrunedBlockStore {
|
|||||||
private static final String CREATE_OUTPUTS_HASH_INDEX = "CREATE INDEX openoutputs_hash_idx ON openoutputs (hash)";
|
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)";
|
private static final String CREATE_UNDOABLE_TABLE_INDEX = "CREATE INDEX undoableblocks_height_idx ON undoableBlocks (height)";
|
||||||
|
|
||||||
private static final String SELECT_BALANCE_SQL = "SELECT value from openoutputs where toaddress = ?";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new H2FullPrunedBlockStore
|
* Creates a new H2FullPrunedBlockStore
|
||||||
* @param params A copy of the NetworkParameters used
|
* @param params A copy of the NetworkParameters used
|
||||||
@@ -116,11 +115,6 @@ public class H2FullPrunedBlockStore extends DatabaseFullPrunedBlockStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public BigInteger calculateBalanceForAddress(Address address) throws BlockStoreException {
|
|
||||||
return calculateBalanceForAddress(address, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getDuplicateKeyErrorCode() {
|
protected String getDuplicateKeyErrorCode() {
|
||||||
return H2_DUPLICATE_KEY_ERROR_CODE;
|
return H2_DUPLICATE_KEY_ERROR_CODE;
|
||||||
@@ -153,19 +147,8 @@ public class H2FullPrunedBlockStore extends DatabaseFullPrunedBlockStore {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getTableExistSQL(String tableName) {
|
|
||||||
return "SELECT * FROM " + tableName + " WHERE 1 = 2";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getDatabaseDriverClass() {
|
protected String getDatabaseDriverClass() {
|
||||||
return DATABASE_DRIVER_CLASS;
|
return DATABASE_DRIVER_CLASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getBalanceSelectSQL() {
|
|
||||||
return SELECT_BALANCE_SQL;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -63,10 +63,11 @@ public class MySQLFullPrunedBlockStore extends DatabaseFullPrunedBlockStore {
|
|||||||
" hash blob NOT NULL,\n" +
|
" hash blob NOT NULL,\n" +
|
||||||
" `index` integer NOT NULL,\n" +
|
" `index` integer NOT NULL,\n" +
|
||||||
" height integer NOT NULL,\n" +
|
" height integer NOT NULL,\n" +
|
||||||
" value blob NOT NULL,\n" +
|
" value bigint NOT NULL,\n" +
|
||||||
" scriptbytes mediumblob NOT NULL,\n" +
|
" scriptbytes mediumblob NOT NULL,\n" +
|
||||||
" toaddress varchar(35),\n" +
|
" toaddress varchar(35),\n" +
|
||||||
" addresstargetable integer,\n" +
|
" addresstargetable tinyint(1),\n" +
|
||||||
|
" coinbase boolean,\n" +
|
||||||
" CONSTRAINT `openoutputs_pk` PRIMARY KEY (hash(32),`index`) USING btree\n" +
|
" CONSTRAINT `openoutputs_pk` PRIMARY KEY (hash(32),`index`) USING btree\n" +
|
||||||
")\n";
|
")\n";
|
||||||
|
|
||||||
@@ -78,12 +79,10 @@ public class MySQLFullPrunedBlockStore extends DatabaseFullPrunedBlockStore {
|
|||||||
private static final String CREATE_UNDOABLE_TABLE_INDEX = "CREATE INDEX undoableblocks_height_idx ON undoableBlocks (height) 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.
|
// 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 FROM openOutputs WHERE hash = ? AND `index` = ?";
|
private static final String SELECT_OPENOUTPUTS_SQL = "SELECT height, value, scriptBytes, coinbase FROM openOutputs WHERE hash = ? AND `index` = ?";
|
||||||
private static final String INSERT_OPENOUTPUTS_SQL = "INSERT INTO openOutputs (hash, `index`, height, value, scriptBytes, toAddress, addressTargetable) VALUES (?, ?, ?, ?, ?, ?, ?)";
|
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 DELETE_OPENOUTPUTS_SQL = "DELETE FROM openOutputs WHERE hash = ? AND `index` = ?";
|
||||||
|
|
||||||
private static final String SELECT_BALANCE_SQL = "SELECT sum(conv(hex(value),16,10)) from openoutputs where toaddress = ?";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new MySQLFullPrunedBlockStore.
|
* Creates a new MySQLFullPrunedBlockStore.
|
||||||
*
|
*
|
||||||
@@ -147,18 +146,8 @@ public class MySQLFullPrunedBlockStore extends DatabaseFullPrunedBlockStore {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getTableExistSQL(String tableName) {
|
|
||||||
return "SELECT * FROM " + tableName + " WHERE 1 = 2";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getDatabaseDriverClass() {
|
protected String getDatabaseDriverClass() {
|
||||||
return DATABASE_DRIVER_CLASS;
|
return DATABASE_DRIVER_CLASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getBalanceSelectSQL() {
|
|
||||||
return SELECT_BALANCE_SQL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,10 +71,11 @@ public class PostgresFullPrunedBlockStore extends DatabaseFullPrunedBlockStore {
|
|||||||
" hash bytea NOT NULL,\n" +
|
" hash bytea NOT NULL,\n" +
|
||||||
" index integer NOT NULL,\n" +
|
" index integer NOT NULL,\n" +
|
||||||
" height integer NOT NULL,\n" +
|
" height integer NOT NULL,\n" +
|
||||||
" value bytea NOT NULL,\n" +
|
" value bigint NOT NULL,\n" +
|
||||||
" scriptbytes bytea NOT NULL,\n" +
|
" scriptbytes bytea NOT NULL,\n" +
|
||||||
" toaddress character varying(35),\n" +
|
" toaddress character varying(35),\n" +
|
||||||
" addresstargetable integer,\n" +
|
" addresstargetable smallint,\n" +
|
||||||
|
" coinbase boolean,\n" +
|
||||||
" CONSTRAINT openoutputs_pk PRIMARY KEY (hash,index)\n" +
|
" CONSTRAINT openoutputs_pk PRIMARY KEY (hash,index)\n" +
|
||||||
")\n";
|
")\n";
|
||||||
|
|
||||||
@@ -87,8 +88,6 @@ public class PostgresFullPrunedBlockStore extends DatabaseFullPrunedBlockStore {
|
|||||||
|
|
||||||
private static final String SELECT_UNDOABLEBLOCKS_EXISTS_SQL = "select 1 from undoableBlocks where hash = ?";
|
private static final String SELECT_UNDOABLEBLOCKS_EXISTS_SQL = "select 1 from undoableBlocks where hash = ?";
|
||||||
|
|
||||||
private static final String SELECT_BALANCE_SQL = "select sum(('x'||lpad(substr(value::text, 3, 50),16,'0'))::bit(64)::bigint) from openoutputs where toaddress = ?";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new PostgresFullPrunedBlockStore.
|
* Creates a new PostgresFullPrunedBlockStore.
|
||||||
*
|
*
|
||||||
@@ -160,27 +159,17 @@ public class PostgresFullPrunedBlockStore extends DatabaseFullPrunedBlockStore {
|
|||||||
return sqlStatements;
|
return sqlStatements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getTableExistSQL(String tableName) {
|
|
||||||
return "SELECT * FROM " + tableName + " WHERE 1 = 2";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getDatabaseDriverClass() {
|
protected String getDatabaseDriverClass() {
|
||||||
return DATABASE_DRIVER_CLASS;
|
return DATABASE_DRIVER_CLASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getBalanceSelectSQL() {
|
|
||||||
return SELECT_BALANCE_SQL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void put(StoredBlock storedBlock, StoredUndoableBlock undoableBlock) throws BlockStoreException {
|
public void put(StoredBlock storedBlock, StoredUndoableBlock undoableBlock) throws BlockStoreException {
|
||||||
maybeConnect();
|
maybeConnect();
|
||||||
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
// We skip the first 4 bytes because (on prodnet) the minimum target has 4 0-bytes
|
||||||
byte[] hashBytes = new byte[28];
|
byte[] hashBytes = new byte[28];
|
||||||
System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 3, hashBytes, 0, 28);
|
System.arraycopy(storedBlock.getHeader().getHash().getBytes(), 4, hashBytes, 0, 28);
|
||||||
int height = storedBlock.getHeight();
|
int height = storedBlock.getHeight();
|
||||||
byte[] transactions = null;
|
byte[] transactions = null;
|
||||||
byte[] txOutChanges = null;
|
byte[] txOutChanges = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user