mirror of
https://github.com/Qortal/qortal.git
synced 2025-02-11 17:55:50 +00:00
Convertion to thread-local Connection
Much tidier code thanks to not having to pass Connection objects around as params. Also no need for two forms of the same method, one with Connection param, one without. Also corrected SQL-Transaction-related methods in DB, e.g. commit, rollback, etc. so they use the proper underlying JDBC methods.
This commit is contained in:
parent
3a6276c4a9
commit
5b78268915
@ -7,6 +7,7 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Savepoint;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.hsqldb.jdbc.JDBCPool;
|
||||
@ -17,10 +18,21 @@ import com.google.common.primitives.Bytes;
|
||||
* Helper methods for common database actions.
|
||||
*
|
||||
*/
|
||||
public class DB {
|
||||
public abstract class DB {
|
||||
|
||||
private static JDBCPool connectionPool;
|
||||
private static String connectionUrl;
|
||||
private static ThreadLocal<Connection> local = new ThreadLocal<Connection>() {
|
||||
@Override
|
||||
protected Connection initialValue() {
|
||||
Connection conn = null;
|
||||
try {
|
||||
conn = connectionPool.getConnection();
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open connection pool to database using prior set connection URL.
|
||||
@ -49,38 +61,51 @@ public class DB {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an on-demand Connection from connection pool.
|
||||
* <p>
|
||||
* Mostly used in database-read scenarios whereas database-write scenarios, especially multi-statement transactions, are likely to pass around a Connection
|
||||
* object.
|
||||
* Return thread-local Connection from connection pool.
|
||||
* <p>
|
||||
* By default HSQLDB will wait up to 30 seconds for a pooled connection to become free.
|
||||
*
|
||||
* @return Connection
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static Connection getConnection() throws SQLException {
|
||||
return connectionPool.getConnection();
|
||||
public static Connection getConnection() {
|
||||
return local.get();
|
||||
}
|
||||
|
||||
public static void startTransaction(Connection c) throws SQLException {
|
||||
c.prepareStatement("START TRANSACTION").execute();
|
||||
public static void releaseConnection() {
|
||||
Connection connection = local.get();
|
||||
if (connection != null)
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
|
||||
local.remove();
|
||||
}
|
||||
|
||||
public static void commit(Connection c) throws SQLException {
|
||||
c.prepareStatement("COMMIT").execute();
|
||||
public static void startTransaction() throws SQLException {
|
||||
Connection connection = DB.getConnection();
|
||||
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
|
||||
connection.setAutoCommit(false);
|
||||
}
|
||||
|
||||
public static void rollback(Connection c) throws SQLException {
|
||||
c.prepareStatement("ROLLBACK").execute();
|
||||
public static void commit() throws SQLException {
|
||||
Connection connection = DB.getConnection();
|
||||
connection.commit();
|
||||
connection.setAutoCommit(true);
|
||||
}
|
||||
|
||||
public static void createSavepoint(Connection c, String savepointName) throws SQLException {
|
||||
c.prepareStatement("SAVEPOINT " + savepointName).execute();
|
||||
public static void rollback() throws SQLException {
|
||||
Connection connection = DB.getConnection();
|
||||
connection.rollback();
|
||||
connection.setAutoCommit(true);
|
||||
}
|
||||
|
||||
public static void rollbackToSavepoint(Connection c, String savepointName) throws SQLException {
|
||||
c.prepareStatement("ROLLBACK TO SAVEPOINT " + savepointName).execute();
|
||||
public static Savepoint createSavepoint(String savepointName) throws SQLException {
|
||||
return DB.getConnection().setSavepoint(savepointName);
|
||||
}
|
||||
|
||||
public static void rollbackToSavepoint(Savepoint savepoint) throws SQLException {
|
||||
DB.getConnection().rollback(savepoint);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,15 +118,16 @@ public class DB {
|
||||
*
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static void close() throws SQLException {
|
||||
getConnection().createStatement().execute("SHUTDOWN");
|
||||
public static void shutdown() throws SQLException {
|
||||
DB.getConnection().createStatement().execute("SHUTDOWN");
|
||||
DB.releaseConnection();
|
||||
connectionPool.close(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown and delete database, then rebuild it.
|
||||
* <p>
|
||||
* See {@link DB#close()} for warnings about connections.
|
||||
* See {@link DB#shutdown()} for warnings about connections.
|
||||
* <p>
|
||||
* Note that this only rebuilds the database schema, not the data itself.
|
||||
*
|
||||
@ -109,7 +135,7 @@ public class DB {
|
||||
*/
|
||||
public static void rebuild() throws SQLException {
|
||||
// Shutdown database and close any access
|
||||
DB.close();
|
||||
DB.shutdown();
|
||||
|
||||
// Wipe files (if any)
|
||||
// TODO
|
||||
@ -188,26 +214,8 @@ public class DB {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static ResultSet checkedExecute(String sql, Object... objects) throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
return checkedExecute(connection, sql, objects);
|
||||
}
|
||||
}
|
||||
PreparedStatement preparedStatement = DB.getConnection().prepareStatement(sql);
|
||||
|
||||
/**
|
||||
* Execute SQL using connection and return ResultSet with but added checking.
|
||||
* <p>
|
||||
* Typically for use within an ongoing SQL Transaction.
|
||||
* <p>
|
||||
* <b>Note: calls ResultSet.next()</b> therefore returned ResultSet is already pointing to first row.
|
||||
*
|
||||
* @param connection
|
||||
* @param sql
|
||||
* @param objects
|
||||
* @return ResultSet, or null if there are no found rows
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static ResultSet checkedExecute(Connection connection, String sql, Object... objects) throws SQLException {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql);
|
||||
for (int i = 0; i < objects.length; ++i)
|
||||
// Special treatment for BigDecimals so that they retain their "scale",
|
||||
// which would otherwise be assumed as 0.
|
||||
@ -216,7 +224,7 @@ public class DB {
|
||||
else
|
||||
preparedStatement.setObject(i + 1, objects[i]);
|
||||
|
||||
return checkedExecute(preparedStatement);
|
||||
return DB.checkedExecute(preparedStatement);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -249,12 +257,11 @@ public class DB {
|
||||
* <p>
|
||||
* Typically used after INSERTing NULL as the IDENTIY column's value to fetch what value was actually stored by HSQLDB.
|
||||
*
|
||||
* @param connection
|
||||
* @return Long
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static Long callIdentity(Connection connection) throws SQLException {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("CALL IDENTITY()");
|
||||
public static Long callIdentity() throws SQLException {
|
||||
PreparedStatement preparedStatement = DB.getConnection().prepareStatement("CALL IDENTITY()");
|
||||
ResultSet resultSet = DB.checkedExecute(preparedStatement);
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
@ -280,34 +287,7 @@ public class DB {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static boolean exists(String tableName, String whereClause, Object... objects) throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
return exists(connection, tableName, whereClause, objects);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Efficiently query database, using connection, for existing of matching row.
|
||||
* <p>
|
||||
* Typically for use within an ongoing SQL Transaction.
|
||||
* <p>
|
||||
* {@code whereClause} is SQL "WHERE" clause containing "?" placeholders suitable for use with PreparedStatements.
|
||||
* <p>
|
||||
* Example call:
|
||||
* <p>
|
||||
* {@code Connection connection = DB.getConnection();}<br>
|
||||
* {@code String manufacturer = "Lamborghini";}<br>
|
||||
* {@code int maxMileage = 100_000;}<br>
|
||||
* {@code boolean isAvailable = DB.exists(connection, "Cars", "manufacturer = ? AND mileage <= ?", manufacturer, maxMileage);}
|
||||
*
|
||||
* @param connection
|
||||
* @param tableName
|
||||
* @param whereClause
|
||||
* @param objects
|
||||
* @return true if matching row found in database, false otherwise
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static boolean exists(Connection connection, String tableName, String whereClause, Object... objects) throws SQLException {
|
||||
PreparedStatement preparedStatement = connection
|
||||
PreparedStatement preparedStatement = DB.getConnection()
|
||||
.prepareStatement("SELECT TRUE FROM " + tableName + " WHERE " + whereClause + " ORDER BY NULL LIMIT 1");
|
||||
ResultSet resultSet = DB.checkedExecute(preparedStatement);
|
||||
if (resultSet == null)
|
||||
|
@ -1,6 +1,5 @@
|
||||
package database;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
@ -23,10 +22,7 @@ public class DatabaseUpdates {
|
||||
* @throws SQLException
|
||||
*/
|
||||
private static void incrementDatabaseVersion() throws SQLException {
|
||||
try (final Connection c = DB.getConnection()) {
|
||||
Statement stmt = c.createStatement();
|
||||
stmt.execute("UPDATE DatabaseInfo SET version = version + 1");
|
||||
}
|
||||
DB.getConnection().createStatement().execute("UPDATE DatabaseInfo SET version = version + 1");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,8 +34,8 @@ public class DatabaseUpdates {
|
||||
private static int fetchDatabaseVersion() throws SQLException {
|
||||
int databaseVersion = 0;
|
||||
|
||||
try (final Connection c = DB.getConnection()) {
|
||||
Statement stmt = c.createStatement();
|
||||
try {
|
||||
Statement stmt = DB.getConnection().createStatement();
|
||||
if (stmt.execute("SELECT version FROM DatabaseInfo")) {
|
||||
ResultSet rs = stmt.getResultSet();
|
||||
|
||||
@ -62,234 +58,232 @@ public class DatabaseUpdates {
|
||||
private static boolean databaseUpdating() throws SQLException {
|
||||
int databaseVersion = fetchDatabaseVersion();
|
||||
|
||||
try (final Connection c = DB.getConnection()) {
|
||||
Statement stmt = c.createStatement();
|
||||
Statement stmt = DB.getConnection().createStatement();
|
||||
|
||||
/*
|
||||
* Try not to add too many constraints as much of these checks will be performed during transaction validation. Also some constraints might be too
|
||||
* harsh on competing unconfirmed transactions.
|
||||
*
|
||||
* Only really add "ON DELETE CASCADE" to sub-tables that store type-specific data. For example on sub-types of Transactions like
|
||||
* PaymentTransactions. A counterexample would be adding "ON DELETE CASCADE" to Assets using Assets' "reference" as a foreign key referring to
|
||||
* Transactions' "signature". We want to database to automatically delete complete transaction data (Transactions row and corresponding
|
||||
* PaymentTransactions row), but leave deleting less related table rows (Assets) to the Java logic.
|
||||
*/
|
||||
/*
|
||||
* Try not to add too many constraints as much of these checks will be performed during transaction validation. Also some constraints might be too harsh
|
||||
* on competing unconfirmed transactions.
|
||||
*
|
||||
* Only really add "ON DELETE CASCADE" to sub-tables that store type-specific data. For example on sub-types of Transactions like PaymentTransactions. A
|
||||
* counterexample would be adding "ON DELETE CASCADE" to Assets using Assets' "reference" as a foreign key referring to Transactions' "signature". We
|
||||
* want to database to automatically delete complete transaction data (Transactions row and corresponding PaymentTransactions row), but leave deleting
|
||||
* less related table rows (Assets) to the Java logic.
|
||||
*/
|
||||
|
||||
switch (databaseVersion) {
|
||||
case 0:
|
||||
// create from new
|
||||
stmt.execute("SET DATABASE DEFAULT TABLE TYPE CACHED");
|
||||
stmt.execute("SET FILES SPACE TRUE");
|
||||
stmt.execute("CREATE TABLE DatabaseInfo ( version INTEGER NOT NULL )");
|
||||
stmt.execute("INSERT INTO DatabaseInfo VALUES ( 0 )");
|
||||
stmt.execute("CREATE TYPE BlockSignature AS VARBINARY(128)");
|
||||
stmt.execute("CREATE TYPE Signature AS VARBINARY(64)");
|
||||
stmt.execute("CREATE TYPE QoraAddress AS VARCHAR(36)");
|
||||
stmt.execute("CREATE TYPE QoraPublicKey AS VARBINARY(32)");
|
||||
stmt.execute("CREATE TYPE QoraAmount AS DECIMAL(19, 8)");
|
||||
stmt.execute("CREATE TYPE RegisteredName AS VARCHAR(400) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE NameData AS VARCHAR(4000)");
|
||||
stmt.execute("CREATE TYPE PollName AS VARCHAR(400) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE PollOption AS VARCHAR(400) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE DataHash AS VARCHAR(100)");
|
||||
stmt.execute("CREATE TYPE AssetID AS BIGINT");
|
||||
stmt.execute("CREATE TYPE AssetName AS VARCHAR(400) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE AssetOrderID AS VARCHAR(100)");
|
||||
stmt.execute("CREATE TYPE ATName AS VARCHAR(200) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE ATType AS VARCHAR(200) COLLATE SQL_TEXT_UCC");
|
||||
break;
|
||||
switch (databaseVersion) {
|
||||
case 0:
|
||||
// create from new
|
||||
stmt.execute("SET DATABASE DEFAULT TABLE TYPE CACHED");
|
||||
stmt.execute("SET FILES SPACE TRUE");
|
||||
stmt.execute("CREATE TABLE DatabaseInfo ( version INTEGER NOT NULL )");
|
||||
stmt.execute("INSERT INTO DatabaseInfo VALUES ( 0 )");
|
||||
stmt.execute("CREATE TYPE BlockSignature AS VARBINARY(128)");
|
||||
stmt.execute("CREATE TYPE Signature AS VARBINARY(64)");
|
||||
stmt.execute("CREATE TYPE QoraAddress AS VARCHAR(36)");
|
||||
stmt.execute("CREATE TYPE QoraPublicKey AS VARBINARY(32)");
|
||||
stmt.execute("CREATE TYPE QoraAmount AS DECIMAL(19, 8)");
|
||||
stmt.execute("CREATE TYPE RegisteredName AS VARCHAR(400) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE NameData AS VARCHAR(4000)");
|
||||
stmt.execute("CREATE TYPE PollName AS VARCHAR(400) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE PollOption AS VARCHAR(400) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE DataHash AS VARCHAR(100)");
|
||||
stmt.execute("CREATE TYPE AssetID AS BIGINT");
|
||||
stmt.execute("CREATE TYPE AssetName AS VARCHAR(400) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE AssetOrderID AS VARCHAR(100)");
|
||||
stmt.execute("CREATE TYPE ATName AS VARCHAR(200) COLLATE SQL_TEXT_UCC");
|
||||
stmt.execute("CREATE TYPE ATType AS VARCHAR(200) COLLATE SQL_TEXT_UCC");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Blocks
|
||||
stmt.execute("CREATE TABLE Blocks (signature BlockSignature PRIMARY KEY, version TINYINT NOT NULL, reference BlockSignature, "
|
||||
+ "transaction_count INTEGER NOT NULL, total_fees QoraAmount NOT NULL, transactions_signature Signature NOT NULL, "
|
||||
+ "height INTEGER NOT NULL, generation TIMESTAMP NOT NULL, generating_balance QoraAmount NOT NULL, "
|
||||
+ "generator QoraPublicKey NOT NULL, generator_signature Signature NOT NULL, AT_data VARBINARY(20000), AT_fees QoraAmount)");
|
||||
stmt.execute("CREATE INDEX BlockHeightIndex ON Blocks (height)");
|
||||
stmt.execute("CREATE INDEX BlockGeneratorIndex ON Blocks (generator)");
|
||||
stmt.execute("CREATE INDEX BlockReferenceIndex ON Blocks (reference)");
|
||||
stmt.execute("SET TABLE Blocks NEW SPACE");
|
||||
break;
|
||||
case 1:
|
||||
// Blocks
|
||||
stmt.execute("CREATE TABLE Blocks (signature BlockSignature PRIMARY KEY, version TINYINT NOT NULL, reference BlockSignature, "
|
||||
+ "transaction_count INTEGER NOT NULL, total_fees QoraAmount NOT NULL, transactions_signature Signature NOT NULL, "
|
||||
+ "height INTEGER NOT NULL, generation TIMESTAMP NOT NULL, generating_balance QoraAmount NOT NULL, "
|
||||
+ "generator QoraPublicKey NOT NULL, generator_signature Signature NOT NULL, AT_data VARBINARY(20000), AT_fees QoraAmount)");
|
||||
stmt.execute("CREATE INDEX BlockHeightIndex ON Blocks (height)");
|
||||
stmt.execute("CREATE INDEX BlockGeneratorIndex ON Blocks (generator)");
|
||||
stmt.execute("CREATE INDEX BlockReferenceIndex ON Blocks (reference)");
|
||||
stmt.execute("SET TABLE Blocks NEW SPACE");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Generic transactions (null reference, creator and milestone_block for genesis transactions)
|
||||
stmt.execute("CREATE TABLE Transactions (signature Signature PRIMARY KEY, reference Signature, type TINYINT NOT NULL, "
|
||||
+ "creator QoraPublicKey, creation TIMESTAMP NOT NULL, fee QoraAmount NOT NULL, milestone_block BlockSignature)");
|
||||
stmt.execute("CREATE INDEX TransactionTypeIndex ON Transactions (type)");
|
||||
stmt.execute("CREATE INDEX TransactionCreationIndex ON Transactions (creation)");
|
||||
stmt.execute("CREATE INDEX TransactionCreatorIndex ON Transactions (creator)");
|
||||
stmt.execute("CREATE INDEX TransactionReferenceIndex ON Transactions (reference)");
|
||||
stmt.execute("SET TABLE Transactions NEW SPACE");
|
||||
case 2:
|
||||
// Generic transactions (null reference, creator and milestone_block for genesis transactions)
|
||||
stmt.execute("CREATE TABLE Transactions (signature Signature PRIMARY KEY, reference Signature, type TINYINT NOT NULL, "
|
||||
+ "creator QoraPublicKey, creation TIMESTAMP NOT NULL, fee QoraAmount NOT NULL, milestone_block BlockSignature)");
|
||||
stmt.execute("CREATE INDEX TransactionTypeIndex ON Transactions (type)");
|
||||
stmt.execute("CREATE INDEX TransactionCreationIndex ON Transactions (creation)");
|
||||
stmt.execute("CREATE INDEX TransactionCreatorIndex ON Transactions (creator)");
|
||||
stmt.execute("CREATE INDEX TransactionReferenceIndex ON Transactions (reference)");
|
||||
stmt.execute("SET TABLE Transactions NEW SPACE");
|
||||
|
||||
// Transaction-Block mapping ("signature" is unique as a transaction cannot be included in more than one block)
|
||||
stmt.execute("CREATE TABLE BlockTransactions (block_signature BlockSignature, sequence INTEGER, transaction_signature Signature, "
|
||||
+ "PRIMARY KEY (block_signature, sequence), FOREIGN KEY (transaction_signature) REFERENCES Transactions (signature) ON DELETE CASCADE, "
|
||||
+ "FOREIGN KEY (block_signature) REFERENCES Blocks (signature) ON DELETE CASCADE)");
|
||||
stmt.execute("SET TABLE BlockTransactions NEW SPACE");
|
||||
// Transaction-Block mapping ("signature" is unique as a transaction cannot be included in more than one block)
|
||||
stmt.execute("CREATE TABLE BlockTransactions (block_signature BlockSignature, sequence INTEGER, transaction_signature Signature, "
|
||||
+ "PRIMARY KEY (block_signature, sequence), FOREIGN KEY (transaction_signature) REFERENCES Transactions (signature) ON DELETE CASCADE, "
|
||||
+ "FOREIGN KEY (block_signature) REFERENCES Blocks (signature) ON DELETE CASCADE)");
|
||||
stmt.execute("SET TABLE BlockTransactions NEW SPACE");
|
||||
|
||||
// Unconfirmed transactions
|
||||
// Do we need this? If a transaction doesn't have a corresponding BlockTransactions record then it's unconfirmed?
|
||||
stmt.execute("CREATE TABLE UnconfirmedTransactions (signature Signature PRIMARY KEY, expiry TIMESTAMP NOT NULL)");
|
||||
stmt.execute("CREATE INDEX UnconfirmedTransactionExpiryIndex ON UnconfirmedTransactions (expiry)");
|
||||
// Unconfirmed transactions
|
||||
// Do we need this? If a transaction doesn't have a corresponding BlockTransactions record then it's unconfirmed?
|
||||
stmt.execute("CREATE TABLE UnconfirmedTransactions (signature Signature PRIMARY KEY, expiry TIMESTAMP NOT NULL)");
|
||||
stmt.execute("CREATE INDEX UnconfirmedTransactionExpiryIndex ON UnconfirmedTransactions (expiry)");
|
||||
|
||||
// Transaction recipients
|
||||
stmt.execute("CREATE TABLE TransactionRecipients (signature Signature, recipient QoraAddress NOT NULL, "
|
||||
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
stmt.execute("SET TABLE TransactionRecipients NEW SPACE");
|
||||
break;
|
||||
// Transaction recipients
|
||||
stmt.execute("CREATE TABLE TransactionRecipients (signature Signature, recipient QoraAddress NOT NULL, "
|
||||
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
stmt.execute("SET TABLE TransactionRecipients NEW SPACE");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Genesis Transactions
|
||||
stmt.execute("CREATE TABLE GenesisTransactions (signature Signature, recipient QoraAddress NOT NULL, "
|
||||
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), "
|
||||
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 3:
|
||||
// Genesis Transactions
|
||||
stmt.execute("CREATE TABLE GenesisTransactions (signature Signature, recipient QoraAddress NOT NULL, "
|
||||
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), "
|
||||
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// Payment Transactions
|
||||
stmt.execute("CREATE TABLE PaymentTransactions (signature Signature, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
||||
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), "
|
||||
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 4:
|
||||
// Payment Transactions
|
||||
stmt.execute("CREATE TABLE PaymentTransactions (signature Signature, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
||||
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), "
|
||||
+ "FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Register Name Transactions
|
||||
stmt.execute("CREATE TABLE RegisterNameTransactions (signature Signature, registrant QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "owner QoraAddress NOT NULL, data NameData NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 5:
|
||||
// Register Name Transactions
|
||||
stmt.execute("CREATE TABLE RegisterNameTransactions (signature Signature, registrant QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "owner QoraAddress NOT NULL, data NameData NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 6:
|
||||
// Update Name Transactions
|
||||
stmt.execute("CREATE TABLE UpdateNameTransactions (signature Signature, owner QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "new_owner QoraAddress NOT NULL, new_data NameData NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 6:
|
||||
// Update Name Transactions
|
||||
stmt.execute("CREATE TABLE UpdateNameTransactions (signature Signature, owner QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "new_owner QoraAddress NOT NULL, new_data NameData NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 7:
|
||||
// Sell Name Transactions
|
||||
stmt.execute("CREATE TABLE SellNameTransactions (signature Signature, owner QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 7:
|
||||
// Sell Name Transactions
|
||||
stmt.execute("CREATE TABLE SellNameTransactions (signature Signature, owner QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "amount QoraAmount NOT NULL, PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 8:
|
||||
// Cancel Sell Name Transactions
|
||||
stmt.execute("CREATE TABLE CancelSellNameTransactions (signature Signature, owner QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 8:
|
||||
// Cancel Sell Name Transactions
|
||||
stmt.execute("CREATE TABLE CancelSellNameTransactions (signature Signature, owner QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 9:
|
||||
// Buy Name Transactions
|
||||
stmt.execute("CREATE TABLE BuyNameTransactions (signature Signature, buyer QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "seller QoraAddress NOT NULL, amount QoraAmount NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 9:
|
||||
// Buy Name Transactions
|
||||
stmt.execute("CREATE TABLE BuyNameTransactions (signature Signature, buyer QoraPublicKey NOT NULL, name RegisteredName NOT NULL, "
|
||||
+ "seller QoraAddress NOT NULL, amount QoraAmount NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 10:
|
||||
// Create Poll Transactions
|
||||
stmt.execute("CREATE TABLE CreatePollTransactions (signature Signature, creator QoraPublicKey NOT NULL, poll PollName NOT NULL, "
|
||||
+ "description VARCHAR(4000) NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
// Poll options. NB: option is implicitly NON NULL and UNIQUE due to being part of compound primary key
|
||||
stmt.execute("CREATE TABLE CreatePollTransactionOptions (signature Signature, option PollOption, "
|
||||
+ "PRIMARY KEY (signature, option), FOREIGN KEY (signature) REFERENCES CreatePollTransactions (signature) ON DELETE CASCADE)");
|
||||
// For the future: add flag to polls to allow one or multiple votes per voter
|
||||
break;
|
||||
case 10:
|
||||
// Create Poll Transactions
|
||||
stmt.execute("CREATE TABLE CreatePollTransactions (signature Signature, creator QoraPublicKey NOT NULL, poll PollName NOT NULL, "
|
||||
+ "description VARCHAR(4000) NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
// Poll options. NB: option is implicitly NON NULL and UNIQUE due to being part of compound primary key
|
||||
stmt.execute("CREATE TABLE CreatePollTransactionOptions (signature Signature, option PollOption, "
|
||||
+ "PRIMARY KEY (signature, option), FOREIGN KEY (signature) REFERENCES CreatePollTransactions (signature) ON DELETE CASCADE)");
|
||||
// For the future: add flag to polls to allow one or multiple votes per voter
|
||||
break;
|
||||
|
||||
case 11:
|
||||
// Vote On Poll Transactions
|
||||
stmt.execute("CREATE TABLE VoteOnPollTransactions (signature Signature, voter QoraPublicKey NOT NULL, poll PollName NOT NULL, "
|
||||
+ "option_index INTEGER NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 11:
|
||||
// Vote On Poll Transactions
|
||||
stmt.execute("CREATE TABLE VoteOnPollTransactions (signature Signature, voter QoraPublicKey NOT NULL, poll PollName NOT NULL, "
|
||||
+ "option_index INTEGER NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 12:
|
||||
// Arbitrary/Multi-payment Transaction Payments
|
||||
stmt.execute("CREATE TABLE SharedTransactionPayments (signature Signature, recipient QoraPublicKey NOT NULL, "
|
||||
+ "amount QoraAmount NOT NULL, asset_id AssetID NOT NULL, "
|
||||
+ "PRIMARY KEY (signature, recipient, asset_id), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 12:
|
||||
// Arbitrary/Multi-payment Transaction Payments
|
||||
stmt.execute("CREATE TABLE SharedTransactionPayments (signature Signature, recipient QoraPublicKey NOT NULL, "
|
||||
+ "amount QoraAmount NOT NULL, asset_id AssetID NOT NULL, "
|
||||
+ "PRIMARY KEY (signature, recipient, asset_id), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 13:
|
||||
// Arbitrary Transactions
|
||||
stmt.execute("CREATE TABLE ArbitraryTransactions (signature Signature, creator QoraPublicKey NOT NULL, service TINYINT NOT NULL, "
|
||||
+ "data_hash DataHash NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
// NB: Actual data payload stored elsewhere
|
||||
// For the future: data payload should be encrypted, at the very least with transaction's reference as the seed for the encryption key
|
||||
break;
|
||||
case 13:
|
||||
// Arbitrary Transactions
|
||||
stmt.execute("CREATE TABLE ArbitraryTransactions (signature Signature, creator QoraPublicKey NOT NULL, service TINYINT NOT NULL, "
|
||||
+ "data_hash DataHash NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
// NB: Actual data payload stored elsewhere
|
||||
// For the future: data payload should be encrypted, at the very least with transaction's reference as the seed for the encryption key
|
||||
break;
|
||||
|
||||
case 14:
|
||||
// Issue Asset Transactions
|
||||
stmt.execute(
|
||||
"CREATE TABLE IssueAssetTransactions (signature Signature, issuer QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, asset_name AssetName NOT NULL, "
|
||||
+ "description VARCHAR(4000) NOT NULL, quantity BIGINT NOT NULL, is_divisible BOOLEAN NOT NULL, asset_id AssetID, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
// For the future: maybe convert quantity from BIGINT to QoraAmount, regardless of divisibility
|
||||
break;
|
||||
case 14:
|
||||
// Issue Asset Transactions
|
||||
stmt.execute(
|
||||
"CREATE TABLE IssueAssetTransactions (signature Signature, issuer QoraPublicKey NOT NULL, owner QoraAddress NOT NULL, asset_name AssetName NOT NULL, "
|
||||
+ "description VARCHAR(4000) NOT NULL, quantity BIGINT NOT NULL, is_divisible BOOLEAN NOT NULL, asset_id AssetID, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
// For the future: maybe convert quantity from BIGINT to QoraAmount, regardless of divisibility
|
||||
break;
|
||||
|
||||
case 15:
|
||||
// Transfer Asset Transactions
|
||||
stmt.execute("CREATE TABLE TransferAssetTransactions (signature Signature, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
||||
+ "asset_id AssetID NOT NULL, amount QoraAmount NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 15:
|
||||
// Transfer Asset Transactions
|
||||
stmt.execute("CREATE TABLE TransferAssetTransactions (signature Signature, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
||||
+ "asset_id AssetID NOT NULL, amount QoraAmount NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 16:
|
||||
// Create Asset Order Transactions
|
||||
stmt.execute("CREATE TABLE CreateAssetOrderTransactions (signature Signature, creator QoraPublicKey NOT NULL, "
|
||||
+ "have_asset_id AssetID NOT NULL, amount QoraAmount NOT NULL, want_asset_id AssetID NOT NULL, price QoraAmount NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 16:
|
||||
// Create Asset Order Transactions
|
||||
stmt.execute("CREATE TABLE CreateAssetOrderTransactions (signature Signature, creator QoraPublicKey NOT NULL, "
|
||||
+ "have_asset_id AssetID NOT NULL, amount QoraAmount NOT NULL, want_asset_id AssetID NOT NULL, price QoraAmount NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 17:
|
||||
// Cancel Asset Order Transactions
|
||||
stmt.execute("CREATE TABLE CancelAssetOrderTransactions (signature Signature, creator QoraPublicKey NOT NULL, "
|
||||
+ "asset_order AssetOrderID NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 17:
|
||||
// Cancel Asset Order Transactions
|
||||
stmt.execute("CREATE TABLE CancelAssetOrderTransactions (signature Signature, creator QoraPublicKey NOT NULL, "
|
||||
+ "asset_order AssetOrderID NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 18:
|
||||
// Multi-payment Transactions
|
||||
stmt.execute("CREATE TABLE MultiPaymentTransactions (signature Signature, sender QoraPublicKey NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 18:
|
||||
// Multi-payment Transactions
|
||||
stmt.execute("CREATE TABLE MultiPaymentTransactions (signature Signature, sender QoraPublicKey NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 19:
|
||||
// Deploy CIYAM AT Transactions
|
||||
stmt.execute("CREATE TABLE DeployATTransactions (signature Signature, creator QoraPublicKey NOT NULL, AT_name ATName NOT NULL, "
|
||||
+ "description VARCHAR(2000) NOT NULL, AT_type ATType NOT NULL, AT_tags VARCHAR(200) NOT NULL, "
|
||||
+ "creation_bytes VARBINARY(100000) NOT NULL, amount QoraAmount NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 19:
|
||||
// Deploy CIYAM AT Transactions
|
||||
stmt.execute("CREATE TABLE DeployATTransactions (signature Signature, creator QoraPublicKey NOT NULL, AT_name ATName NOT NULL, "
|
||||
+ "description VARCHAR(2000) NOT NULL, AT_type ATType NOT NULL, AT_tags VARCHAR(200) NOT NULL, "
|
||||
+ "creation_bytes VARBINARY(100000) NOT NULL, amount QoraAmount NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 20:
|
||||
// Message Transactions
|
||||
stmt.execute(
|
||||
"CREATE TABLE MessageTransactions (signature Signature, version TINYINT NOT NULL, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
||||
+ "is_text BOOLEAN NOT NULL, is_encrypted BOOLEAN NOT NULL, amount QoraAmount NOT NULL, asset_id AssetID NOT NULL, data VARBINARY(4000) NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
case 20:
|
||||
// Message Transactions
|
||||
stmt.execute(
|
||||
"CREATE TABLE MessageTransactions (signature Signature, version TINYINT NOT NULL, sender QoraPublicKey NOT NULL, recipient QoraAddress NOT NULL, "
|
||||
+ "is_text BOOLEAN NOT NULL, is_encrypted BOOLEAN NOT NULL, amount QoraAmount NOT NULL, asset_id AssetID NOT NULL, data VARBINARY(4000) NOT NULL, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
case 21:
|
||||
// Assets (including QORA coin itself)
|
||||
stmt.execute(
|
||||
"CREATE TABLE Assets (asset_id AssetID IDENTITY, owner QoraPublicKey NOT NULL, asset_name AssetName NOT NULL, description VARCHAR(4000) NOT NULL, "
|
||||
+ "quantity BIGINT NOT NULL, is_divisible BOOLEAN NOT NULL, reference Signature NOT NULL)");
|
||||
stmt.execute("CREATE INDEX AssetNameIndex on Assets (asset_name)");
|
||||
break;
|
||||
case 21:
|
||||
// Assets (including QORA coin itself)
|
||||
stmt.execute(
|
||||
"CREATE TABLE Assets (asset_id AssetID IDENTITY, owner QoraPublicKey NOT NULL, asset_name AssetName NOT NULL, description VARCHAR(4000) NOT NULL, "
|
||||
+ "quantity BIGINT NOT NULL, is_divisible BOOLEAN NOT NULL, reference Signature NOT NULL)");
|
||||
stmt.execute("CREATE INDEX AssetNameIndex on Assets (asset_name)");
|
||||
break;
|
||||
|
||||
case 22:
|
||||
// Accounts
|
||||
stmt.execute("CREATE TABLE Accounts (account QoraAddress, reference Signature, PRIMARY KEY (account))");
|
||||
stmt.execute(
|
||||
"CREATE TABLE AccountBalances (account QoraAddress, asset_id AssetID, balance QoraAmount NOT NULL, PRIMARY KEY (account, asset_id))");
|
||||
break;
|
||||
case 22:
|
||||
// Accounts
|
||||
stmt.execute("CREATE TABLE Accounts (account QoraAddress, reference Signature, PRIMARY KEY (account))");
|
||||
stmt.execute(
|
||||
"CREATE TABLE AccountBalances (account QoraAddress, asset_id AssetID, balance QoraAmount NOT NULL, PRIMARY KEY (account, asset_id))");
|
||||
break;
|
||||
|
||||
default:
|
||||
// nothing to do
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
// nothing to do
|
||||
return false;
|
||||
}
|
||||
|
||||
// database was updated
|
||||
|
@ -1,7 +1,6 @@
|
||||
package database;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
@ -13,14 +12,13 @@ import java.util.List;
|
||||
* <p>
|
||||
* Columns, and corresponding values, are bound via close-coupled pairs in a chain thus:
|
||||
* <p>
|
||||
* {@code SaveHelper helper = new SaveHelper(connection, "TableName"); }<br>
|
||||
* {@code SaveHelper helper = new SaveHelper("TableName"); }<br>
|
||||
* {@code helper.bind("column_name", someColumnValue).bind("column2", columnValue2); }<br>
|
||||
* {@code helper.execute(); }<br>
|
||||
*
|
||||
*/
|
||||
public class SaveHelper {
|
||||
|
||||
private Connection connection;
|
||||
private String table;
|
||||
|
||||
private List<String> columns = new ArrayList<String>();
|
||||
@ -29,11 +27,9 @@ public class SaveHelper {
|
||||
/**
|
||||
* Construct a SaveHelper, using SQL Connection and table name.
|
||||
*
|
||||
* @param connection
|
||||
* @param table
|
||||
*/
|
||||
public SaveHelper(Connection connection, String table) {
|
||||
this.connection = connection;
|
||||
public SaveHelper(String table) {
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
@ -52,8 +48,6 @@ public class SaveHelper {
|
||||
|
||||
/**
|
||||
* Build PreparedStatement using bound column-value pairs then execute it.
|
||||
* <p>
|
||||
* Note that after this call, the SaveHelper's Connection is set to null and so this object is not reusable.
|
||||
*
|
||||
* @return the result from {@link PreparedStatement#execute()}
|
||||
* @throws SQLException
|
||||
@ -61,15 +55,11 @@ public class SaveHelper {
|
||||
public boolean execute() throws SQLException {
|
||||
String sql = this.formatInsertWithPlaceholders();
|
||||
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql);
|
||||
PreparedStatement preparedStatement = DB.getConnection().prepareStatement(sql);
|
||||
|
||||
this.bindValues(preparedStatement);
|
||||
|
||||
try {
|
||||
return preparedStatement.execute();
|
||||
} finally {
|
||||
this.connection = null;
|
||||
}
|
||||
return preparedStatement.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,6 @@
|
||||
package qora.account;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@ -41,28 +40,21 @@ public class Account {
|
||||
}
|
||||
|
||||
public BigDecimal getConfirmedBalance(long assetId) throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
return getConfirmedBalance(connection, assetId);
|
||||
}
|
||||
}
|
||||
|
||||
public BigDecimal getConfirmedBalance(Connection connection, long assetId) throws SQLException {
|
||||
ResultSet resultSet = DB.checkedExecute(connection, "SELECT balance FROM AccountBalances WHERE account = ? and asset_id = ?", this.getAddress(),
|
||||
assetId);
|
||||
ResultSet resultSet = DB.checkedExecute("SELECT balance FROM AccountBalances WHERE account = ? and asset_id = ?", this.getAddress(), assetId);
|
||||
if (resultSet == null)
|
||||
return BigDecimal.ZERO.setScale(8);
|
||||
|
||||
return resultSet.getBigDecimal(1);
|
||||
}
|
||||
|
||||
public void setConfirmedBalance(Connection connection, long assetId, BigDecimal balance) throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "AccountBalances");
|
||||
public void setConfirmedBalance(long assetId, BigDecimal balance) throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper("AccountBalances");
|
||||
saveHelper.bind("account", this.getAddress()).bind("asset_id", assetId).bind("balance", balance);
|
||||
saveHelper.execute();
|
||||
}
|
||||
|
||||
public void deleteBalance(Connection connection, long assetId) throws SQLException {
|
||||
DB.checkedExecute(connection, "DELETE FROM AccountBalances WHERE account = ? and asset_id = ?", this.getAddress(), assetId);
|
||||
public void deleteBalance(long assetId) throws SQLException {
|
||||
DB.checkedExecute("DELETE FROM AccountBalances WHERE account = ? and asset_id = ?", this.getAddress(), assetId);
|
||||
}
|
||||
|
||||
// Reference manipulations
|
||||
@ -74,22 +66,7 @@ public class Account {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public byte[] getLastReference() throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
return getLastReference(connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch last reference for account using supplied DB connection.
|
||||
* <p>
|
||||
* Typically for use within an ongoing SQL Transaction.
|
||||
*
|
||||
* @param connection
|
||||
* @return byte[] reference, or null if no reference or account not found.
|
||||
* @throws SQLException
|
||||
*/
|
||||
public byte[] getLastReference(Connection connection) throws SQLException {
|
||||
ResultSet resultSet = DB.checkedExecute(connection, "SELECT reference FROM Accounts WHERE account = ?", this.getAddress());
|
||||
ResultSet resultSet = DB.checkedExecute("SELECT reference FROM Accounts WHERE account = ?", this.getAddress());
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
@ -99,13 +76,12 @@ public class Account {
|
||||
/**
|
||||
* Set last reference for account.
|
||||
*
|
||||
* @param connection
|
||||
* @param reference
|
||||
* -- null allowed
|
||||
* @throws SQLException
|
||||
*/
|
||||
public void setLastReference(Connection connection, byte[] reference) throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "Accounts");
|
||||
public void setLastReference(byte[] reference) throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper("Accounts");
|
||||
saveHelper.bind("account", this.getAddress()).bind("reference", reference);
|
||||
saveHelper.execute();
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package qora.assets;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@ -103,34 +102,26 @@ public class Asset {
|
||||
}
|
||||
}
|
||||
|
||||
public void save(Connection connection) throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "Assets");
|
||||
public void save() throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper("Assets");
|
||||
saveHelper.bind("asset_id", this.assetId).bind("owner", this.owner.getAddress()).bind("asset_name", this.name).bind("description", this.description)
|
||||
.bind("quantity", this.quantity).bind("is_divisible", this.isDivisible).bind("reference", this.reference);
|
||||
saveHelper.execute();
|
||||
|
||||
if (this.assetId == null)
|
||||
this.assetId = DB.callIdentity(connection);
|
||||
this.assetId = DB.callIdentity();
|
||||
}
|
||||
|
||||
public void delete(Connection connection) throws SQLException {
|
||||
DB.checkedExecute(connection, "DELETE FROM Assets WHERE asset_id = ?", this.assetId);
|
||||
public void delete() throws SQLException {
|
||||
DB.checkedExecute("DELETE FROM Assets WHERE asset_id = ?", this.assetId);
|
||||
}
|
||||
|
||||
public static boolean exists(long assetId) throws SQLException {
|
||||
return DB.exists("Assets", "asset_id = ?", assetId);
|
||||
}
|
||||
|
||||
public static boolean exists(Connection connection, long assetId) throws SQLException {
|
||||
return DB.exists(connection, "Assets", "asset_id = ?", assetId);
|
||||
}
|
||||
|
||||
public static boolean exists(String assetName) throws SQLException {
|
||||
return DB.exists("Assets", "asset_name = ?", assetName);
|
||||
}
|
||||
|
||||
public static boolean exists(Connection connection, String assetName) throws SQLException {
|
||||
return DB.exists(connection, "Assets", "asset_name = ?", assetName);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Savepoint;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -395,20 +396,18 @@ public class Block {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static Block fromHeight(int height) throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT " + DB_COLUMNS + " FROM Blocks WHERE height = ?");
|
||||
preparedStatement.setInt(1, height);
|
||||
PreparedStatement preparedStatement = DB.getConnection().prepareStatement("SELECT " + DB_COLUMNS + " FROM Blocks WHERE height = ?");
|
||||
preparedStatement.setInt(1, height);
|
||||
|
||||
try {
|
||||
return new Block(DB.checkedExecute(preparedStatement));
|
||||
} catch (NoDataFoundException e) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new Block(DB.checkedExecute(preparedStatement));
|
||||
} catch (NoDataFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void save(Connection connection) throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "Blocks");
|
||||
protected void save() throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper("Blocks");
|
||||
|
||||
saveHelper.bind("signature", this.getSignature()).bind("version", this.version).bind("reference", this.reference)
|
||||
.bind("transaction_count", this.transactionCount).bind("total_fees", this.totalFees).bind("transactions_signature", this.transactionsSignature)
|
||||
@ -742,17 +741,16 @@ public class Block {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Block is valid using passed connection.
|
||||
* Returns whether Block is valid.
|
||||
* <p>
|
||||
* Performs various tests like checking for parent block, correct block timestamp, version, generating balance, etc.
|
||||
* <p>
|
||||
* Checks block's transactions using an HSQLDB "SAVEPOINT" and hence needs to be called within an ongoing SQL Transaction.
|
||||
*
|
||||
* @param connection
|
||||
* @return true if block is valid, false otherwise.
|
||||
* @throws SQLException
|
||||
*/
|
||||
public boolean isValid(Connection connection) throws SQLException {
|
||||
public boolean isValid() throws SQLException {
|
||||
// TODO
|
||||
|
||||
// Check parent blocks exists
|
||||
@ -796,7 +794,7 @@ public class Block {
|
||||
}
|
||||
|
||||
// Check transactions
|
||||
DB.createSavepoint(connection, "BLOCK_TRANSACTIONS");
|
||||
Savepoint savepoint = DB.createSavepoint("BLOCK_TRANSACTIONS");
|
||||
try {
|
||||
for (Transaction transaction : this.getTransactions()) {
|
||||
// GenesisTransactions are not allowed (GenesisBlock overrides isValid() to allow them)
|
||||
@ -809,12 +807,12 @@ public class Block {
|
||||
|
||||
// Check transaction is even valid
|
||||
// NOTE: in Gen1 there was an extra block height passed to DeployATTransaction.isValid
|
||||
if (transaction.isValid(connection) != Transaction.ValidationResult.OK)
|
||||
if (transaction.isValid() != Transaction.ValidationResult.OK)
|
||||
return false;
|
||||
|
||||
// Process transaction to make sure other transactions validate properly
|
||||
try {
|
||||
transaction.process(connection);
|
||||
transaction.process();
|
||||
} catch (Exception e) {
|
||||
// LOGGER.error("Exception during transaction processing, tx " + Base58.encode(transaction.getSignature()), e);
|
||||
return false;
|
||||
@ -823,7 +821,7 @@ public class Block {
|
||||
} finally {
|
||||
// Revert back to savepoint
|
||||
try {
|
||||
DB.rollbackToSavepoint(connection, "BLOCK_TRANSACTIONS");
|
||||
DB.rollbackToSavepoint(savepoint);
|
||||
} catch (SQLException e) {
|
||||
/*
|
||||
* Rollback failure most likely due to prior SQLException, so catch rollback's SQLException and discard. A "return false" in try-block will
|
||||
@ -836,16 +834,16 @@ public class Block {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void process(Connection connection) throws SQLException {
|
||||
public void process() throws SQLException {
|
||||
// Process transactions (we'll link them to this block after saving the block itself)
|
||||
List<Transaction> transactions = this.getTransactions();
|
||||
for (Transaction transaction : transactions)
|
||||
transaction.process(connection);
|
||||
transaction.process();
|
||||
|
||||
// If fees are non-zero then add fees to generator's balance
|
||||
BigDecimal blockFee = this.getTotalFees();
|
||||
if (blockFee.compareTo(BigDecimal.ZERO) == 1)
|
||||
this.generator.setConfirmedBalance(connection, Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).add(blockFee));
|
||||
this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).add(blockFee));
|
||||
|
||||
// Link block into blockchain by fetching signature of highest block and setting that as our reference
|
||||
int blockchainHeight = BlockChain.getHeight();
|
||||
@ -854,7 +852,7 @@ public class Block {
|
||||
this.reference = latestBlock.getSignature();
|
||||
|
||||
this.height = blockchainHeight + 1;
|
||||
this.save(connection);
|
||||
this.save();
|
||||
|
||||
// Link transactions to this block, thus removing them from unconfirmed transactions list.
|
||||
for (int sequence = 0; sequence < transactions.size(); ++sequence) {
|
||||
@ -862,7 +860,7 @@ public class Block {
|
||||
|
||||
// Link transaction to this block
|
||||
BlockTransaction blockTransaction = new BlockTransaction(this.getSignature(), sequence, transaction.getSignature());
|
||||
blockTransaction.save(connection);
|
||||
blockTransaction.save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package qora.block;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@ -58,17 +57,15 @@ public class BlockChain {
|
||||
// (Re)build database
|
||||
DB.rebuild();
|
||||
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
// Add Genesis Block
|
||||
GenesisBlock genesisBlock = GenesisBlock.getInstance();
|
||||
genesisBlock.process(connection);
|
||||
// Add Genesis Block
|
||||
GenesisBlock genesisBlock = GenesisBlock.getInstance();
|
||||
genesisBlock.process();
|
||||
|
||||
// Add QORA asset.
|
||||
// NOTE: Asset's transaction reference is Genesis Block's generator signature which doesn't exist as a transaction!
|
||||
Asset qoraAsset = new Asset(Asset.QORA, genesisBlock.getGenerator().getAddress(), "Qora", "This is the simulated Qora asset.", 10_000_000_000L,
|
||||
true, genesisBlock.getGeneratorSignature());
|
||||
qoraAsset.save(connection);
|
||||
}
|
||||
// Add QORA asset.
|
||||
// NOTE: Asset's transaction reference is Genesis Block's generator signature which doesn't exist as a transaction!
|
||||
Asset qoraAsset = new Asset(Asset.QORA, genesisBlock.getGenerator().getAddress(), "Qora", "This is the simulated Qora asset.", 10_000_000_000L, true,
|
||||
genesisBlock.getGeneratorSignature());
|
||||
qoraAsset.save();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,13 +90,11 @@ public class BlockChain {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int getHeight() throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
ResultSet rs = DB.checkedExecute(connection.prepareStatement("SELECT MAX(height) FROM Blocks"));
|
||||
if (rs == null)
|
||||
return 0;
|
||||
ResultSet rs = DB.checkedExecute("SELECT MAX(height) FROM Blocks");
|
||||
if (rs == null)
|
||||
return 0;
|
||||
|
||||
return rs.getInt(1);
|
||||
}
|
||||
return rs.getInt(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,5 @@
|
||||
package qora.block;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@ -40,15 +39,13 @@ public class BlockFactory {
|
||||
if (height == 1)
|
||||
return GenesisBlock.getInstance();
|
||||
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT signature FROM Blocks WHERE height = ?");
|
||||
preparedStatement.setInt(1, height);
|
||||
PreparedStatement preparedStatement = DB.getConnection().prepareStatement("SELECT signature FROM Blocks WHERE height = ?");
|
||||
preparedStatement.setInt(1, height);
|
||||
|
||||
try {
|
||||
return new Block(DB.checkedExecute(preparedStatement));
|
||||
} catch (NoDataFoundException e) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new Block(DB.checkedExecute(preparedStatement));
|
||||
} catch (NoDataFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package qora.block;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@ -44,16 +43,14 @@ public class BlockTransaction {
|
||||
// Load/Save
|
||||
|
||||
protected BlockTransaction(byte[] blockSignature, int sequence) throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
ResultSet rs = DB.checkedExecute("SELECT transaction_signature FROM BlockTransactions WHERE block_signature = ? and sequence = ?", blockSignature,
|
||||
sequence);
|
||||
if (rs == null)
|
||||
throw new NoDataFoundException();
|
||||
ResultSet rs = DB.checkedExecute("SELECT transaction_signature FROM BlockTransactions WHERE block_signature = ? and sequence = ?", blockSignature,
|
||||
sequence);
|
||||
if (rs == null)
|
||||
throw new NoDataFoundException();
|
||||
|
||||
this.blockSignature = blockSignature;
|
||||
this.sequence = sequence;
|
||||
this.transactionSignature = DB.getResultSetBytes(rs.getBinaryStream(1), Transaction.SIGNATURE_LENGTH);
|
||||
}
|
||||
this.blockSignature = blockSignature;
|
||||
this.sequence = sequence;
|
||||
this.transactionSignature = DB.getResultSetBytes(rs.getBinaryStream(1), Transaction.SIGNATURE_LENGTH);
|
||||
}
|
||||
|
||||
protected BlockTransaction(byte[] transactionSignature) throws SQLException {
|
||||
@ -97,8 +94,8 @@ public class BlockTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
protected void save(Connection connection) throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "BlockTransactions");
|
||||
protected void save() throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper("BlockTransactions");
|
||||
saveHelper.bind("block_signature", this.blockSignature).bind("sequence", this.sequence).bind("transaction_signature", this.transactionSignature);
|
||||
saveHelper.execute();
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package qora.block;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
@ -329,14 +328,14 @@ public class GenesisBlock extends Block {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(Connection connection) throws SQLException {
|
||||
public boolean isValid() throws SQLException {
|
||||
// Check there is no other block in DB
|
||||
if (BlockChain.getHeight() != 0)
|
||||
return false;
|
||||
|
||||
// Validate transactions
|
||||
for (Transaction transaction : this.getTransactions())
|
||||
if (transaction.isValid(connection) != Transaction.ValidationResult.OK)
|
||||
if (transaction.isValid() != Transaction.ValidationResult.OK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -5,7 +5,6 @@ import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@ -98,10 +97,10 @@ public class CreateOrderTransaction extends Transaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Connection connection) throws SQLException {
|
||||
super.save(connection);
|
||||
public void save() throws SQLException {
|
||||
super.save();
|
||||
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "CreateAssetOrderTransactions");
|
||||
SaveHelper saveHelper = new SaveHelper("CreateAssetOrderTransactions");
|
||||
saveHelper.bind("signature", this.signature).bind("creator", this.creator.getPublicKey()).bind("have_asset_id", this.order.getHaveAssetId())
|
||||
.bind("amount", this.order.getAmount()).bind("want_asset_id", this.order.getWantAssetId()).bind("price", this.order.getPrice());
|
||||
saveHelper.execute();
|
||||
@ -144,20 +143,20 @@ public class CreateOrderTransaction extends Transaction {
|
||||
|
||||
// Processing
|
||||
|
||||
public ValidationResult isValid(Connection connection) throws SQLException {
|
||||
public ValidationResult isValid() throws SQLException {
|
||||
// TODO
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
public void process(Connection connection) throws SQLException {
|
||||
this.save(connection);
|
||||
public void process() throws SQLException {
|
||||
this.save();
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
public void orphan(Connection connection) throws SQLException {
|
||||
this.delete(connection);
|
||||
public void orphan() throws SQLException {
|
||||
this.delete();
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
@ -103,10 +102,10 @@ public class GenesisTransaction extends Transaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Connection connection) throws SQLException {
|
||||
super.save(connection);
|
||||
public void save() throws SQLException {
|
||||
super.save();
|
||||
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "GenesisTransactions");
|
||||
SaveHelper saveHelper = new SaveHelper("GenesisTransactions");
|
||||
saveHelper.bind("signature", this.signature).bind("recipient", this.recipient.getAddress()).bind("amount", this.amount);
|
||||
saveHelper.execute();
|
||||
}
|
||||
@ -194,7 +193,7 @@ public class GenesisTransaction extends Transaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid(Connection connection) {
|
||||
public ValidationResult isValid() {
|
||||
// Check amount is zero or positive
|
||||
if (this.amount.compareTo(BigDecimal.ZERO) == -1)
|
||||
return ValidationResult.NEGATIVE_AMOUNT;
|
||||
@ -207,25 +206,25 @@ public class GenesisTransaction extends Transaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(Connection connection) throws SQLException {
|
||||
this.save(connection);
|
||||
public void process() throws SQLException {
|
||||
this.save();
|
||||
|
||||
// Set recipient's balance
|
||||
this.recipient.setConfirmedBalance(connection, Asset.QORA, this.amount);
|
||||
this.recipient.setConfirmedBalance(Asset.QORA, this.amount);
|
||||
|
||||
// Set recipient's reference
|
||||
recipient.setLastReference(connection, this.signature);
|
||||
recipient.setLastReference(this.signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orphan(Connection connection) throws SQLException {
|
||||
this.delete(connection);
|
||||
public void orphan() throws SQLException {
|
||||
this.delete();
|
||||
|
||||
// Reset recipient's balance
|
||||
this.recipient.deleteBalance(connection, Asset.QORA);
|
||||
this.recipient.deleteBalance(Asset.QORA);
|
||||
|
||||
// Set recipient's reference
|
||||
recipient.setLastReference(connection, null);
|
||||
recipient.setLastReference(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
@ -184,10 +183,10 @@ public class IssueAssetTransaction extends Transaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Connection connection) throws SQLException {
|
||||
super.save(connection);
|
||||
public void save() throws SQLException {
|
||||
super.save();
|
||||
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "IssueAssetTransactions");
|
||||
SaveHelper saveHelper = new SaveHelper("IssueAssetTransactions");
|
||||
saveHelper.bind("signature", this.signature).bind("creator", this.creator.getPublicKey()).bind("asset_name", this.assetName)
|
||||
.bind("description", this.description).bind("quantity", this.quantity).bind("is_divisible", this.isDivisible).bind("asset_id", this.assetId);
|
||||
saveHelper.execute();
|
||||
@ -268,7 +267,7 @@ public class IssueAssetTransaction extends Transaction {
|
||||
|
||||
// Processing
|
||||
|
||||
public ValidationResult isValid(Connection connection) throws SQLException {
|
||||
public ValidationResult isValid() throws SQLException {
|
||||
// Lowest cost checks first
|
||||
|
||||
// Are IssueAssetTransactions even allowed at this point?
|
||||
@ -297,55 +296,55 @@ public class IssueAssetTransaction extends Transaction {
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
||||
// Check reference is correct
|
||||
if (!Arrays.equals(this.issuer.getLastReference(connection), this.reference))
|
||||
if (!Arrays.equals(this.issuer.getLastReference(), this.reference))
|
||||
return ValidationResult.INVALID_REFERENCE;
|
||||
|
||||
// Check issuer has enough funds
|
||||
if (this.issuer.getConfirmedBalance(connection, Asset.QORA).compareTo(this.fee) == -1)
|
||||
if (this.issuer.getConfirmedBalance(Asset.QORA).compareTo(this.fee) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
// XXX: Surely we want to check the asset name isn't already taken?
|
||||
if (Asset.exists(connection, this.assetName))
|
||||
if (Asset.exists(this.assetName))
|
||||
return ValidationResult.ASSET_ALREADY_EXISTS;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
public void process(Connection connection) throws SQLException {
|
||||
public void process() throws SQLException {
|
||||
// Issue asset
|
||||
Asset asset = new Asset(owner.getAddress(), this.assetName, this.description, this.quantity, this.isDivisible, this.reference);
|
||||
asset.save(connection);
|
||||
asset.save();
|
||||
|
||||
// Note newly assigned asset ID in our transaction record
|
||||
this.assetId = asset.getAssetId();
|
||||
|
||||
this.save(connection);
|
||||
this.save();
|
||||
|
||||
// Update issuer's balance
|
||||
this.issuer.setConfirmedBalance(connection, Asset.QORA, this.issuer.getConfirmedBalance(connection, Asset.QORA).subtract(this.fee));
|
||||
this.issuer.setConfirmedBalance(Asset.QORA, this.issuer.getConfirmedBalance(Asset.QORA).subtract(this.fee));
|
||||
|
||||
// Update issuer's reference
|
||||
this.issuer.setLastReference(connection, this.signature);
|
||||
|
||||
this.issuer.setLastReference(this.signature);
|
||||
|
||||
// Add asset to owner
|
||||
this.owner.setConfirmedBalance(connection, this.assetId, BigDecimal.valueOf(this.quantity).setScale(8));
|
||||
this.owner.setConfirmedBalance(this.assetId, BigDecimal.valueOf(this.quantity).setScale(8));
|
||||
}
|
||||
|
||||
public void orphan(Connection connection) throws SQLException {
|
||||
public void orphan() throws SQLException {
|
||||
// Remove asset from owner
|
||||
this.owner.deleteBalance(connection, this.assetId);
|
||||
|
||||
this.owner.deleteBalance(this.assetId);
|
||||
|
||||
// Unissue asset
|
||||
Asset asset = Asset.fromAssetId(this.assetId);
|
||||
asset.delete(connection);
|
||||
asset.delete();
|
||||
|
||||
this.delete(connection);
|
||||
this.delete();
|
||||
|
||||
// Update issuer's balance
|
||||
this.issuer.setConfirmedBalance(connection, Asset.QORA, this.issuer.getConfirmedBalance(connection, Asset.QORA).add(this.fee));
|
||||
this.issuer.setConfirmedBalance(Asset.QORA, this.issuer.getConfirmedBalance(Asset.QORA).add(this.fee));
|
||||
|
||||
// Update issuer's reference
|
||||
this.issuer.setLastReference(connection, this.reference);
|
||||
this.issuer.setLastReference(this.reference);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
@ -163,10 +162,10 @@ public class MessageTransaction extends Transaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Connection connection) throws SQLException {
|
||||
super.save(connection);
|
||||
public void save() throws SQLException {
|
||||
super.save();
|
||||
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "MessageTransactions");
|
||||
SaveHelper saveHelper = new SaveHelper("MessageTransactions");
|
||||
saveHelper.bind("signature", this.signature).bind("version", this.version).bind("sender", this.sender.getPublicKey())
|
||||
.bind("recipient", this.recipient.getAddress()).bind("is_text", this.isText).bind("is_encrypted", this.isEncrypted).bind("amount", this.amount)
|
||||
.bind("asset_id", this.assetId).bind("data", this.data);
|
||||
@ -274,7 +273,7 @@ public class MessageTransaction extends Transaction {
|
||||
|
||||
// Processing
|
||||
|
||||
public ValidationResult isValid(Connection connection) throws SQLException {
|
||||
public ValidationResult isValid() throws SQLException {
|
||||
// Lowest cost checks first
|
||||
|
||||
// Are message transactions even allowed at this point?
|
||||
@ -307,70 +306,70 @@ public class MessageTransaction extends Transaction {
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
||||
// Check reference is correct
|
||||
if (!Arrays.equals(this.sender.getLastReference(connection), this.reference))
|
||||
if (!Arrays.equals(this.sender.getLastReference(), this.reference))
|
||||
return ValidationResult.INVALID_REFERENCE;
|
||||
|
||||
// Does asset exist? (This test not present in gen1)
|
||||
if (this.assetId != Asset.QORA && !Asset.exists(connection, this.assetId))
|
||||
if (this.assetId != Asset.QORA && !Asset.exists(this.assetId))
|
||||
return ValidationResult.ASSET_DOES_NOT_EXIST;
|
||||
|
||||
// If asset is QORA then we need to check amount + fee in one go
|
||||
if (this.assetId == Asset.QORA) {
|
||||
// Check sender has enough funds for amount + fee in QORA
|
||||
if (this.sender.getConfirmedBalance(connection, Asset.QORA).compareTo(this.amount.add(this.fee)) == -1)
|
||||
if (this.sender.getConfirmedBalance(Asset.QORA).compareTo(this.amount.add(this.fee)) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
} else {
|
||||
// Check sender has enough funds for amount in whatever asset
|
||||
if (this.sender.getConfirmedBalance(connection, this.assetId).compareTo(this.amount) == -1)
|
||||
if (this.sender.getConfirmedBalance(this.assetId).compareTo(this.amount) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
// Check sender has enough funds for fee in QORA
|
||||
if (this.sender.getConfirmedBalance(connection, Asset.QORA).compareTo(this.fee) == -1)
|
||||
if (this.sender.getConfirmedBalance(Asset.QORA).compareTo(this.fee) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
}
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
public void process(Connection connection) throws SQLException {
|
||||
this.save(connection);
|
||||
public void process() throws SQLException {
|
||||
this.save();
|
||||
|
||||
// Update sender's balance due to amount
|
||||
this.sender.setConfirmedBalance(connection, this.assetId, this.sender.getConfirmedBalance(connection, this.assetId).subtract(this.amount));
|
||||
this.sender.setConfirmedBalance(this.assetId, this.sender.getConfirmedBalance(this.assetId).subtract(this.amount));
|
||||
// Update sender's balance due to fee
|
||||
this.sender.setConfirmedBalance(connection, Asset.QORA, this.sender.getConfirmedBalance(connection, Asset.QORA).subtract(this.fee));
|
||||
this.sender.setConfirmedBalance(Asset.QORA, this.sender.getConfirmedBalance(Asset.QORA).subtract(this.fee));
|
||||
|
||||
// Update recipient's balance
|
||||
this.recipient.setConfirmedBalance(connection, this.assetId, this.recipient.getConfirmedBalance(connection, this.assetId).add(this.amount));
|
||||
this.recipient.setConfirmedBalance(this.assetId, this.recipient.getConfirmedBalance(this.assetId).add(this.amount));
|
||||
|
||||
// Update sender's reference
|
||||
this.sender.setLastReference(connection, this.signature);
|
||||
this.sender.setLastReference(this.signature);
|
||||
|
||||
// For QORA amounts only: if recipient has no reference yet, then this is their starting reference
|
||||
if (this.assetId == Asset.QORA && this.recipient.getLastReference(connection) == null)
|
||||
this.recipient.setLastReference(connection, this.signature);
|
||||
if (this.assetId == Asset.QORA && this.recipient.getLastReference() == null)
|
||||
this.recipient.setLastReference(this.signature);
|
||||
}
|
||||
|
||||
public void orphan(Connection connection) throws SQLException {
|
||||
this.delete(connection);
|
||||
public void orphan() throws SQLException {
|
||||
this.delete();
|
||||
|
||||
// Update sender's balance due to amount
|
||||
this.sender.setConfirmedBalance(connection, this.assetId, this.sender.getConfirmedBalance(connection, this.assetId).add(this.amount));
|
||||
this.sender.setConfirmedBalance(this.assetId, this.sender.getConfirmedBalance(this.assetId).add(this.amount));
|
||||
// Update sender's balance due to fee
|
||||
this.sender.setConfirmedBalance(connection, Asset.QORA, this.sender.getConfirmedBalance(connection, Asset.QORA).add(this.fee));
|
||||
this.sender.setConfirmedBalance(Asset.QORA, this.sender.getConfirmedBalance(Asset.QORA).add(this.fee));
|
||||
|
||||
// Update recipient's balance
|
||||
this.recipient.setConfirmedBalance(connection, this.assetId, this.recipient.getConfirmedBalance(connection, this.assetId).subtract(this.amount));
|
||||
this.recipient.setConfirmedBalance(this.assetId, this.recipient.getConfirmedBalance(this.assetId).subtract(this.amount));
|
||||
|
||||
// Update sender's reference
|
||||
this.sender.setLastReference(connection, this.reference);
|
||||
this.sender.setLastReference(this.reference);
|
||||
|
||||
/*
|
||||
* For QORA amounts only: If recipient's last reference is this transaction's signature, then they can't have made any transactions of their own (which
|
||||
* would have changed their last reference) thus this is their first reference so remove it.
|
||||
*/
|
||||
if (this.assetId == Asset.QORA && Arrays.equals(this.recipient.getLastReference(connection), this.signature))
|
||||
this.recipient.setLastReference(connection, null);
|
||||
if (this.assetId == Asset.QORA && Arrays.equals(this.recipient.getLastReference(), this.signature))
|
||||
this.recipient.setLastReference(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
@ -111,10 +110,10 @@ public class PaymentTransaction extends Transaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Connection connection) throws SQLException {
|
||||
super.save(connection);
|
||||
public void save() throws SQLException {
|
||||
super.save();
|
||||
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "PaymentTransactions");
|
||||
SaveHelper saveHelper = new SaveHelper("PaymentTransactions");
|
||||
saveHelper.bind("signature", this.signature).bind("sender", this.sender.getPublicKey()).bind("recipient", this.recipient.getAddress()).bind("amount",
|
||||
this.amount);
|
||||
saveHelper.execute();
|
||||
@ -174,7 +173,7 @@ public class PaymentTransaction extends Transaction {
|
||||
|
||||
// Processing
|
||||
|
||||
public ValidationResult isValid(Connection connection) throws SQLException {
|
||||
public ValidationResult isValid() throws SQLException {
|
||||
// Lowest cost checks first
|
||||
|
||||
// Check recipient is a valid address
|
||||
@ -190,51 +189,51 @@ public class PaymentTransaction extends Transaction {
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
||||
// Check reference is correct
|
||||
if (!Arrays.equals(this.sender.getLastReference(connection), this.reference))
|
||||
if (!Arrays.equals(this.sender.getLastReference(), this.reference))
|
||||
return ValidationResult.INVALID_REFERENCE;
|
||||
|
||||
// Check sender has enough funds
|
||||
if (this.sender.getConfirmedBalance(connection, Asset.QORA).compareTo(this.amount.add(this.fee)) == -1)
|
||||
if (this.sender.getConfirmedBalance(Asset.QORA).compareTo(this.amount.add(this.fee)) == -1)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
public void process(Connection connection) throws SQLException {
|
||||
this.save(connection);
|
||||
public void process() throws SQLException {
|
||||
this.save();
|
||||
|
||||
// Update sender's balance
|
||||
this.sender.setConfirmedBalance(connection, Asset.QORA, this.sender.getConfirmedBalance(connection, Asset.QORA).subtract(this.amount).subtract(this.fee));
|
||||
this.sender.setConfirmedBalance(Asset.QORA, this.sender.getConfirmedBalance(Asset.QORA).subtract(this.amount).subtract(this.fee));
|
||||
|
||||
// Update recipient's balance
|
||||
this.recipient.setConfirmedBalance(connection, Asset.QORA, this.recipient.getConfirmedBalance(connection, Asset.QORA).add(this.amount));
|
||||
this.recipient.setConfirmedBalance(Asset.QORA, this.recipient.getConfirmedBalance(Asset.QORA).add(this.amount));
|
||||
|
||||
// Update sender's reference
|
||||
this.sender.setLastReference(connection, this.signature);
|
||||
this.sender.setLastReference(this.signature);
|
||||
|
||||
// If recipient has no reference yet, then this is their starting reference
|
||||
if (this.recipient.getLastReference(connection) == null)
|
||||
this.recipient.setLastReference(connection, this.signature);
|
||||
if (this.recipient.getLastReference() == null)
|
||||
this.recipient.setLastReference(this.signature);
|
||||
}
|
||||
|
||||
public void orphan(Connection connection) throws SQLException {
|
||||
this.delete(connection);
|
||||
public void orphan() throws SQLException {
|
||||
this.delete();
|
||||
|
||||
// Update sender's balance
|
||||
this.sender.setConfirmedBalance(connection, Asset.QORA, this.sender.getConfirmedBalance(connection, Asset.QORA).add(this.amount).add(this.fee));
|
||||
this.sender.setConfirmedBalance(Asset.QORA, this.sender.getConfirmedBalance(Asset.QORA).add(this.amount).add(this.fee));
|
||||
|
||||
// Update recipient's balance
|
||||
this.recipient.setConfirmedBalance(connection, Asset.QORA, this.recipient.getConfirmedBalance(connection, Asset.QORA).subtract(this.amount));
|
||||
this.recipient.setConfirmedBalance(Asset.QORA, this.recipient.getConfirmedBalance(Asset.QORA).subtract(this.amount));
|
||||
|
||||
// Update sender's reference
|
||||
this.sender.setLastReference(connection, this.reference);
|
||||
this.sender.setLastReference(this.reference);
|
||||
|
||||
/*
|
||||
* If recipient's last reference is this transaction's signature, then they can't have made any transactions of their own (which would have changed
|
||||
* their last reference) thus this is their first reference so remove it.
|
||||
*/
|
||||
if (Arrays.equals(this.recipient.getLastReference(connection), this.signature))
|
||||
this.recipient.setLastReference(connection, null);
|
||||
if (Arrays.equals(this.recipient.getLastReference(), this.signature))
|
||||
this.recipient.setLastReference(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package qora.transaction;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
@ -247,18 +246,18 @@ public abstract class Transaction {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
protected void save(Connection connection) throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper(connection, "Transactions");
|
||||
protected void save() throws SQLException {
|
||||
SaveHelper saveHelper = new SaveHelper("Transactions");
|
||||
saveHelper.bind("signature", this.signature).bind("reference", this.reference).bind("type", this.type.value)
|
||||
.bind("creator", this.creator.getPublicKey()).bind("creation", new Timestamp(this.timestamp)).bind("fee", this.fee)
|
||||
.bind("milestone_block", null);
|
||||
saveHelper.execute();
|
||||
}
|
||||
|
||||
protected void delete(Connection connection) throws SQLException {
|
||||
protected void delete() throws SQLException {
|
||||
// NOTE: The corresponding row in sub-table is deleted automatically by the database thanks to "ON DELETE CASCADE" in the sub-table's FOREIGN KEY
|
||||
// definition.
|
||||
DB.checkedExecute(connection, "DELETE FROM Transactions WHERE signature = ?", this.signature);
|
||||
DB.checkedExecute("DELETE FROM Transactions WHERE signature = ?", this.signature);
|
||||
}
|
||||
|
||||
// Navigation
|
||||
@ -403,40 +402,37 @@ public abstract class Transaction {
|
||||
/**
|
||||
* Returns whether transaction can be added to the blockchain.
|
||||
* <p>
|
||||
* Checks if transaction can have {@link Transaction#process(Connection)} called.
|
||||
* Checks if transaction can have {@link Transaction#process()} called.
|
||||
* <p>
|
||||
* Expected to be called within an ongoing SQL Transaction, typically by {@link Block#process(Connection)}, hence the need for the Connection parameter.
|
||||
* Expected to be called within an ongoing SQL Transaction, typically by {@link Block#process()}.
|
||||
* <p>
|
||||
* Transactions that have already been processed will return false.
|
||||
*
|
||||
* @param connection
|
||||
* @return true if transaction can be processed, false otherwise
|
||||
* @throws SQLException
|
||||
*/
|
||||
public abstract ValidationResult isValid(Connection connection) throws SQLException;
|
||||
public abstract ValidationResult isValid() throws SQLException;
|
||||
|
||||
/**
|
||||
* Actually process a transaction, updating the blockchain.
|
||||
* <p>
|
||||
* Processes transaction, updating balances, references, assets, etc. as appropriate.
|
||||
* <p>
|
||||
* Expected to be called within an ongoing SQL Transaction, typically by {@link Block#process(Connection)}, hence the need for the Connection parameter.
|
||||
* Expected to be called within an ongoing SQL Transaction, typically by {@link Block#process()}.
|
||||
*
|
||||
* @param connection
|
||||
* @throws SQLException
|
||||
*/
|
||||
public abstract void process(Connection connection) throws SQLException;
|
||||
public abstract void process() throws SQLException;
|
||||
|
||||
/**
|
||||
* Undo transaction, updating the blockchain.
|
||||
* <p>
|
||||
* Undoes transaction, updating balances, references, assets, etc. as appropriate.
|
||||
* <p>
|
||||
* Expected to be called within an ongoing SQL Transaction, typically by {@link Block#process(Connection)}, hence the need for the Connection parameter.
|
||||
* Expected to be called within an ongoing SQL Transaction, typically by {@link Block#process()}.
|
||||
*
|
||||
* @param connection
|
||||
* @throws SQLException
|
||||
*/
|
||||
public abstract void orphan(Connection connection) throws SQLException;
|
||||
public abstract void orphan() throws SQLException;
|
||||
|
||||
}
|
||||
|
@ -3,13 +3,11 @@ package test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import database.DB;
|
||||
import qora.block.Block;
|
||||
import qora.block.GenesisBlock;
|
||||
import qora.transaction.Transaction;
|
||||
@ -19,79 +17,73 @@ public class blocks extends common {
|
||||
|
||||
@Test
|
||||
public void testGenesisBlockTransactions() throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
GenesisBlock block = GenesisBlock.getInstance();
|
||||
assertNotNull(block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
// only true if blockchain is empty
|
||||
// assertTrue(block.isValid(connection));
|
||||
GenesisBlock block = GenesisBlock.getInstance();
|
||||
assertNotNull(block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
// only true if blockchain is empty
|
||||
// assertTrue(block.isValid(connection));
|
||||
|
||||
List<Transaction> transactions = block.getTransactions();
|
||||
assertNotNull(transactions);
|
||||
List<Transaction> transactions = block.getTransactions();
|
||||
assertNotNull(transactions);
|
||||
|
||||
for (Transaction transaction : transactions) {
|
||||
assertNotNull(transaction);
|
||||
assertEquals(Transaction.TransactionType.GENESIS, transaction.getType());
|
||||
assertTrue(transaction.getFee().compareTo(BigDecimal.ZERO) == 0);
|
||||
assertNull(transaction.getReference());
|
||||
assertTrue(transaction.isSignatureValid());
|
||||
assertEquals(Transaction.ValidationResult.OK, transaction.isValid(connection));
|
||||
}
|
||||
|
||||
// Attempt to load first transaction directly from database
|
||||
Transaction transaction = TransactionFactory.fromSignature(transactions.get(0).getSignature());
|
||||
for (Transaction transaction : transactions) {
|
||||
assertNotNull(transaction);
|
||||
assertEquals(Transaction.TransactionType.GENESIS, transaction.getType());
|
||||
assertTrue(transaction.getFee().compareTo(BigDecimal.ZERO) == 0);
|
||||
assertNull(transaction.getReference());
|
||||
assertTrue(transaction.isSignatureValid());
|
||||
assertEquals(Transaction.ValidationResult.OK, transaction.isValid(connection));
|
||||
assertEquals(Transaction.ValidationResult.OK, transaction.isValid());
|
||||
}
|
||||
|
||||
// Attempt to load first transaction directly from database
|
||||
Transaction transaction = TransactionFactory.fromSignature(transactions.get(0).getSignature());
|
||||
assertNotNull(transaction);
|
||||
assertEquals(Transaction.TransactionType.GENESIS, transaction.getType());
|
||||
assertTrue(transaction.getFee().compareTo(BigDecimal.ZERO) == 0);
|
||||
assertNull(transaction.getReference());
|
||||
assertTrue(transaction.isSignatureValid());
|
||||
assertEquals(Transaction.ValidationResult.OK, transaction.isValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockPaymentTransactions() throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
// Block 949 has lots of varied transactions
|
||||
// Blocks 390 & 754 have only payment transactions
|
||||
Block block = Block.fromHeight(754);
|
||||
assertNotNull("Block 754 is required for this test", block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
// Block 949 has lots of varied transactions
|
||||
// Blocks 390 & 754 have only payment transactions
|
||||
Block block = Block.fromHeight(754);
|
||||
assertNotNull("Block 754 is required for this test", block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
|
||||
List<Transaction> transactions = block.getTransactions();
|
||||
assertNotNull(transactions);
|
||||
List<Transaction> transactions = block.getTransactions();
|
||||
assertNotNull(transactions);
|
||||
|
||||
for (Transaction transaction : transactions) {
|
||||
assertNotNull(transaction);
|
||||
assertEquals(Transaction.TransactionType.PAYMENT, transaction.getType());
|
||||
assertFalse(transaction.getFee().compareTo(BigDecimal.ZERO) == 0);
|
||||
assertNotNull(transaction.getReference());
|
||||
assertTrue(transaction.isSignatureValid());
|
||||
}
|
||||
|
||||
// Attempt to load first transaction directly from database
|
||||
Transaction transaction = TransactionFactory.fromSignature(transactions.get(0).getSignature());
|
||||
for (Transaction transaction : transactions) {
|
||||
assertNotNull(transaction);
|
||||
assertEquals(Transaction.TransactionType.PAYMENT, transaction.getType());
|
||||
assertFalse(transaction.getFee().compareTo(BigDecimal.ZERO) == 0);
|
||||
assertNotNull(transaction.getReference());
|
||||
assertTrue(transaction.isSignatureValid());
|
||||
}
|
||||
|
||||
// Attempt to load first transaction directly from database
|
||||
Transaction transaction = TransactionFactory.fromSignature(transactions.get(0).getSignature());
|
||||
assertNotNull(transaction);
|
||||
assertEquals(Transaction.TransactionType.PAYMENT, transaction.getType());
|
||||
assertFalse(transaction.getFee().compareTo(BigDecimal.ZERO) == 0);
|
||||
assertNotNull(transaction.getReference());
|
||||
assertTrue(transaction.isSignatureValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockSerialization() throws SQLException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
// Block 949 has lots of varied transactions
|
||||
// Blocks 390 & 754 have only payment transactions
|
||||
Block block = Block.fromHeight(754);
|
||||
assertNotNull("Block 754 is required for this test", block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
// Block 949 has lots of varied transactions
|
||||
// Blocks 390 & 754 have only payment transactions
|
||||
Block block = Block.fromHeight(754);
|
||||
assertNotNull("Block 754 is required for this test", block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
|
||||
byte[] bytes = block.toBytes();
|
||||
byte[] bytes = block.toBytes();
|
||||
|
||||
assertEquals(block.getDataLength(), bytes.length);
|
||||
}
|
||||
assertEquals(block.getDataLength(), bytes.length);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public class common {
|
||||
|
||||
@AfterClass
|
||||
public static void closeDatabase() throws SQLException {
|
||||
DB.close();
|
||||
DB.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,37 +13,29 @@ public class connections extends common {
|
||||
|
||||
@Test
|
||||
public void testConnection() {
|
||||
try {
|
||||
Connection c = DB.getConnection();
|
||||
c.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
fail();
|
||||
}
|
||||
Connection connection = DB.getConnection();
|
||||
assertNotNull(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimultaneousConnections() {
|
||||
// First connection is the thread-local one
|
||||
Connection connection = DB.getConnection();
|
||||
assertNotNull(connection);
|
||||
|
||||
int n_connections = 5;
|
||||
Connection[] connections = new Connection[n_connections];
|
||||
|
||||
try {
|
||||
for (int i = 0; i < n_connections; ++i)
|
||||
connections[i] = DB.getConnection();
|
||||
|
||||
// Close in same order as opening
|
||||
for (int i = 0; i < n_connections; ++i)
|
||||
connections[i].close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
fail();
|
||||
for (int i = 0; i < n_connections; ++i) {
|
||||
connections[i] = DB.getConnection();
|
||||
assertEquals(connection, connections[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectionAfterShutdown() {
|
||||
try {
|
||||
DB.close();
|
||||
DB.shutdown();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
fail();
|
||||
@ -51,8 +43,8 @@ public class connections extends common {
|
||||
|
||||
try {
|
||||
DB.open();
|
||||
Connection c = DB.getConnection();
|
||||
c.close();
|
||||
Connection connection = DB.getConnection();
|
||||
assertNotNull(connection);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
fail();
|
||||
|
@ -164,7 +164,7 @@ public class migrate extends common {
|
||||
|
||||
JSONArray transactions = (JSONArray) json.get("transactions");
|
||||
|
||||
DB.startTransaction(c);
|
||||
DB.startTransaction();
|
||||
|
||||
// Blocks:
|
||||
// signature, version, reference, transaction_count, total_fees, transactions_signature, height, generation, generating_balance, generator,
|
||||
@ -590,7 +590,7 @@ public class migrate extends common {
|
||||
blockTxPStmt.execute();
|
||||
blockTxPStmt.clearParameters();
|
||||
|
||||
DB.commit(c);
|
||||
DB.commit();
|
||||
}
|
||||
|
||||
// new milestone block every 500 blocks?
|
||||
@ -600,7 +600,6 @@ public class migrate extends common {
|
||||
++height;
|
||||
}
|
||||
|
||||
c.close();
|
||||
System.out.println("Migration finished with new blockchain height " + BlockChain.getHeight());
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,11 @@
|
||||
package test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import database.DB;
|
||||
import qora.account.PublicKeyAccount;
|
||||
import qora.transaction.PaymentTransaction;
|
||||
import utils.Base58;
|
||||
@ -25,9 +23,7 @@ public class save extends common {
|
||||
PaymentTransaction paymentTransaction = new PaymentTransaction(sender, "Qrecipient", BigDecimal.valueOf(12345L), BigDecimal.ONE,
|
||||
Instant.now().getEpochSecond(), reference, signature);
|
||||
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
paymentTransaction.save(connection);
|
||||
}
|
||||
paymentTransaction.save();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,14 +2,12 @@ package test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import database.DB;
|
||||
import qora.block.Block;
|
||||
import qora.block.GenesisBlock;
|
||||
import qora.transaction.GenesisTransaction;
|
||||
@ -50,19 +48,17 @@ public class transactions extends common {
|
||||
|
||||
@Test
|
||||
public void testPaymentSerialization() throws SQLException, ParseException {
|
||||
try (final Connection connection = DB.getConnection()) {
|
||||
// Block 949 has lots of varied transactions
|
||||
// Blocks 390 & 754 have only payment transactions
|
||||
Block block = Block.fromHeight(754);
|
||||
assertNotNull("Block 754 is required for this test", block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
// Block 949 has lots of varied transactions
|
||||
// Blocks 390 & 754 have only payment transactions
|
||||
Block block = Block.fromHeight(754);
|
||||
assertNotNull("Block 754 is required for this test", block);
|
||||
assertTrue(block.isSignatureValid());
|
||||
|
||||
List<Transaction> transactions = block.getTransactions();
|
||||
assertNotNull(transactions);
|
||||
List<Transaction> transactions = block.getTransactions();
|
||||
assertNotNull(transactions);
|
||||
|
||||
for (Transaction transaction : transactions)
|
||||
testGenericSerialization(transaction);
|
||||
}
|
||||
for (Transaction transaction : transactions)
|
||||
testGenericSerialization(transaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
x
Reference in New Issue
Block a user