mirror of
https://github.com/Qortal/qortal.git
synced 2025-02-12 02:05: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.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Savepoint;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.hsqldb.jdbc.JDBCPool;
|
import org.hsqldb.jdbc.JDBCPool;
|
||||||
@ -17,10 +18,21 @@ import com.google.common.primitives.Bytes;
|
|||||||
* Helper methods for common database actions.
|
* Helper methods for common database actions.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class DB {
|
public abstract class DB {
|
||||||
|
|
||||||
private static JDBCPool connectionPool;
|
private static JDBCPool connectionPool;
|
||||||
private static String connectionUrl;
|
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.
|
* 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.
|
* Return thread-local 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.
|
|
||||||
* <p>
|
* <p>
|
||||||
* By default HSQLDB will wait up to 30 seconds for a pooled connection to become free.
|
* By default HSQLDB will wait up to 30 seconds for a pooled connection to become free.
|
||||||
*
|
*
|
||||||
* @return Connection
|
* @return Connection
|
||||||
* @throws SQLException
|
|
||||||
*/
|
*/
|
||||||
public static Connection getConnection() throws SQLException {
|
public static Connection getConnection() {
|
||||||
return connectionPool.getConnection();
|
return local.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void startTransaction(Connection c) throws SQLException {
|
public static void releaseConnection() {
|
||||||
c.prepareStatement("START TRANSACTION").execute();
|
Connection connection = local.get();
|
||||||
|
if (connection != null)
|
||||||
|
try {
|
||||||
|
connection.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void commit(Connection c) throws SQLException {
|
local.remove();
|
||||||
c.prepareStatement("COMMIT").execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void rollback(Connection c) throws SQLException {
|
public static void startTransaction() throws SQLException {
|
||||||
c.prepareStatement("ROLLBACK").execute();
|
Connection connection = DB.getConnection();
|
||||||
|
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
|
||||||
|
connection.setAutoCommit(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void createSavepoint(Connection c, String savepointName) throws SQLException {
|
public static void commit() throws SQLException {
|
||||||
c.prepareStatement("SAVEPOINT " + savepointName).execute();
|
Connection connection = DB.getConnection();
|
||||||
|
connection.commit();
|
||||||
|
connection.setAutoCommit(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void rollbackToSavepoint(Connection c, String savepointName) throws SQLException {
|
public static void rollback() throws SQLException {
|
||||||
c.prepareStatement("ROLLBACK TO SAVEPOINT " + savepointName).execute();
|
Connection connection = DB.getConnection();
|
||||||
|
connection.rollback();
|
||||||
|
connection.setAutoCommit(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public static void close() throws SQLException {
|
public static void shutdown() throws SQLException {
|
||||||
getConnection().createStatement().execute("SHUTDOWN");
|
DB.getConnection().createStatement().execute("SHUTDOWN");
|
||||||
|
DB.releaseConnection();
|
||||||
connectionPool.close(0);
|
connectionPool.close(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shutdown and delete database, then rebuild it.
|
* Shutdown and delete database, then rebuild it.
|
||||||
* <p>
|
* <p>
|
||||||
* See {@link DB#close()} for warnings about connections.
|
* See {@link DB#shutdown()} for warnings about connections.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that this only rebuilds the database schema, not the data itself.
|
* 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 {
|
public static void rebuild() throws SQLException {
|
||||||
// Shutdown database and close any access
|
// Shutdown database and close any access
|
||||||
DB.close();
|
DB.shutdown();
|
||||||
|
|
||||||
// Wipe files (if any)
|
// Wipe files (if any)
|
||||||
// TODO
|
// TODO
|
||||||
@ -188,26 +214,8 @@ public class DB {
|
|||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public static ResultSet checkedExecute(String sql, Object... objects) throws SQLException {
|
public static ResultSet checkedExecute(String sql, Object... objects) throws SQLException {
|
||||||
try (final Connection connection = DB.getConnection()) {
|
PreparedStatement preparedStatement = DB.getConnection().prepareStatement(sql);
|
||||||
return checkedExecute(connection, sql, objects);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
for (int i = 0; i < objects.length; ++i)
|
||||||
// Special treatment for BigDecimals so that they retain their "scale",
|
// Special treatment for BigDecimals so that they retain their "scale",
|
||||||
// which would otherwise be assumed as 0.
|
// which would otherwise be assumed as 0.
|
||||||
@ -216,7 +224,7 @@ public class DB {
|
|||||||
else
|
else
|
||||||
preparedStatement.setObject(i + 1, objects[i]);
|
preparedStatement.setObject(i + 1, objects[i]);
|
||||||
|
|
||||||
return checkedExecute(preparedStatement);
|
return DB.checkedExecute(preparedStatement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -249,12 +257,11 @@ public class DB {
|
|||||||
* <p>
|
* <p>
|
||||||
* Typically used after INSERTing NULL as the IDENTIY column's value to fetch what value was actually stored by HSQLDB.
|
* Typically used after INSERTing NULL as the IDENTIY column's value to fetch what value was actually stored by HSQLDB.
|
||||||
*
|
*
|
||||||
* @param connection
|
|
||||||
* @return Long
|
* @return Long
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public static Long callIdentity(Connection connection) throws SQLException {
|
public static Long callIdentity() throws SQLException {
|
||||||
PreparedStatement preparedStatement = connection.prepareStatement("CALL IDENTITY()");
|
PreparedStatement preparedStatement = DB.getConnection().prepareStatement("CALL IDENTITY()");
|
||||||
ResultSet resultSet = DB.checkedExecute(preparedStatement);
|
ResultSet resultSet = DB.checkedExecute(preparedStatement);
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
@ -280,34 +287,7 @@ public class DB {
|
|||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public static boolean exists(String tableName, String whereClause, Object... objects) throws SQLException {
|
public static boolean exists(String tableName, String whereClause, Object... objects) throws SQLException {
|
||||||
try (final Connection connection = DB.getConnection()) {
|
PreparedStatement preparedStatement = 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
|
|
||||||
.prepareStatement("SELECT TRUE FROM " + tableName + " WHERE " + whereClause + " ORDER BY NULL LIMIT 1");
|
.prepareStatement("SELECT TRUE FROM " + tableName + " WHERE " + whereClause + " ORDER BY NULL LIMIT 1");
|
||||||
ResultSet resultSet = DB.checkedExecute(preparedStatement);
|
ResultSet resultSet = DB.checkedExecute(preparedStatement);
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package database;
|
package database;
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
@ -23,10 +22,7 @@ public class DatabaseUpdates {
|
|||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
private static void incrementDatabaseVersion() throws SQLException {
|
private static void incrementDatabaseVersion() throws SQLException {
|
||||||
try (final Connection c = DB.getConnection()) {
|
DB.getConnection().createStatement().execute("UPDATE DatabaseInfo SET version = version + 1");
|
||||||
Statement stmt = c.createStatement();
|
|
||||||
stmt.execute("UPDATE DatabaseInfo SET version = version + 1");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,8 +34,8 @@ public class DatabaseUpdates {
|
|||||||
private static int fetchDatabaseVersion() throws SQLException {
|
private static int fetchDatabaseVersion() throws SQLException {
|
||||||
int databaseVersion = 0;
|
int databaseVersion = 0;
|
||||||
|
|
||||||
try (final Connection c = DB.getConnection()) {
|
try {
|
||||||
Statement stmt = c.createStatement();
|
Statement stmt = DB.getConnection().createStatement();
|
||||||
if (stmt.execute("SELECT version FROM DatabaseInfo")) {
|
if (stmt.execute("SELECT version FROM DatabaseInfo")) {
|
||||||
ResultSet rs = stmt.getResultSet();
|
ResultSet rs = stmt.getResultSet();
|
||||||
|
|
||||||
@ -62,17 +58,16 @@ public class DatabaseUpdates {
|
|||||||
private static boolean databaseUpdating() throws SQLException {
|
private static boolean databaseUpdating() throws SQLException {
|
||||||
int databaseVersion = fetchDatabaseVersion();
|
int databaseVersion = fetchDatabaseVersion();
|
||||||
|
|
||||||
try (final Connection c = DB.getConnection()) {
|
Statement stmt = DB.getConnection().createStatement();
|
||||||
Statement stmt = c.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
|
* 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
|
||||||
* harsh on competing unconfirmed transactions.
|
* 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
|
* Only really add "ON DELETE CASCADE" to sub-tables that store type-specific data. For example on sub-types of Transactions like PaymentTransactions. A
|
||||||
* PaymentTransactions. A counterexample would be adding "ON DELETE CASCADE" to Assets using Assets' "reference" as a foreign key referring to
|
* counterexample would be adding "ON DELETE CASCADE" to Assets using Assets' "reference" as a foreign key referring to Transactions' "signature". We
|
||||||
* Transactions' "signature". We want to database to automatically delete complete transaction data (Transactions row and corresponding
|
* want to database to automatically delete complete transaction data (Transactions row and corresponding PaymentTransactions row), but leave deleting
|
||||||
* PaymentTransactions row), but leave deleting less related table rows (Assets) to the Java logic.
|
* less related table rows (Assets) to the Java logic.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
switch (databaseVersion) {
|
switch (databaseVersion) {
|
||||||
@ -290,7 +285,6 @@ public class DatabaseUpdates {
|
|||||||
// nothing to do
|
// nothing to do
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// database was updated
|
// database was updated
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package database;
|
package database;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -13,14 +12,13 @@ import java.util.List;
|
|||||||
* <p>
|
* <p>
|
||||||
* Columns, and corresponding values, are bound via close-coupled pairs in a chain thus:
|
* Columns, and corresponding values, are bound via close-coupled pairs in a chain thus:
|
||||||
* <p>
|
* <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.bind("column_name", someColumnValue).bind("column2", columnValue2); }<br>
|
||||||
* {@code helper.execute(); }<br>
|
* {@code helper.execute(); }<br>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SaveHelper {
|
public class SaveHelper {
|
||||||
|
|
||||||
private Connection connection;
|
|
||||||
private String table;
|
private String table;
|
||||||
|
|
||||||
private List<String> columns = new ArrayList<String>();
|
private List<String> columns = new ArrayList<String>();
|
||||||
@ -29,11 +27,9 @@ public class SaveHelper {
|
|||||||
/**
|
/**
|
||||||
* Construct a SaveHelper, using SQL Connection and table name.
|
* Construct a SaveHelper, using SQL Connection and table name.
|
||||||
*
|
*
|
||||||
* @param connection
|
|
||||||
* @param table
|
* @param table
|
||||||
*/
|
*/
|
||||||
public SaveHelper(Connection connection, String table) {
|
public SaveHelper(String table) {
|
||||||
this.connection = connection;
|
|
||||||
this.table = table;
|
this.table = table;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,8 +48,6 @@ public class SaveHelper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Build PreparedStatement using bound column-value pairs then execute it.
|
* 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()}
|
* @return the result from {@link PreparedStatement#execute()}
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
@ -61,15 +55,11 @@ public class SaveHelper {
|
|||||||
public boolean execute() throws SQLException {
|
public boolean execute() throws SQLException {
|
||||||
String sql = this.formatInsertWithPlaceholders();
|
String sql = this.formatInsertWithPlaceholders();
|
||||||
|
|
||||||
PreparedStatement preparedStatement = connection.prepareStatement(sql);
|
PreparedStatement preparedStatement = DB.getConnection().prepareStatement(sql);
|
||||||
|
|
||||||
this.bindValues(preparedStatement);
|
this.bindValues(preparedStatement);
|
||||||
|
|
||||||
try {
|
|
||||||
return preparedStatement.execute();
|
return preparedStatement.execute();
|
||||||
} finally {
|
|
||||||
this.connection = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package qora.account;
|
package qora.account;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -41,28 +40,21 @@ public class Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getConfirmedBalance(long assetId) throws SQLException {
|
public BigDecimal getConfirmedBalance(long assetId) throws SQLException {
|
||||||
try (final Connection connection = DB.getConnection()) {
|
ResultSet resultSet = DB.checkedExecute("SELECT balance FROM AccountBalances WHERE account = ? and asset_id = ?", this.getAddress(), assetId);
|
||||||
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);
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return BigDecimal.ZERO.setScale(8);
|
return BigDecimal.ZERO.setScale(8);
|
||||||
|
|
||||||
return resultSet.getBigDecimal(1);
|
return resultSet.getBigDecimal(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConfirmedBalance(Connection connection, long assetId, BigDecimal balance) throws SQLException {
|
public void setConfirmedBalance(long assetId, BigDecimal balance) throws SQLException {
|
||||||
SaveHelper saveHelper = new SaveHelper(connection, "AccountBalances");
|
SaveHelper saveHelper = new SaveHelper("AccountBalances");
|
||||||
saveHelper.bind("account", this.getAddress()).bind("asset_id", assetId).bind("balance", balance);
|
saveHelper.bind("account", this.getAddress()).bind("asset_id", assetId).bind("balance", balance);
|
||||||
saveHelper.execute();
|
saveHelper.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteBalance(Connection connection, long assetId) throws SQLException {
|
public void deleteBalance(long assetId) throws SQLException {
|
||||||
DB.checkedExecute(connection, "DELETE FROM AccountBalances WHERE account = ? and asset_id = ?", this.getAddress(), assetId);
|
DB.checkedExecute("DELETE FROM AccountBalances WHERE account = ? and asset_id = ?", this.getAddress(), assetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference manipulations
|
// Reference manipulations
|
||||||
@ -74,22 +66,7 @@ public class Account {
|
|||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public byte[] getLastReference() throws SQLException {
|
public byte[] getLastReference() throws SQLException {
|
||||||
try (final Connection connection = DB.getConnection()) {
|
ResultSet resultSet = DB.checkedExecute("SELECT reference FROM Accounts WHERE account = ?", this.getAddress());
|
||||||
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());
|
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -99,13 +76,12 @@ public class Account {
|
|||||||
/**
|
/**
|
||||||
* Set last reference for account.
|
* Set last reference for account.
|
||||||
*
|
*
|
||||||
* @param connection
|
|
||||||
* @param reference
|
* @param reference
|
||||||
* -- null allowed
|
* -- null allowed
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public void setLastReference(Connection connection, byte[] reference) throws SQLException {
|
public void setLastReference(byte[] reference) throws SQLException {
|
||||||
SaveHelper saveHelper = new SaveHelper(connection, "Accounts");
|
SaveHelper saveHelper = new SaveHelper("Accounts");
|
||||||
saveHelper.bind("account", this.getAddress()).bind("reference", reference);
|
saveHelper.bind("account", this.getAddress()).bind("reference", reference);
|
||||||
saveHelper.execute();
|
saveHelper.execute();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package qora.assets;
|
package qora.assets;
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -103,34 +102,26 @@ public class Asset {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save(Connection connection) throws SQLException {
|
public void save() throws SQLException {
|
||||||
SaveHelper saveHelper = new SaveHelper(connection, "Assets");
|
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)
|
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);
|
.bind("quantity", this.quantity).bind("is_divisible", this.isDivisible).bind("reference", this.reference);
|
||||||
saveHelper.execute();
|
saveHelper.execute();
|
||||||
|
|
||||||
if (this.assetId == null)
|
if (this.assetId == null)
|
||||||
this.assetId = DB.callIdentity(connection);
|
this.assetId = DB.callIdentity();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete(Connection connection) throws SQLException {
|
public void delete() throws SQLException {
|
||||||
DB.checkedExecute(connection, "DELETE FROM Assets WHERE asset_id = ?", this.assetId);
|
DB.checkedExecute("DELETE FROM Assets WHERE asset_id = ?", this.assetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean exists(long assetId) throws SQLException {
|
public static boolean exists(long assetId) throws SQLException {
|
||||||
return DB.exists("Assets", "asset_id = ?", assetId);
|
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 {
|
public static boolean exists(String assetName) throws SQLException {
|
||||||
return DB.exists("Assets", "asset_name = ?", assetName);
|
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.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Savepoint;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -395,8 +396,7 @@ public class Block {
|
|||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public static Block fromHeight(int height) throws SQLException {
|
public static Block fromHeight(int height) throws SQLException {
|
||||||
try (final Connection connection = DB.getConnection()) {
|
PreparedStatement preparedStatement = DB.getConnection().prepareStatement("SELECT " + DB_COLUMNS + " FROM Blocks WHERE height = ?");
|
||||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT " + DB_COLUMNS + " FROM Blocks WHERE height = ?");
|
|
||||||
preparedStatement.setInt(1, height);
|
preparedStatement.setInt(1, height);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -405,10 +405,9 @@ public class Block {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected void save(Connection connection) throws SQLException {
|
protected void save() throws SQLException {
|
||||||
SaveHelper saveHelper = new SaveHelper(connection, "Blocks");
|
SaveHelper saveHelper = new SaveHelper("Blocks");
|
||||||
|
|
||||||
saveHelper.bind("signature", this.getSignature()).bind("version", this.version).bind("reference", this.reference)
|
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)
|
.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>
|
* <p>
|
||||||
* Performs various tests like checking for parent block, correct block timestamp, version, generating balance, etc.
|
* Performs various tests like checking for parent block, correct block timestamp, version, generating balance, etc.
|
||||||
* <p>
|
* <p>
|
||||||
* Checks block's transactions using an HSQLDB "SAVEPOINT" and hence needs to be called within an ongoing SQL Transaction.
|
* 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.
|
* @return true if block is valid, false otherwise.
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public boolean isValid(Connection connection) throws SQLException {
|
public boolean isValid() throws SQLException {
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
// Check parent blocks exists
|
// Check parent blocks exists
|
||||||
@ -796,7 +794,7 @@ public class Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check transactions
|
// Check transactions
|
||||||
DB.createSavepoint(connection, "BLOCK_TRANSACTIONS");
|
Savepoint savepoint = DB.createSavepoint("BLOCK_TRANSACTIONS");
|
||||||
try {
|
try {
|
||||||
for (Transaction transaction : this.getTransactions()) {
|
for (Transaction transaction : this.getTransactions()) {
|
||||||
// GenesisTransactions are not allowed (GenesisBlock overrides isValid() to allow them)
|
// GenesisTransactions are not allowed (GenesisBlock overrides isValid() to allow them)
|
||||||
@ -809,12 +807,12 @@ public class Block {
|
|||||||
|
|
||||||
// Check transaction is even valid
|
// Check transaction is even valid
|
||||||
// NOTE: in Gen1 there was an extra block height passed to DeployATTransaction.isValid
|
// 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;
|
return false;
|
||||||
|
|
||||||
// Process transaction to make sure other transactions validate properly
|
// Process transaction to make sure other transactions validate properly
|
||||||
try {
|
try {
|
||||||
transaction.process(connection);
|
transaction.process();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// LOGGER.error("Exception during transaction processing, tx " + Base58.encode(transaction.getSignature()), e);
|
// LOGGER.error("Exception during transaction processing, tx " + Base58.encode(transaction.getSignature()), e);
|
||||||
return false;
|
return false;
|
||||||
@ -823,7 +821,7 @@ public class Block {
|
|||||||
} finally {
|
} finally {
|
||||||
// Revert back to savepoint
|
// Revert back to savepoint
|
||||||
try {
|
try {
|
||||||
DB.rollbackToSavepoint(connection, "BLOCK_TRANSACTIONS");
|
DB.rollbackToSavepoint(savepoint);
|
||||||
} catch (SQLException e) {
|
} 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
|
* 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;
|
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)
|
// Process transactions (we'll link them to this block after saving the block itself)
|
||||||
List<Transaction> transactions = this.getTransactions();
|
List<Transaction> transactions = this.getTransactions();
|
||||||
for (Transaction transaction : transactions)
|
for (Transaction transaction : transactions)
|
||||||
transaction.process(connection);
|
transaction.process();
|
||||||
|
|
||||||
// If fees are non-zero then add fees to generator's balance
|
// If fees are non-zero then add fees to generator's balance
|
||||||
BigDecimal blockFee = this.getTotalFees();
|
BigDecimal blockFee = this.getTotalFees();
|
||||||
if (blockFee.compareTo(BigDecimal.ZERO) == 1)
|
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
|
// Link block into blockchain by fetching signature of highest block and setting that as our reference
|
||||||
int blockchainHeight = BlockChain.getHeight();
|
int blockchainHeight = BlockChain.getHeight();
|
||||||
@ -854,7 +852,7 @@ public class Block {
|
|||||||
this.reference = latestBlock.getSignature();
|
this.reference = latestBlock.getSignature();
|
||||||
|
|
||||||
this.height = blockchainHeight + 1;
|
this.height = blockchainHeight + 1;
|
||||||
this.save(connection);
|
this.save();
|
||||||
|
|
||||||
// Link transactions to this block, thus removing them from unconfirmed transactions list.
|
// Link transactions to this block, thus removing them from unconfirmed transactions list.
|
||||||
for (int sequence = 0; sequence < transactions.size(); ++sequence) {
|
for (int sequence = 0; sequence < transactions.size(); ++sequence) {
|
||||||
@ -862,7 +860,7 @@ public class Block {
|
|||||||
|
|
||||||
// Link transaction to this block
|
// Link transaction to this block
|
||||||
BlockTransaction blockTransaction = new BlockTransaction(this.getSignature(), sequence, transaction.getSignature());
|
BlockTransaction blockTransaction = new BlockTransaction(this.getSignature(), sequence, transaction.getSignature());
|
||||||
blockTransaction.save(connection);
|
blockTransaction.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package qora.block;
|
package qora.block;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -58,17 +57,15 @@ public class BlockChain {
|
|||||||
// (Re)build database
|
// (Re)build database
|
||||||
DB.rebuild();
|
DB.rebuild();
|
||||||
|
|
||||||
try (final Connection connection = DB.getConnection()) {
|
|
||||||
// Add Genesis Block
|
// Add Genesis Block
|
||||||
GenesisBlock genesisBlock = GenesisBlock.getInstance();
|
GenesisBlock genesisBlock = GenesisBlock.getInstance();
|
||||||
genesisBlock.process(connection);
|
genesisBlock.process();
|
||||||
|
|
||||||
// Add QORA asset.
|
// Add QORA asset.
|
||||||
// NOTE: Asset's transaction reference is Genesis Block's generator signature which doesn't exist as a transaction!
|
// 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,
|
Asset qoraAsset = new Asset(Asset.QORA, genesisBlock.getGenerator().getAddress(), "Qora", "This is the simulated Qora asset.", 10_000_000_000L, true,
|
||||||
true, genesisBlock.getGeneratorSignature());
|
genesisBlock.getGeneratorSignature());
|
||||||
qoraAsset.save(connection);
|
qoraAsset.save();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,14 +90,12 @@ public class BlockChain {
|
|||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public static int getHeight() throws SQLException {
|
public static int getHeight() throws SQLException {
|
||||||
try (final Connection connection = DB.getConnection()) {
|
ResultSet rs = DB.checkedExecute("SELECT MAX(height) FROM Blocks");
|
||||||
ResultSet rs = DB.checkedExecute(connection.prepareStatement("SELECT MAX(height) FROM Blocks"));
|
|
||||||
if (rs == null)
|
if (rs == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return rs.getInt(1);
|
return rs.getInt(1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return Qora balance adjusted to within min/max limits.
|
* Return Qora balance adjusted to within min/max limits.
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package qora.block;
|
package qora.block;
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -40,8 +39,7 @@ public class BlockFactory {
|
|||||||
if (height == 1)
|
if (height == 1)
|
||||||
return GenesisBlock.getInstance();
|
return GenesisBlock.getInstance();
|
||||||
|
|
||||||
try (final Connection connection = DB.getConnection()) {
|
PreparedStatement preparedStatement = DB.getConnection().prepareStatement("SELECT signature FROM Blocks WHERE height = ?");
|
||||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT signature FROM Blocks WHERE height = ?");
|
|
||||||
preparedStatement.setInt(1, height);
|
preparedStatement.setInt(1, height);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -50,7 +48,6 @@ public class BlockFactory {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package qora.block;
|
package qora.block;
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -44,7 +43,6 @@ public class BlockTransaction {
|
|||||||
// Load/Save
|
// Load/Save
|
||||||
|
|
||||||
protected BlockTransaction(byte[] blockSignature, int sequence) throws SQLException {
|
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,
|
ResultSet rs = DB.checkedExecute("SELECT transaction_signature FROM BlockTransactions WHERE block_signature = ? and sequence = ?", blockSignature,
|
||||||
sequence);
|
sequence);
|
||||||
if (rs == null)
|
if (rs == null)
|
||||||
@ -54,7 +52,6 @@ public class BlockTransaction {
|
|||||||
this.sequence = sequence;
|
this.sequence = sequence;
|
||||||
this.transactionSignature = DB.getResultSetBytes(rs.getBinaryStream(1), Transaction.SIGNATURE_LENGTH);
|
this.transactionSignature = DB.getResultSetBytes(rs.getBinaryStream(1), Transaction.SIGNATURE_LENGTH);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected BlockTransaction(byte[] transactionSignature) throws SQLException {
|
protected BlockTransaction(byte[] transactionSignature) throws SQLException {
|
||||||
ResultSet rs = DB.checkedExecute("SELECT block_signature, sequence FROM BlockTransactions WHERE transaction_signature = ?", transactionSignature);
|
ResultSet rs = DB.checkedExecute("SELECT block_signature, sequence FROM BlockTransactions WHERE transaction_signature = ?", transactionSignature);
|
||||||
@ -97,8 +94,8 @@ public class BlockTransaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void save(Connection connection) throws SQLException {
|
protected void save() throws SQLException {
|
||||||
SaveHelper saveHelper = new SaveHelper(connection, "BlockTransactions");
|
SaveHelper saveHelper = new SaveHelper("BlockTransactions");
|
||||||
saveHelper.bind("block_signature", this.blockSignature).bind("sequence", this.sequence).bind("transaction_signature", this.transactionSignature);
|
saveHelper.bind("block_signature", this.blockSignature).bind("sequence", this.sequence).bind("transaction_signature", this.transactionSignature);
|
||||||
saveHelper.execute();
|
saveHelper.execute();
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package qora.block;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -329,14 +328,14 @@ public class GenesisBlock extends Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(Connection connection) throws SQLException {
|
public boolean isValid() throws SQLException {
|
||||||
// Check there is no other block in DB
|
// Check there is no other block in DB
|
||||||
if (BlockChain.getHeight() != 0)
|
if (BlockChain.getHeight() != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Validate transactions
|
// Validate transactions
|
||||||
for (Transaction transaction : this.getTransactions())
|
for (Transaction transaction : this.getTransactions())
|
||||||
if (transaction.isValid(connection) != Transaction.ValidationResult.OK)
|
if (transaction.isValid() != Transaction.ValidationResult.OK)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -5,7 +5,6 @@ import java.io.IOException;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -98,10 +97,10 @@ public class CreateOrderTransaction extends Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(Connection connection) throws SQLException {
|
public void save() throws SQLException {
|
||||||
super.save(connection);
|
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())
|
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());
|
.bind("amount", this.order.getAmount()).bind("want_asset_id", this.order.getWantAssetId()).bind("price", this.order.getPrice());
|
||||||
saveHelper.execute();
|
saveHelper.execute();
|
||||||
@ -144,20 +143,20 @@ public class CreateOrderTransaction extends Transaction {
|
|||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
public ValidationResult isValid(Connection connection) throws SQLException {
|
public ValidationResult isValid() throws SQLException {
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(Connection connection) throws SQLException {
|
public void process() throws SQLException {
|
||||||
this.save(connection);
|
this.save();
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
public void orphan(Connection connection) throws SQLException {
|
public void orphan() throws SQLException {
|
||||||
this.delete(connection);
|
this.delete();
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -103,10 +102,10 @@ public class GenesisTransaction extends Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(Connection connection) throws SQLException {
|
public void save() throws SQLException {
|
||||||
super.save(connection);
|
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.bind("signature", this.signature).bind("recipient", this.recipient.getAddress()).bind("amount", this.amount);
|
||||||
saveHelper.execute();
|
saveHelper.execute();
|
||||||
}
|
}
|
||||||
@ -194,7 +193,7 @@ public class GenesisTransaction extends Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid(Connection connection) {
|
public ValidationResult isValid() {
|
||||||
// Check amount is zero or positive
|
// Check amount is zero or positive
|
||||||
if (this.amount.compareTo(BigDecimal.ZERO) == -1)
|
if (this.amount.compareTo(BigDecimal.ZERO) == -1)
|
||||||
return ValidationResult.NEGATIVE_AMOUNT;
|
return ValidationResult.NEGATIVE_AMOUNT;
|
||||||
@ -207,25 +206,25 @@ public class GenesisTransaction extends Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(Connection connection) throws SQLException {
|
public void process() throws SQLException {
|
||||||
this.save(connection);
|
this.save();
|
||||||
|
|
||||||
// Set recipient's balance
|
// Set recipient's balance
|
||||||
this.recipient.setConfirmedBalance(connection, Asset.QORA, this.amount);
|
this.recipient.setConfirmedBalance(Asset.QORA, this.amount);
|
||||||
|
|
||||||
// Set recipient's reference
|
// Set recipient's reference
|
||||||
recipient.setLastReference(connection, this.signature);
|
recipient.setLastReference(this.signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void orphan(Connection connection) throws SQLException {
|
public void orphan() throws SQLException {
|
||||||
this.delete(connection);
|
this.delete();
|
||||||
|
|
||||||
// Reset recipient's balance
|
// Reset recipient's balance
|
||||||
this.recipient.deleteBalance(connection, Asset.QORA);
|
this.recipient.deleteBalance(Asset.QORA);
|
||||||
|
|
||||||
// Set recipient's reference
|
// 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.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -184,10 +183,10 @@ public class IssueAssetTransaction extends Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(Connection connection) throws SQLException {
|
public void save() throws SQLException {
|
||||||
super.save(connection);
|
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)
|
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);
|
.bind("description", this.description).bind("quantity", this.quantity).bind("is_divisible", this.isDivisible).bind("asset_id", this.assetId);
|
||||||
saveHelper.execute();
|
saveHelper.execute();
|
||||||
@ -268,7 +267,7 @@ public class IssueAssetTransaction extends Transaction {
|
|||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
public ValidationResult isValid(Connection connection) throws SQLException {
|
public ValidationResult isValid() throws SQLException {
|
||||||
// Lowest cost checks first
|
// Lowest cost checks first
|
||||||
|
|
||||||
// Are IssueAssetTransactions even allowed at this point?
|
// Are IssueAssetTransactions even allowed at this point?
|
||||||
@ -297,55 +296,55 @@ public class IssueAssetTransaction extends Transaction {
|
|||||||
return ValidationResult.NEGATIVE_FEE;
|
return ValidationResult.NEGATIVE_FEE;
|
||||||
|
|
||||||
// Check reference is correct
|
// 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;
|
return ValidationResult.INVALID_REFERENCE;
|
||||||
|
|
||||||
// Check issuer has enough funds
|
// 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;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
// XXX: Surely we want to check the asset name isn't already taken?
|
// 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.ASSET_ALREADY_EXISTS;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(Connection connection) throws SQLException {
|
public void process() throws SQLException {
|
||||||
// Issue asset
|
// Issue asset
|
||||||
Asset asset = new Asset(owner.getAddress(), this.assetName, this.description, this.quantity, this.isDivisible, this.reference);
|
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
|
// Note newly assigned asset ID in our transaction record
|
||||||
this.assetId = asset.getAssetId();
|
this.assetId = asset.getAssetId();
|
||||||
|
|
||||||
this.save(connection);
|
this.save();
|
||||||
|
|
||||||
// Update issuer's balance
|
// 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
|
// Update issuer's reference
|
||||||
this.issuer.setLastReference(connection, this.signature);
|
this.issuer.setLastReference(this.signature);
|
||||||
|
|
||||||
// Add asset to owner
|
// 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
|
// Remove asset from owner
|
||||||
this.owner.deleteBalance(connection, this.assetId);
|
this.owner.deleteBalance(this.assetId);
|
||||||
|
|
||||||
// Unissue asset
|
// Unissue asset
|
||||||
Asset asset = Asset.fromAssetId(this.assetId);
|
Asset asset = Asset.fromAssetId(this.assetId);
|
||||||
asset.delete(connection);
|
asset.delete();
|
||||||
|
|
||||||
this.delete(connection);
|
this.delete();
|
||||||
|
|
||||||
// Update issuer's balance
|
// 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
|
// 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.math.BigDecimal;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -163,10 +162,10 @@ public class MessageTransaction extends Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(Connection connection) throws SQLException {
|
public void save() throws SQLException {
|
||||||
super.save(connection);
|
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())
|
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("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);
|
.bind("asset_id", this.assetId).bind("data", this.data);
|
||||||
@ -274,7 +273,7 @@ public class MessageTransaction extends Transaction {
|
|||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
public ValidationResult isValid(Connection connection) throws SQLException {
|
public ValidationResult isValid() throws SQLException {
|
||||||
// Lowest cost checks first
|
// Lowest cost checks first
|
||||||
|
|
||||||
// Are message transactions even allowed at this point?
|
// Are message transactions even allowed at this point?
|
||||||
@ -307,70 +306,70 @@ public class MessageTransaction extends Transaction {
|
|||||||
return ValidationResult.NEGATIVE_FEE;
|
return ValidationResult.NEGATIVE_FEE;
|
||||||
|
|
||||||
// Check reference is correct
|
// 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;
|
return ValidationResult.INVALID_REFERENCE;
|
||||||
|
|
||||||
// Does asset exist? (This test not present in gen1)
|
// 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;
|
return ValidationResult.ASSET_DOES_NOT_EXIST;
|
||||||
|
|
||||||
// If asset is QORA then we need to check amount + fee in one go
|
// If asset is QORA then we need to check amount + fee in one go
|
||||||
if (this.assetId == Asset.QORA) {
|
if (this.assetId == Asset.QORA) {
|
||||||
// Check sender has enough funds for amount + fee in 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;
|
return ValidationResult.NO_BALANCE;
|
||||||
} else {
|
} else {
|
||||||
// Check sender has enough funds for amount in whatever asset
|
// 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;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
// Check sender has enough funds for fee in QORA
|
// 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.NO_BALANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(Connection connection) throws SQLException {
|
public void process() throws SQLException {
|
||||||
this.save(connection);
|
this.save();
|
||||||
|
|
||||||
// Update sender's balance due to amount
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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)
|
if (this.assetId == Asset.QORA && this.recipient.getLastReference() == null)
|
||||||
this.recipient.setLastReference(connection, this.signature);
|
this.recipient.setLastReference(this.signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void orphan(Connection connection) throws SQLException {
|
public void orphan() throws SQLException {
|
||||||
this.delete(connection);
|
this.delete();
|
||||||
|
|
||||||
// Update sender's balance due to amount
|
// 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
|
// 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
|
// 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
|
// 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
|
* 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.
|
* 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))
|
if (this.assetId == Asset.QORA && Arrays.equals(this.recipient.getLastReference(), this.signature))
|
||||||
this.recipient.setLastReference(connection, null);
|
this.recipient.setLastReference(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -111,10 +110,10 @@ public class PaymentTransaction extends Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(Connection connection) throws SQLException {
|
public void save() throws SQLException {
|
||||||
super.save(connection);
|
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",
|
saveHelper.bind("signature", this.signature).bind("sender", this.sender.getPublicKey()).bind("recipient", this.recipient.getAddress()).bind("amount",
|
||||||
this.amount);
|
this.amount);
|
||||||
saveHelper.execute();
|
saveHelper.execute();
|
||||||
@ -174,7 +173,7 @@ public class PaymentTransaction extends Transaction {
|
|||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
public ValidationResult isValid(Connection connection) throws SQLException {
|
public ValidationResult isValid() throws SQLException {
|
||||||
// Lowest cost checks first
|
// Lowest cost checks first
|
||||||
|
|
||||||
// Check recipient is a valid address
|
// Check recipient is a valid address
|
||||||
@ -190,51 +189,51 @@ public class PaymentTransaction extends Transaction {
|
|||||||
return ValidationResult.NEGATIVE_FEE;
|
return ValidationResult.NEGATIVE_FEE;
|
||||||
|
|
||||||
// Check reference is correct
|
// 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;
|
return ValidationResult.INVALID_REFERENCE;
|
||||||
|
|
||||||
// Check sender has enough funds
|
// 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.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(Connection connection) throws SQLException {
|
public void process() throws SQLException {
|
||||||
this.save(connection);
|
this.save();
|
||||||
|
|
||||||
// Update sender's balance
|
// 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
|
// 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
|
// 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 recipient has no reference yet, then this is their starting reference
|
||||||
if (this.recipient.getLastReference(connection) == null)
|
if (this.recipient.getLastReference() == null)
|
||||||
this.recipient.setLastReference(connection, this.signature);
|
this.recipient.setLastReference(this.signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void orphan(Connection connection) throws SQLException {
|
public void orphan() throws SQLException {
|
||||||
this.delete(connection);
|
this.delete();
|
||||||
|
|
||||||
// Update sender's balance
|
// 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
|
// 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
|
// 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
|
* 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.
|
* their last reference) thus this is their first reference so remove it.
|
||||||
*/
|
*/
|
||||||
if (Arrays.equals(this.recipient.getLastReference(connection), this.signature))
|
if (Arrays.equals(this.recipient.getLastReference(), this.signature))
|
||||||
this.recipient.setLastReference(connection, null);
|
this.recipient.setLastReference(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package qora.transaction;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
@ -247,18 +246,18 @@ public abstract class Transaction {
|
|||||||
this.signature = signature;
|
this.signature = signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void save(Connection connection) throws SQLException {
|
protected void save() throws SQLException {
|
||||||
SaveHelper saveHelper = new SaveHelper(connection, "Transactions");
|
SaveHelper saveHelper = new SaveHelper("Transactions");
|
||||||
saveHelper.bind("signature", this.signature).bind("reference", this.reference).bind("type", this.type.value)
|
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("creator", this.creator.getPublicKey()).bind("creation", new Timestamp(this.timestamp)).bind("fee", this.fee)
|
||||||
.bind("milestone_block", null);
|
.bind("milestone_block", null);
|
||||||
saveHelper.execute();
|
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
|
// 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.
|
// definition.
|
||||||
DB.checkedExecute(connection, "DELETE FROM Transactions WHERE signature = ?", this.signature);
|
DB.checkedExecute("DELETE FROM Transactions WHERE signature = ?", this.signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
@ -403,40 +402,37 @@ public abstract class Transaction {
|
|||||||
/**
|
/**
|
||||||
* Returns whether transaction can be added to the blockchain.
|
* Returns whether transaction can be added to the blockchain.
|
||||||
* <p>
|
* <p>
|
||||||
* Checks if transaction can have {@link Transaction#process(Connection)} called.
|
* Checks if transaction can have {@link Transaction#process()} called.
|
||||||
* <p>
|
* <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>
|
* <p>
|
||||||
* Transactions that have already been processed will return false.
|
* Transactions that have already been processed will return false.
|
||||||
*
|
*
|
||||||
* @param connection
|
|
||||||
* @return true if transaction can be processed, false otherwise
|
* @return true if transaction can be processed, false otherwise
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public abstract ValidationResult isValid(Connection connection) throws SQLException;
|
public abstract ValidationResult isValid() throws SQLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actually process a transaction, updating the blockchain.
|
* Actually process a transaction, updating the blockchain.
|
||||||
* <p>
|
* <p>
|
||||||
* Processes transaction, updating balances, references, assets, etc. as appropriate.
|
* Processes transaction, updating balances, references, assets, etc. as appropriate.
|
||||||
* <p>
|
* <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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public abstract void process(Connection connection) throws SQLException;
|
public abstract void process() throws SQLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undo transaction, updating the blockchain.
|
* Undo transaction, updating the blockchain.
|
||||||
* <p>
|
* <p>
|
||||||
* Undoes transaction, updating balances, references, assets, etc. as appropriate.
|
* Undoes transaction, updating balances, references, assets, etc. as appropriate.
|
||||||
* <p>
|
* <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
|
* @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 static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import database.DB;
|
|
||||||
import qora.block.Block;
|
import qora.block.Block;
|
||||||
import qora.block.GenesisBlock;
|
import qora.block.GenesisBlock;
|
||||||
import qora.transaction.Transaction;
|
import qora.transaction.Transaction;
|
||||||
@ -19,7 +17,6 @@ public class blocks extends common {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenesisBlockTransactions() throws SQLException {
|
public void testGenesisBlockTransactions() throws SQLException {
|
||||||
try (final Connection connection = DB.getConnection()) {
|
|
||||||
GenesisBlock block = GenesisBlock.getInstance();
|
GenesisBlock block = GenesisBlock.getInstance();
|
||||||
assertNotNull(block);
|
assertNotNull(block);
|
||||||
assertTrue(block.isSignatureValid());
|
assertTrue(block.isSignatureValid());
|
||||||
@ -35,7 +32,7 @@ public class blocks extends common {
|
|||||||
assertTrue(transaction.getFee().compareTo(BigDecimal.ZERO) == 0);
|
assertTrue(transaction.getFee().compareTo(BigDecimal.ZERO) == 0);
|
||||||
assertNull(transaction.getReference());
|
assertNull(transaction.getReference());
|
||||||
assertTrue(transaction.isSignatureValid());
|
assertTrue(transaction.isSignatureValid());
|
||||||
assertEquals(Transaction.ValidationResult.OK, transaction.isValid(connection));
|
assertEquals(Transaction.ValidationResult.OK, transaction.isValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to load first transaction directly from database
|
// Attempt to load first transaction directly from database
|
||||||
@ -45,13 +42,11 @@ public class blocks extends common {
|
|||||||
assertTrue(transaction.getFee().compareTo(BigDecimal.ZERO) == 0);
|
assertTrue(transaction.getFee().compareTo(BigDecimal.ZERO) == 0);
|
||||||
assertNull(transaction.getReference());
|
assertNull(transaction.getReference());
|
||||||
assertTrue(transaction.isSignatureValid());
|
assertTrue(transaction.isSignatureValid());
|
||||||
assertEquals(Transaction.ValidationResult.OK, transaction.isValid(connection));
|
assertEquals(Transaction.ValidationResult.OK, transaction.isValid());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBlockPaymentTransactions() throws SQLException {
|
public void testBlockPaymentTransactions() throws SQLException {
|
||||||
try (final Connection connection = DB.getConnection()) {
|
|
||||||
// Block 949 has lots of varied transactions
|
// Block 949 has lots of varied transactions
|
||||||
// Blocks 390 & 754 have only payment transactions
|
// Blocks 390 & 754 have only payment transactions
|
||||||
Block block = Block.fromHeight(754);
|
Block block = Block.fromHeight(754);
|
||||||
@ -77,11 +72,9 @@ public class blocks extends common {
|
|||||||
assertNotNull(transaction.getReference());
|
assertNotNull(transaction.getReference());
|
||||||
assertTrue(transaction.isSignatureValid());
|
assertTrue(transaction.isSignatureValid());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBlockSerialization() throws SQLException {
|
public void testBlockSerialization() throws SQLException {
|
||||||
try (final Connection connection = DB.getConnection()) {
|
|
||||||
// Block 949 has lots of varied transactions
|
// Block 949 has lots of varied transactions
|
||||||
// Blocks 390 & 754 have only payment transactions
|
// Blocks 390 & 754 have only payment transactions
|
||||||
Block block = Block.fromHeight(754);
|
Block block = Block.fromHeight(754);
|
||||||
@ -92,6 +85,5 @@ public class blocks extends common {
|
|||||||
|
|
||||||
assertEquals(block.getDataLength(), bytes.length);
|
assertEquals(block.getDataLength(), bytes.length);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ public class common {
|
|||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void closeDatabase() throws SQLException {
|
public static void closeDatabase() throws SQLException {
|
||||||
DB.close();
|
DB.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,37 +13,29 @@ public class connections extends common {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConnection() {
|
public void testConnection() {
|
||||||
try {
|
Connection connection = DB.getConnection();
|
||||||
Connection c = DB.getConnection();
|
assertNotNull(connection);
|
||||||
c.close();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimultaneousConnections() {
|
public void testSimultaneousConnections() {
|
||||||
|
// First connection is the thread-local one
|
||||||
|
Connection connection = DB.getConnection();
|
||||||
|
assertNotNull(connection);
|
||||||
|
|
||||||
int n_connections = 5;
|
int n_connections = 5;
|
||||||
Connection[] connections = new Connection[n_connections];
|
Connection[] connections = new Connection[n_connections];
|
||||||
|
|
||||||
try {
|
for (int i = 0; i < n_connections; ++i) {
|
||||||
for (int i = 0; i < n_connections; ++i)
|
|
||||||
connections[i] = DB.getConnection();
|
connections[i] = DB.getConnection();
|
||||||
|
assertEquals(connection, connections[i]);
|
||||||
// Close in same order as opening
|
|
||||||
for (int i = 0; i < n_connections; ++i)
|
|
||||||
connections[i].close();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConnectionAfterShutdown() {
|
public void testConnectionAfterShutdown() {
|
||||||
try {
|
try {
|
||||||
DB.close();
|
DB.shutdown();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
fail();
|
fail();
|
||||||
@ -51,8 +43,8 @@ public class connections extends common {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
DB.open();
|
DB.open();
|
||||||
Connection c = DB.getConnection();
|
Connection connection = DB.getConnection();
|
||||||
c.close();
|
assertNotNull(connection);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
fail();
|
fail();
|
||||||
|
@ -164,7 +164,7 @@ public class migrate extends common {
|
|||||||
|
|
||||||
JSONArray transactions = (JSONArray) json.get("transactions");
|
JSONArray transactions = (JSONArray) json.get("transactions");
|
||||||
|
|
||||||
DB.startTransaction(c);
|
DB.startTransaction();
|
||||||
|
|
||||||
// Blocks:
|
// Blocks:
|
||||||
// signature, version, reference, transaction_count, total_fees, transactions_signature, height, generation, generating_balance, generator,
|
// 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.execute();
|
||||||
blockTxPStmt.clearParameters();
|
blockTxPStmt.clearParameters();
|
||||||
|
|
||||||
DB.commit(c);
|
DB.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// new milestone block every 500 blocks?
|
// new milestone block every 500 blocks?
|
||||||
@ -600,7 +600,6 @@ public class migrate extends common {
|
|||||||
++height;
|
++height;
|
||||||
}
|
}
|
||||||
|
|
||||||
c.close();
|
|
||||||
System.out.println("Migration finished with new blockchain height " + BlockChain.getHeight());
|
System.out.println("Migration finished with new blockchain height " + BlockChain.getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import database.DB;
|
|
||||||
import qora.account.PublicKeyAccount;
|
import qora.account.PublicKeyAccount;
|
||||||
import qora.transaction.PaymentTransaction;
|
import qora.transaction.PaymentTransaction;
|
||||||
import utils.Base58;
|
import utils.Base58;
|
||||||
@ -25,9 +23,7 @@ public class save extends common {
|
|||||||
PaymentTransaction paymentTransaction = new PaymentTransaction(sender, "Qrecipient", BigDecimal.valueOf(12345L), BigDecimal.ONE,
|
PaymentTransaction paymentTransaction = new PaymentTransaction(sender, "Qrecipient", BigDecimal.valueOf(12345L), BigDecimal.ONE,
|
||||||
Instant.now().getEpochSecond(), reference, signature);
|
Instant.now().getEpochSecond(), reference, signature);
|
||||||
|
|
||||||
try (final Connection connection = DB.getConnection()) {
|
paymentTransaction.save();
|
||||||
paymentTransaction.save(connection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,12 @@ package test;
|
|||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import database.DB;
|
|
||||||
import qora.block.Block;
|
import qora.block.Block;
|
||||||
import qora.block.GenesisBlock;
|
import qora.block.GenesisBlock;
|
||||||
import qora.transaction.GenesisTransaction;
|
import qora.transaction.GenesisTransaction;
|
||||||
@ -50,7 +48,6 @@ public class transactions extends common {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPaymentSerialization() throws SQLException, ParseException {
|
public void testPaymentSerialization() throws SQLException, ParseException {
|
||||||
try (final Connection connection = DB.getConnection()) {
|
|
||||||
// Block 949 has lots of varied transactions
|
// Block 949 has lots of varied transactions
|
||||||
// Blocks 390 & 754 have only payment transactions
|
// Blocks 390 & 754 have only payment transactions
|
||||||
Block block = Block.fromHeight(754);
|
Block block = Block.fromHeight(754);
|
||||||
@ -63,7 +60,6 @@ public class transactions extends common {
|
|||||||
for (Transaction transaction : transactions)
|
for (Transaction transaction : transactions)
|
||||||
testGenericSerialization(transaction);
|
testGenericSerialization(transaction);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMessageSerialization() throws SQLException, ParseException {
|
public void testMessageSerialization() throws SQLException, ParseException {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user